[PATCH xserver] XMir DDX

Peter Hutterer peter.hutterer at who-t.net
Thu May 5 05:40:16 UTC 2016


On Fri, Apr 29, 2016 at 11:11:20AM +0200, Robert Ancell wrote:
> Contributions from:
>   Andreas Pokorny <andreas.pokorny at canonical.com>
>   Chris Townsend <christopher.townsend at canonical.com>
>   Christopher James Halse Rogers <christopher.halse.rogers at canonical.com>
>   Daniel van Vugt <vanvugt at gmail.com>
>   Maarten Lankhorst <maarten.lankhorst at ubuntu.com>
>   Robert Ancell <robert.ancell at canonical.com>
> 
> Signed-off-by: Robert Ancell <robert.ancell at canonical.com>
> ---
>  configure.ac                |   24 +
>  hw/Makefile.am              |    9 +-
>  hw/xmir/.gitignore          |    1 +
>  hw/xmir/Makefile.am         |   27 +
>  hw/xmir/xmir-cursor.c       |  210 +++++++
>  hw/xmir/xmir-cvt.c          |  304 ++++++++++
>  hw/xmir/xmir-input.c        |  592 ++++++++++++++++++
>  hw/xmir/xmir-output.c       |  424 +++++++++++++
>  hw/xmir/xmir-thread-proxy.c |  109 ++++
>  hw/xmir/xmir.c              | 1416 +++++++++++++++++++++++++++++++++++++++++++
>  hw/xmir/xmir.h              |  165 +++++
>  11 files changed, 3279 insertions(+), 2 deletions(-)
>  create mode 100644 hw/xmir/.gitignore
>  create mode 100644 hw/xmir/Makefile.am
>  create mode 100644 hw/xmir/xmir-cursor.c
>  create mode 100644 hw/xmir/xmir-cvt.c
>  create mode 100644 hw/xmir/xmir-input.c
>  create mode 100644 hw/xmir/xmir-output.c
>  create mode 100644 hw/xmir/xmir-thread-proxy.c
>  create mode 100644 hw/xmir/xmir.c
>  create mode 100644 hw/xmir/xmir.h

[...]

> diff --git a/hw/xmir/xmir-input.c b/hw/xmir/xmir-input.c
> new file mode 100644
> index 0000000..758c7ae
> --- /dev/null
> +++ b/hw/xmir/xmir-input.c
> @@ -0,0 +1,592 @@
> +/*
> + * Copyright © 2015-2016 Canonical Ltd
> + *
> + * Permission to use, copy, modify, distribute, and sell this software
> + * and its documentation for any purpose is hereby granted without
> + * fee, provided that the above copyright notice appear in all copies
> + * and that both that copyright notice and this permission notice
> + * appear in supporting documentation, and that the name of the
> + * copyright holders not be used in advertising or publicity
> + * pertaining to distribution of the software without specific,
> + * written prior permission.  The copyright holders make no
> + * representations about the suitability of this software for any
> + * purpose.  It is provided "as is" without express or implied
> + * warranty.
> + *
> + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
> + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
> + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
> + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
> + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
> + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
> + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
> + * SOFTWARE.
> + */
> +
> +#include "xmir.h"
> +
> +#include <linux/input.h>
> +
> +#include <sys/mman.h>
> +#include <xkbsrv.h>
> +#include <xserver-properties.h>
> +#include <inpututils.h>
> +
> +static void
> +xmir_pointer_control(DeviceIntPtr device, PtrCtrl *ctrl)
> +{
> +    /* Nothing to do, dix handles all settings */
> +}
> +
> +static int
> +xmir_pointer_proc(DeviceIntPtr device, int what)
> +{
> +#define NBUTTONS 10
> +#define NAXES 2
> +    BYTE map[NBUTTONS + 1];
> +    int i = 0;
> +    Atom btn_labels[NBUTTONS] = { 0 };
> +    Atom axes_labels[NAXES] = { 0 };
> +
> +    switch (what) {
> +    case DEVICE_INIT:
> +        device->public.on = FALSE;
> +
> +        for (i = 1; i <= NBUTTONS; i++)
> +            map[i] = i;
> +
> +        btn_labels[0] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_LEFT);
> +        btn_labels[1] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_MIDDLE);
> +        btn_labels[2] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_RIGHT);
> +        btn_labels[3] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_WHEEL_UP);
> +        btn_labels[4] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_WHEEL_DOWN);
> +        btn_labels[5] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_HWHEEL_LEFT);
> +        btn_labels[6] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_HWHEEL_RIGHT);
> +        /* Don't know about the rest */
> +
> +        axes_labels[0] = XIGetKnownProperty(AXIS_LABEL_PROP_ABS_X);
> +        axes_labels[1] = XIGetKnownProperty(AXIS_LABEL_PROP_ABS_Y);
> +
> +        if (!InitValuatorClassDeviceStruct(device, 2, btn_labels,
> +                                           GetMotionHistorySize(), Absolute))
> +            return BadValue;
> +
> +        /* Valuators */
> +        InitValuatorAxisStruct(device, 0, axes_labels[0],
> +                               0, 0xFFFF, 10000, 0, 10000, Absolute);
> +        InitValuatorAxisStruct(device, 1, axes_labels[1],
> +                               0, 0xFFFF, 10000, 0, 10000, Absolute);

no smooth scrolling?

> +
> +        if (!InitPtrFeedbackClassDeviceStruct(device, xmir_pointer_control))
> +            return BadValue;
> +
> +        if (!InitButtonClassDeviceStruct(device, 3, btn_labels, map))
> +            return BadValue;
> +
> +        return Success;
> +
> +    case DEVICE_ON:
> +        device->public.on = TRUE;
> +        return Success;
> +
> +    case DEVICE_OFF:
> +    case DEVICE_CLOSE:
> +        device->public.on = FALSE;
> +        return Success;
> +    }
> +
> +    return BadMatch;
> +
> +#undef NBUTTONS
> +#undef NAXES
> +}
> +
> +static void
> +xmir_keyboard_control(DeviceIntPtr device, KeybdCtrl *ctrl)
> +{
> +}
> +
> +static int
> +xmir_keyboard_proc(DeviceIntPtr device, int what)
> +{
> +    switch (what) {
> +    case DEVICE_INIT:
> +        device->public.on = FALSE;
> +        if (!InitKeyboardDeviceStructFromString(device,
> +                                                NULL /*xmir_input->keymap*/, 0,
> +                                                NULL, xmir_keyboard_control))
> +            return BadValue;
> +
> +        return Success;
> +    case DEVICE_ON:
> +        device->public.on = TRUE;
> +        return Success;
> +
> +    case DEVICE_OFF:
> +    case DEVICE_CLOSE:
> +        device->public.on = FALSE;
> +        return Success;
> +    }
> +
> +    return BadMatch;
> +}
> +
> +static void
> +pointer_convert_xy(struct xmir_input *xmir_input,
> +                   struct xmir_window *xmir_window,
> +                   int *x, int *y)
> +{
> +    bool reflect_x = false;
> +    bool reflect_y = false;
> +    bool swap_xy = false;
> +    int dx = xmir_window->window->drawable.x;
> +    int dy = xmir_window->window->drawable.y;

nitpick: dx/dy is usually used for deltas

> +    int sx = *x, sy = *y;
> +    int w = xmir_window->window->drawable.width;
> +    int h = xmir_window->window->drawable.height;
> +
> +    /* reflection test parameters */
> +    bool magic_x_invert = false, magic_y_invert = false;

uhm, let's not leave these in the final patch?

> +
> +    DebugF("Raw input %i,%i in window (%i,%i)->(%i,%i) orientation %i\n", *x, *y, dx, dy, dx + w, dy + h, xmir_window->orientation);

please split lines like this up

> +
> +    if (magic_x_invert)
> +        reflect_x = !reflect_x;
> +
> +    if (magic_y_invert)
> +        reflect_y = !reflect_y;
> +
> +    switch (xmir_window->orientation) {
> +    case 90:
> +        reflect_x = !reflect_x; swap_xy = true; break;
> +    case 180:
> +        reflect_x = !reflect_x; reflect_y = !reflect_y; break;
> +    case 270:
> +        reflect_y = !reflect_y; swap_xy = true; break;

multiple lines please
> +    }
> +
> +    if (!swap_xy) {
> +        sx = *x;
> +        sy = *y;
> +    } else {

as much as I dislike it myself, server style is }\n else

> +        sx = *y;
> +        sy = *x;
> +    }
> +
> +    if (!reflect_x)
> +        *x = sx + dx;
> +    else
> +        *x = w + dx - sx;
> +
> +    if (!reflect_y)
> +        *y = sy + dy;
> +    else
> +        *y = h + dy - sy;
> +
> +    DebugF("Converted to %i, %i\n", *x, *y);
> +}
> +
> +static Bool
> +pointer_ensure_focus(struct xmir_input *xmir_input,
> +                     struct xmir_window *xmir_window,
> +                     DeviceIntPtr dev, int sx, int sy)
> +{
> +    ScreenPtr screen = xmir_window->window->drawable.pScreen;
> +
> +    if (xmir_input->focus_window == xmir_window)
> +        return FALSE;
> +
> +    if (xmir_input->focus_window) {
> +        xmir_input->focus_window = NULL;
> +        CheckMotion(NULL, GetMaster(dev, MASTER_POINTER));
> +    }
> +
> +    xmir_input->focus_window = xmir_window;
> +
> +    pointer_convert_xy(xmir_input, xmir_window, &sx, &sy);
> +
> +    (screen->SetCursorPosition) (dev, screen, sx, sy, TRUE);
> +    CheckMotion(NULL, GetMaster(dev, MASTER_POINTER));
> +
> +    return TRUE;
> +}
> +
> +static void
> +pointer_handle_motion(struct xmir_input *xmir_input,
> +                      struct xmir_window *xmir_window,
> +                      MirPointerEvent const *pev)
> +{
> +    int sx = mir_pointer_event_axis_value(pev, mir_pointer_axis_x);
> +    int sy = mir_pointer_event_axis_value(pev, mir_pointer_axis_y);
> +    int vscroll = 0;
> +    ValuatorMask mask;
> +
> +    pointer_ensure_focus(xmir_input, xmir_window, xmir_input->pointer, sx, sy);
> +
> +    pointer_convert_xy(xmir_input, xmir_window, &sx, &sy);
> +
> +    valuator_mask_zero(&mask);
> +    valuator_mask_set(&mask, 0, sx);
> +    valuator_mask_set(&mask, 1, sy);
> +
> +    QueuePointerEvents(xmir_input->pointer, MotionNotify, 0,
> +                       POINTER_ABSOLUTE | POINTER_SCREEN, &mask);
> +
> +    /* Mouse wheel: Moving the wheel is a press+release of button 4/5 */
> +    vscroll = mir_pointer_event_axis_value(pev, mir_pointer_axis_vscroll);

use smooth scrolling, not button events if you have the native smooth
scrolling values anyway.

> +    if (vscroll) {
> +        int button = vscroll < 0 ? 5 : 4;
> +        valuator_mask_zero(&mask);
> +        QueuePointerEvents(xmir_input->pointer, ButtonPress, button, 0, &mask);
> +        QueuePointerEvents(xmir_input->pointer, ButtonRelease, button, 0, &mask);
> +    }
> +}
> +
> +static void
> +pointer_handle_button(struct xmir_input *xmir_input,
> +                      struct xmir_window *xmir_window,
> +                      MirPointerEvent const *pev)

convention in the server is const foo *bar, not foo const *bar

> +{
> +    DeviceIntPtr dev = xmir_input->pointer;
> +    struct {MirPointerButton mir_button; int x_button;} map[3] =

multiple lines please.

> +    {
> +        {mir_pointer_button_primary, 1},   /* Usually left button */
> +        {mir_pointer_button_secondary, 3}, /* Middle button */
> +        {mir_pointer_button_tertiary, 2},  /* Right button */
> +    };
> +    int i;
> +    ValuatorMask mask;

empty line here.

> +    valuator_mask_zero(&mask);
> +
> +    for (i = 0; i < 3; ++i) {
> +        MirPointerButton mir_button = map[i].mir_button;
> +        int x_button = map[i].x_button;
> +        int oldstate = BitIsOn(dev->button->down, x_button) ?
> +                       ButtonPress : ButtonRelease;
> +        int newstate = mir_pointer_event_button_state(pev, mir_button) ?
> +                       ButtonPress : ButtonRelease;
> +
> +        if (oldstate != newstate)
> +            QueuePointerEvents(dev, newstate, x_button, 0, &mask);
> +    }

doesn't your event contain the actual button event? why do you need to check
the device state and bitmask around it?

> +
> +    /* XXX: Map rest of input buttons too! */
> +}
> +
> +static DeviceIntPtr
> +add_device(struct xmir_input *xmir_input,
> +           const char *driver, DeviceProc device_proc)
> +{
> +    DeviceIntPtr dev = NULL;
> +    static Atom type_atom;
> +    char name[32];
> +
> +    dev = AddInputDevice(serverClient, device_proc, TRUE);
> +    if (dev == NULL)
> +        return NULL;
> +
> +    if (type_atom == None)
> +        type_atom = MakeAtom(driver, strlen(driver), TRUE);
> +    snprintf(name, sizeof name, "%s:%d", driver, xmir_input->id);
> +    AssignTypeAndName(dev, type_atom, name);
> +    dev->public.devicePrivate = xmir_input;
> +    dev->type = SLAVE;
> +    dev->spriteInfo->spriteOwner = FALSE;
> +
> +    return dev;
> +}
> +
> +static void
> +xmir_input_destroy(struct xmir_input *xmir_input)
> +{
> +    RemoveDevice(xmir_input->pointer, FALSE);
> +    RemoveDevice(xmir_input->keyboard, FALSE);
> +    free(xmir_input);
> +}
> +
> +Bool
> +LegalModifier(unsigned int key, DeviceIntPtr pDev)
> +{
> +    return TRUE;
> +}
> +
> +void
> +ProcessInputEvents(void)
> +{
> +    mieqProcessInputEvents();
> +}
> +
> +void
> +DDXRingBell(int volume, int pitch, int duration)
> +{
> +}
> +
> +static WindowPtr
> +xmir_xy_to_window(ScreenPtr screen, SpritePtr sprite, int x, int y)
> +{
> +    struct xmir_input *xmir_input = NULL;
> +    DeviceIntPtr device;
> +
> +    for (device = inputInfo.devices; device; device = device->next) {
> +        if (device->deviceProc == xmir_pointer_proc &&
> +            device->spriteInfo->sprite == sprite) {
> +            xmir_input = device->public.devicePrivate;
> +            break;
> +        }
> +    }
> +
> +    if (xmir_input == NULL) {
> +        /* XTEST device */
> +        sprite->spriteTraceGood = 1;
> +        return sprite->spriteTrace[0];
> +    }
> +
> +    if (xmir_input->focus_window) {
> +        sprite->spriteTraceGood = 2;
> +        sprite->spriteTrace[1] = xmir_input->focus_window->window;
> +        return miSpriteTrace(sprite, x, y);
> +    }
> +    else {
> +        sprite->spriteTraceGood = 1;
> +        return sprite->spriteTrace[0];
> +    }
> +}
> +
> +static void
> +fake_touch_move(struct xmir_input *xmir_input, struct xmir_window *xmir_window, int sx, int sy)
> +{
> +    ValuatorMask mask;
> +
> +    pointer_convert_xy(xmir_input, xmir_window, &sx, &sy);
> +
> +    valuator_mask_zero(&mask);
> +    valuator_mask_set(&mask, 0, sx);
> +    valuator_mask_set(&mask, 1, sy);
> +
> +    QueuePointerEvents(xmir_input->touch, MotionNotify, 0,
> +                       POINTER_ABSOLUTE | POINTER_SCREEN, &mask);
> +}
> +
> +static void
> +xmir_window_handle_input_event(struct xmir_input *xmir_input,
> +                               struct xmir_window *xmir_window,
> +                               MirInputEvent const* ev)
> +{
> +    switch (mir_input_event_get_type(ev)) {
> +    case mir_input_event_type_key: {
> +        MirKeyboardEvent const *kev;
> +        MirKeyboardAction action;
> +
> +        kev = mir_input_event_get_keyboard_event(ev);
> +        action = mir_keyboard_event_action(kev);
> +
> +        QueueKeyboardEvents(xmir_input->keyboard,
> +                            action == mir_keyboard_action_up ? KeyRelease : KeyPress,
> +                            mir_keyboard_event_scan_code(kev) + 8);
> +        break;
> +    }
> +    case mir_input_event_type_touch: {
> +        MirTouchEvent const *tev;
> +        int i = 0, count, sx, sy;
> +        ValuatorMask mask;
> +
> +        tev = mir_input_event_get_touch_event(ev);
> +        count = mir_touch_event_point_count(tev);
> +
> +        /* Do we really need this multifinger tracking at all?... */
> +        if (count < 1) {
> +            xmir_input->touch_id = -1;
> +            break;
> +        }
> +
> +        if (xmir_input->touch_id != -1) {
> +            for (i = 0; i < count; ++i)
> +                if (mir_touch_event_id(tev, i) == xmir_input->touch_id)
> +                    break;
> +        }
> +        if (i >= count) {
> +            for (i = 0; i < count; ++i)
> +                if (mir_touch_event_action(tev, i) == mir_touch_action_down)
> +                    break;
> +        }
> +
> +        if (i >= count)
> +            break;
> +
> +        sx = mir_touch_event_axis_value(tev, i, mir_touch_axis_x);
> +        sy = mir_touch_event_axis_value(tev, i, mir_touch_axis_y);
> +        valuator_mask_zero(&mask);
> +
> +        switch (mir_touch_event_action(tev, i)) {
> +        case mir_touch_action_up:
> +            fake_touch_move(xmir_input, xmir_window, sx, sy);
> +            QueuePointerEvents(xmir_input->touch, ButtonRelease, 1, 0, &mask);
> +            xmir_input->touch_id = -1;
> +            break;
> +        case mir_touch_action_down:
> +            xmir_input->touch_id = mir_touch_event_id(tev, i);
> +            if (!pointer_ensure_focus(xmir_input, xmir_window, xmir_input->touch, sx, sy))
> +                fake_touch_move(xmir_input, xmir_window, sx, sy);
> +            QueuePointerEvents(xmir_input->touch, ButtonPress, 1, 0, &mask);
> +            break;
> +        case mir_touch_action_change:
> +            fake_touch_move(xmir_input, xmir_window, sx, sy);
> +            break;

you're mapping touch points to pointer movements? why don't use pass them on
as touch events?

these comments are just the couple that jumped out skimming the patch, but
some more clarification about why certain decisions were made would be
useful.

Cheers,
   Peter


More information about the xorg-devel mailing list