diff options
Diffstat (limited to 'xserver/hw/xwayland/xwayland-input.c')
-rw-r--r-- | xserver/hw/xwayland/xwayland-input.c | 1305 |
1 files changed, 1286 insertions, 19 deletions
diff --git a/xserver/hw/xwayland/xwayland-input.c b/xserver/hw/xwayland/xwayland-input.c index d96e6f2a4..fbbcb39cc 100644 --- a/xserver/hw/xwayland/xwayland-input.c +++ b/xserver/hw/xwayland/xwayland-input.c @@ -34,12 +34,14 @@ #include <inpututils.h> #include <mipointer.h> #include <mipointrst.h> +#include <misc.h> +#include "tablet-unstable-v2-client-protocol.h" -/* Copied from mipointer.c */ -#define MIPOINTER(dev) \ - (IsFloating(dev) ? \ - (miPointerPtr)dixLookupPrivate(&(dev)->devPrivates, miPointerPrivKey): \ - (miPointerPtr)dixLookupPrivate(&(GetMaster(dev, MASTER_POINTER))->devPrivates, miPointerPrivKey)) +struct axis_discrete_pending { + struct xorg_list l; + uint32_t axis; + int32_t discrete; +}; struct sync_pending { struct xorg_list l; @@ -62,6 +64,12 @@ static void xwl_seat_destroy_confined_pointer(struct xwl_seat *xwl_seat); static void +init_tablet_manager_seat(struct xwl_screen *xwl_screen, + struct xwl_seat *xwl_seat); +static void +release_tablet_manager_seat(struct xwl_seat *xwl_seat); + +static void xwl_pointer_control(DeviceIntPtr device, PtrCtrl *ctrl) { /* Nothing to do, dix handles all settings */ @@ -287,6 +295,75 @@ xwl_touch_proc(DeviceIntPtr device, int what) #undef NTOUCHPOINTS } +static int +xwl_tablet_proc(DeviceIntPtr device, int what) +{ +#define NBUTTONS 9 +#define NAXES 6 + Atom btn_labels[NBUTTONS] = { 0 }; + Atom axes_labels[NAXES] = { 0 }; + BYTE map[NBUTTONS + 1] = { 0 }; + int i; + + switch (what) { + case DEVICE_INIT: + device->public.on = FALSE; + + for (i = 1; i <= NBUTTONS; i++) + map[i] = i; + + axes_labels[0] = XIGetKnownProperty(AXIS_LABEL_PROP_ABS_X); + axes_labels[1] = XIGetKnownProperty(AXIS_LABEL_PROP_ABS_Y); + axes_labels[2] = XIGetKnownProperty(AXIS_LABEL_PROP_ABS_PRESSURE); + axes_labels[3] = XIGetKnownProperty(AXIS_LABEL_PROP_ABS_TILT_X); + axes_labels[4] = XIGetKnownProperty(AXIS_LABEL_PROP_ABS_TILT_Y); + axes_labels[5] = XIGetKnownProperty(AXIS_LABEL_PROP_ABS_WHEEL); + + if (!InitValuatorClassDeviceStruct(device, NAXES, axes_labels, + GetMotionHistorySize(), Absolute)) + return BadValue; + + /* Valuators - match the xf86-input-wacom ranges */ + InitValuatorAxisStruct(device, 0, axes_labels[0], + 0, 262143, 10000, 0, 10000, Absolute); + InitValuatorAxisStruct(device, 1, axes_labels[1], + 0, 262143, 10000, 0, 10000, Absolute); + /* pressure */ + InitValuatorAxisStruct(device, 2, axes_labels[2], + 0, 65535, 1, 0, 1, Absolute); + /* tilt x */ + InitValuatorAxisStruct(device, 3, axes_labels[3], + -64, 63, 57, 0, 57, Absolute); + /* tilt y */ + InitValuatorAxisStruct(device, 4, axes_labels[4], + -64, 63, 57, 0, 57, Absolute); + /* abs wheel (airbrush) or rotation (artpen) */ + InitValuatorAxisStruct(device, 5, axes_labels[5], + -900, 899, 1, 0, 1, Absolute); + + if (!InitPtrFeedbackClassDeviceStruct(device, xwl_pointer_control)) + return BadValue; + + if (!InitButtonClassDeviceStruct(device, NBUTTONS, 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 NAXES +#undef NBUTTONS +} + static void pointer_handle_enter(void *data, struct wl_pointer *pointer, uint32_t serial, struct wl_surface *surface, @@ -347,9 +424,9 @@ pointer_handle_enter(void *data, struct wl_pointer *pointer, * of our surfaces might not have been shown. In that case we'll * have a cursor surface frame callback pending which we need to * clear so that we can continue submitting new cursor frames. */ - if (xwl_seat->cursor_frame_cb) { - wl_callback_destroy(xwl_seat->cursor_frame_cb); - xwl_seat->cursor_frame_cb = NULL; + if (xwl_seat->cursor.frame_cb) { + wl_callback_destroy(xwl_seat->cursor.frame_cb); + xwl_seat->cursor.frame_cb = NULL; xwl_seat_set_cursor(xwl_seat); } @@ -494,6 +571,8 @@ pointer_handle_axis(void *data, struct wl_pointer *pointer, int index; const int divisor = 10; ValuatorMask mask; + struct axis_discrete_pending *pending = NULL; + struct axis_discrete_pending *iter; switch (axis) { case WL_POINTER_AXIS_VERTICAL_SCROLL: @@ -506,8 +585,22 @@ pointer_handle_axis(void *data, struct wl_pointer *pointer, return; } + xorg_list_for_each_entry(iter, &xwl_seat->axis_discrete_pending, l) { + if (iter->axis == axis) { + pending = iter; + break; + } + } + valuator_mask_zero(&mask); - valuator_mask_set_double(&mask, index, wl_fixed_to_double(value) / divisor); + + if (pending) { + valuator_mask_set(&mask, index, pending->discrete); + xorg_list_del(&pending->l); + free(pending); + } else { + valuator_mask_set_double(&mask, index, wl_fixed_to_double(value) / divisor); + } QueuePointerEvents(xwl_seat->pointer, MotionNotify, 0, POINTER_RELATIVE, &mask); } @@ -537,6 +630,16 @@ static void pointer_handle_axis_discrete(void *data, struct wl_pointer *wl_pointer, uint32_t axis, int32_t discrete) { + struct xwl_seat *xwl_seat = data; + + struct axis_discrete_pending *pending = malloc(sizeof *pending); + if (!pending) + return; + + pending->axis = axis; + pending->discrete = discrete; + + xorg_list_add(&pending->l, &xwl_seat->axis_discrete_pending); } static const struct wl_pointer_listener pointer_listener = { @@ -864,9 +967,9 @@ touch_handle_down(void *data, struct wl_touch *wl_touch, if (surface == NULL) return; - xwl_touch = calloc(sizeof *xwl_touch, 1); + xwl_touch = calloc(1, sizeof *xwl_touch); if (xwl_touch == NULL) { - ErrorF("touch_handle_down ENOMEM"); + ErrorF("%s: ENOMEM\n", __func__); return; } @@ -945,6 +1048,84 @@ static const struct wl_touch_listener touch_listener = { touch_handle_cancel }; +static struct xwl_seat * +find_matching_seat(DeviceIntPtr device) +{ + DeviceIntPtr dev; + + for (dev = inputInfo.devices; dev; dev = dev->next) + if (dev->deviceProc == xwl_keyboard_proc && + device == GetMaster(dev, MASTER_KEYBOARD)) + return (struct xwl_seat *) dev->public.devicePrivate; + + return NULL; +} + +static void +release_grab(struct xwl_seat *xwl_seat) +{ + if (xwl_seat->keyboard_grab) + zwp_xwayland_keyboard_grab_v1_destroy(xwl_seat->keyboard_grab); + xwl_seat->keyboard_grab = NULL; +} + +static void +set_grab(struct xwl_seat *xwl_seat, struct xwl_window *xwl_window) +{ + struct xwl_screen *xwl_screen; + + if (!xwl_window) + return; + + /* We already have a grab */ + if (xwl_seat->keyboard_grab) + release_grab (xwl_seat); + + xwl_screen = xwl_seat->xwl_screen; + xwl_seat->keyboard_grab = + zwp_xwayland_keyboard_grab_manager_v1_grab_keyboard(xwl_screen->wp_grab, + xwl_window->surface, + xwl_seat->seat); +} + +static void +xwl_keyboard_activate_grab(DeviceIntPtr device, GrabPtr grab, TimeStamp time, Bool passive) +{ + struct xwl_seat *xwl_seat = device->public.devicePrivate; + + /* We are not interested in passive grabs */ + if (!passive) { + /* If the device is the MASTER_KEYBOARD, we don't have an xwl_seat */ + if (xwl_seat == NULL) + xwl_seat = find_matching_seat(device); + if (xwl_seat) + set_grab(xwl_seat, xwl_window_from_window(grab->window)); + } + + ActivateKeyboardGrab(device, grab, time, passive); +} + +static void +xwl_keyboard_deactivate_grab(DeviceIntPtr device) +{ + struct xwl_seat *xwl_seat = device->public.devicePrivate; + + /* If the device is the MASTER_KEYBOARD, we don't have an xwl_seat */ + if (xwl_seat == NULL) + xwl_seat = find_matching_seat(device); + if (xwl_seat) + release_grab (xwl_seat); + + DeactivateKeyboardGrab(device); +} + +static void +setup_keyboard_grab_handler (DeviceIntPtr device) +{ + device->deviceGrab.ActivateGrab = xwl_keyboard_activate_grab; + device->deviceGrab.DeactivateGrab = xwl_keyboard_deactivate_grab; +} + static DeviceIntPtr add_device(struct xwl_seat *xwl_seat, const char *driver, DeviceProc device_proc) @@ -1033,6 +1214,8 @@ release_relative_pointer(struct xwl_seat *xwl_seat) static void init_keyboard(struct xwl_seat *xwl_seat) { + DeviceIntPtr master; + xwl_seat->wl_keyboard = wl_seat_get_keyboard(xwl_seat->seat); wl_keyboard_add_listener(xwl_seat->wl_keyboard, &keyboard_listener, xwl_seat); @@ -1044,11 +1227,19 @@ init_keyboard(struct xwl_seat *xwl_seat) } EnableDevice(xwl_seat->keyboard, TRUE); xwl_seat->keyboard->key->xkbInfo->checkRepeat = keyboard_check_repeat; + + if (xwl_seat->xwl_screen->wp_grab) { + /* We have Xwayland grab protocol supported by the compositor */ + master = GetMaster(xwl_seat->keyboard, MASTER_KEYBOARD); + if (master) + setup_keyboard_grab_handler(master); + } } static void release_keyboard(struct xwl_seat *xwl_seat) { + release_grab(xwl_seat); wl_keyboard_release(xwl_seat->wl_keyboard); xwl_seat->wl_keyboard = NULL; @@ -1126,13 +1317,38 @@ static const struct wl_seat_listener seat_listener = { }; static void +xwl_cursor_init(struct xwl_cursor *xwl_cursor, struct xwl_screen *xwl_screen, + void (* update_proc)(struct xwl_cursor *)) +{ + xwl_cursor->surface = wl_compositor_create_surface(xwl_screen->compositor); + xwl_cursor->update_proc = update_proc; + xwl_cursor->frame_cb = NULL; + xwl_cursor->needs_update = FALSE; +} + +static void +xwl_cursor_release(struct xwl_cursor *xwl_cursor) +{ + wl_surface_destroy(xwl_cursor->surface); + if (xwl_cursor->frame_cb) + wl_callback_destroy(xwl_cursor->frame_cb); +} + +static void +xwl_seat_update_cursor(struct xwl_cursor *xwl_cursor) +{ + struct xwl_seat *xwl_seat = wl_container_of(xwl_cursor, xwl_seat, cursor); + xwl_seat_set_cursor(xwl_seat); +} + +static void create_input_device(struct xwl_screen *xwl_screen, uint32_t id, uint32_t version) { struct xwl_seat *xwl_seat; - xwl_seat = calloc(sizeof *xwl_seat, 1); + xwl_seat = calloc(1, sizeof *xwl_seat); if (xwl_seat == NULL) { - ErrorF("create_input ENOMEM\n"); + ErrorF("%s: ENOMEM\n", __func__); return; } @@ -1144,11 +1360,16 @@ create_input_device(struct xwl_screen *xwl_screen, uint32_t id, uint32_t version &wl_seat_interface, min(version, 5)); xwl_seat->id = id; - xwl_seat->cursor = wl_compositor_create_surface(xwl_screen->compositor); + xwl_cursor_init(&xwl_seat->cursor, xwl_seat->xwl_screen, + xwl_seat_update_cursor); wl_seat_add_listener(xwl_seat->seat, &seat_listener, xwl_seat); + + init_tablet_manager_seat(xwl_screen, xwl_seat); + wl_array_init(&xwl_seat->keys); xorg_list_init(&xwl_seat->touches); + xorg_list_init(&xwl_seat->axis_discrete_pending); xorg_list_init(&xwl_seat->sync_pending); } @@ -1157,6 +1378,7 @@ xwl_seat_destroy(struct xwl_seat *xwl_seat) { struct xwl_touch *xwl_touch, *next_xwl_touch; struct sync_pending *p, *npd; + struct axis_discrete_pending *ad, *ad_next; xorg_list_for_each_entry_safe(xwl_touch, next_xwl_touch, &xwl_seat->touches, link_touch) { @@ -1169,15 +1391,1035 @@ xwl_seat_destroy(struct xwl_seat *xwl_seat) free (p); } + xorg_list_for_each_entry_safe(ad, ad_next, &xwl_seat->axis_discrete_pending, l) { + xorg_list_del(&ad->l); + free(ad); + } + + release_tablet_manager_seat(xwl_seat); + + release_grab(xwl_seat); wl_seat_destroy(xwl_seat->seat); - wl_surface_destroy(xwl_seat->cursor); - if (xwl_seat->cursor_frame_cb) - wl_callback_destroy(xwl_seat->cursor_frame_cb); + xwl_cursor_release(&xwl_seat->cursor); wl_array_release(&xwl_seat->keys); free(xwl_seat); } static void +tablet_handle_name(void *data, struct zwp_tablet_v2 *tablet, const char *name) +{ +} + +static void +tablet_handle_id(void *data, struct zwp_tablet_v2 *tablet, uint32_t vid, + uint32_t pid) +{ +} + +static void +tablet_handle_path(void *data, struct zwp_tablet_v2 *tablet, const char *path) +{ +} + +static void +tablet_handle_done(void *data, struct zwp_tablet_v2 *tablet) +{ + struct xwl_tablet *xwl_tablet = data; + struct xwl_seat *xwl_seat = xwl_tablet->seat; + + if (xwl_seat->stylus == NULL) { + xwl_seat->stylus = add_device(xwl_seat, "xwayland-stylus", xwl_tablet_proc); + ActivateDevice(xwl_seat->stylus, TRUE); + } + EnableDevice(xwl_seat->stylus, TRUE); + + if (xwl_seat->eraser == NULL) { + xwl_seat->eraser = add_device(xwl_seat, "xwayland-eraser", xwl_tablet_proc); + ActivateDevice(xwl_seat->eraser, TRUE); + } + EnableDevice(xwl_seat->eraser, TRUE); + + if (xwl_seat->puck == NULL) { + xwl_seat->puck = add_device(xwl_seat, "xwayland-cursor", xwl_tablet_proc); + ActivateDevice(xwl_seat->puck, TRUE); + } + EnableDevice(xwl_seat->puck, TRUE); +} + +static void +tablet_handle_removed(void *data, struct zwp_tablet_v2 *tablet) +{ + struct xwl_tablet *xwl_tablet = data; + struct xwl_seat *xwl_seat = xwl_tablet->seat; + + xorg_list_del(&xwl_tablet->link); + + /* The tablet is merely disabled, not removed. The next tablet + will re-use the same X devices */ + if (xorg_list_is_empty(&xwl_seat->tablets)) { + if (xwl_seat->stylus) + DisableDevice(xwl_seat->stylus, TRUE); + if (xwl_seat->eraser) + DisableDevice(xwl_seat->eraser, TRUE); + if (xwl_seat->puck) + DisableDevice(xwl_seat->puck, TRUE); + /* pads are removed separately */ + } + + zwp_tablet_v2_destroy(tablet); + free(xwl_tablet); +} + +static const struct zwp_tablet_v2_listener tablet_listener = { + tablet_handle_name, + tablet_handle_id, + tablet_handle_path, + tablet_handle_done, + tablet_handle_removed +}; + +static void +tablet_tool_receive_type(void *data, struct zwp_tablet_tool_v2 *tool, + uint32_t type) +{ + struct xwl_tablet_tool *xwl_tablet_tool = data; + struct xwl_seat *xwl_seat = xwl_tablet_tool->seat; + + switch (type) { + case ZWP_TABLET_TOOL_V2_TYPE_ERASER: + xwl_tablet_tool->xdevice = xwl_seat->eraser; + break; + case ZWP_TABLET_TOOL_V2_TYPE_MOUSE: + case ZWP_TABLET_TOOL_V2_TYPE_LENS: + xwl_tablet_tool->xdevice = xwl_seat->puck; + break; + default: + xwl_tablet_tool->xdevice = xwl_seat->stylus; + break; + } +} + +static void +tablet_tool_receive_hardware_serial(void *data, struct zwp_tablet_tool_v2 *tool, + uint32_t hi, uint32_t low) +{ +} + +static void +tablet_tool_receive_hardware_id_wacom(void *data, struct zwp_tablet_tool_v2 *tool, + uint32_t hi, uint32_t low) +{ +} + +static void +tablet_tool_receive_capability(void *data, struct zwp_tablet_tool_v2 *tool, + uint32_t capability) +{ +} + +static void +tablet_tool_receive_done(void *data, struct zwp_tablet_tool_v2 *tool) +{ +} + +static void +tablet_tool_receive_removed(void *data, struct zwp_tablet_tool_v2 *tool) +{ + struct xwl_tablet_tool *xwl_tablet_tool = data; + + xorg_list_del(&xwl_tablet_tool->link); + xwl_cursor_release(&xwl_tablet_tool->cursor); + zwp_tablet_tool_v2_destroy(tool); + free(xwl_tablet_tool); +} + +static void +tablet_tool_proximity_in(void *data, struct zwp_tablet_tool_v2 *tool, + uint32_t serial, struct zwp_tablet_v2 *tablet, + struct wl_surface *wl_surface) +{ + struct xwl_tablet_tool *xwl_tablet_tool = data; + struct xwl_seat *xwl_seat = xwl_tablet_tool->seat; + + /* There's a race here where if we create and then immediately + * destroy a surface, we might end up in a state where the Wayland + * compositor sends us an event for a surface that doesn't exist. + * + * Don't process enter events in this case. + * + * see pointer_handle_enter() + */ + if (wl_surface == NULL) + return; + + xwl_tablet_tool->proximity_in_serial = serial; + xwl_seat->tablet_focus_window = wl_surface_get_user_data(wl_surface); + + xwl_tablet_tool_set_cursor(xwl_tablet_tool); +} + +static void +tablet_tool_proximity_out(void *data, struct zwp_tablet_tool_v2 *tool) +{ + struct xwl_tablet_tool *xwl_tablet_tool = data; + struct xwl_seat *xwl_seat = xwl_tablet_tool->seat; + + xwl_tablet_tool->proximity_in_serial = 0; + xwl_seat->tablet_focus_window = NULL; + + xwl_tablet_tool->pressure = 0; + xwl_tablet_tool->tilt_x = 0; + xwl_tablet_tool->tilt_y = 0; + xwl_tablet_tool->rotation = 0; + xwl_tablet_tool->slider = 0; +} + +static void +tablet_tool_down(void *data, struct zwp_tablet_tool_v2 *tool, uint32_t serial) +{ + struct xwl_tablet_tool *xwl_tablet_tool = data; + struct xwl_seat *xwl_seat = xwl_tablet_tool->seat; + ValuatorMask mask; + + xwl_seat->xwl_screen->serial = serial; + + valuator_mask_zero(&mask); + QueuePointerEvents(xwl_tablet_tool->xdevice, ButtonPress, 1, 0, &mask); +} + +static void +tablet_tool_up(void *data, struct zwp_tablet_tool_v2 *tool) +{ + struct xwl_tablet_tool *xwl_tablet_tool = data; + ValuatorMask mask; + + valuator_mask_zero(&mask); + QueuePointerEvents(xwl_tablet_tool->xdevice, ButtonRelease, 1, 0, &mask); +} + +static void +tablet_tool_motion(void *data, struct zwp_tablet_tool_v2 *tool, + wl_fixed_t x, wl_fixed_t y) +{ + struct xwl_tablet_tool *xwl_tablet_tool = data; + struct xwl_seat *xwl_seat = xwl_tablet_tool->seat; + int32_t dx, dy; + double sx = wl_fixed_to_double(x); + double sy = wl_fixed_to_double(y); + + if (!xwl_seat->tablet_focus_window) + return; + + dx = xwl_seat->tablet_focus_window->window->drawable.x; + dy = xwl_seat->tablet_focus_window->window->drawable.y; + + xwl_tablet_tool->x = (double) dx + sx; + xwl_tablet_tool->y = (double) dy + sy; +} + +static void +tablet_tool_pressure(void *data, struct zwp_tablet_tool_v2 *tool, + uint32_t pressure) +{ + struct xwl_tablet_tool *xwl_tablet_tool = data; + struct xwl_seat *xwl_seat = xwl_tablet_tool->seat; + + if (!xwl_seat->tablet_focus_window) + return; + + /* normalized to 65535 already */ + xwl_tablet_tool->pressure = pressure; +} + +static void +tablet_tool_distance(void *data, struct zwp_tablet_tool_v2 *tool, + uint32_t distance_raw) +{ +} + +static void +tablet_tool_tilt(void *data, struct zwp_tablet_tool_v2 *tool, + wl_fixed_t tilt_x, wl_fixed_t tilt_y) +{ + struct xwl_tablet_tool *xwl_tablet_tool = data; + struct xwl_seat *xwl_seat = xwl_tablet_tool->seat; + + if (!xwl_seat->tablet_focus_window) + return; + + xwl_tablet_tool->tilt_x = wl_fixed_to_double(tilt_x); + xwl_tablet_tool->tilt_y = wl_fixed_to_double(tilt_y); +} + +static void +tablet_tool_rotation(void *data, struct zwp_tablet_tool_v2 *tool, + wl_fixed_t angle) +{ + struct xwl_tablet_tool *xwl_tablet_tool = data; + struct xwl_seat *xwl_seat = xwl_tablet_tool->seat; + double rotation = wl_fixed_to_double(angle); + + if (!xwl_seat->tablet_focus_window) + return; + + /* change origin (buttons facing right [libinput +90 degrees]) and + * scaling (5 points per degree) to match wacom driver behavior + */ + rotation = remainderf(rotation + 90.0f, 360.0f); + rotation *= 5.0f; + xwl_tablet_tool->rotation = rotation; +} + +static void +tablet_tool_slider(void *data, struct zwp_tablet_tool_v2 *tool, + int32_t position_raw) +{ + struct xwl_tablet_tool *xwl_tablet_tool = data; + struct xwl_seat *xwl_seat = xwl_tablet_tool->seat; + float position = position_raw / 65535.0; + + if (!xwl_seat->tablet_focus_window) + return; + + xwl_tablet_tool->slider = (position * 1799.0f) - 900.0f; +} + +static void +tablet_tool_wheel(void *data, struct zwp_tablet_tool_v2 *tool, + wl_fixed_t degrees, int32_t clicks) +{ + struct xwl_tablet_tool *xwl_tablet_tool = data; + struct xwl_seat *xwl_seat = xwl_tablet_tool->seat; + + if (!xwl_seat->tablet_focus_window) + return; + + xwl_tablet_tool->wheel_clicks = clicks; +} + +static void +tablet_tool_button_state(void *data, struct zwp_tablet_tool_v2 *tool, + uint32_t serial, uint32_t button, uint32_t state) +{ + struct xwl_tablet_tool *xwl_tablet_tool = data; + struct xwl_seat *xwl_seat = xwl_tablet_tool->seat; + uint32_t *mask = &xwl_tablet_tool->buttons_now; + int xbtn = 0; + + /* BTN_0 .. BTN_9 */ + if (button >= 0x100 && button <= 0x109) { + xbtn = button - 0x100 + 1; + } + /* BTN_A .. BTN_Z */ + else if (button >= 0x130 && button <= 0x135) { + xbtn = button - 0x130 + 10; + } + /* BTN_BASE .. BTN_BASE6 */ + else if (button >= 0x126 && button <= 0x12b) { + xbtn = button - 0x126 + 16; + } + else { + switch (button) { + case 0x110: /* BTN_LEFT */ + case 0x14a: /* BTN_TOUCH */ + xbtn = 1; + break; + + case 0x112: /* BTN_MIDDLE */ + case 0x14b: /* BTN_STYLUS */ + xbtn = 2; + break; + + case 0x111: /* BTN_RIGHT */ + case 0x14c: /* BTN_STYLUS2 */ + xbtn = 3; + break; + + case 0x113: /* BTN_SIDE */ + case 0x116: /* BTN_BACK */ + case 0x149: /* BTN_STYLUS3 */ + xbtn = 8; + break; + + case 0x114: /* BTN_EXTRA */ + case 0x115: /* BTN_FORWARD */ + xbtn = 9; + break; + } + } + + if (!xbtn) { + ErrorF("unknown tablet button number %d\n", button); + return; + } + + BUG_RETURN(xbtn >= 8 * sizeof(*mask)); + + if (state) + SetBit(mask, xbtn - 1); + else + ClearBit(mask, xbtn - 1); + + xwl_seat->xwl_screen->serial = serial; +} + +static void +tablet_tool_frame(void *data, struct zwp_tablet_tool_v2 *tool, uint32_t time) +{ + struct xwl_tablet_tool *xwl_tablet_tool = data; + ValuatorMask mask; + uint32_t released, pressed, diff; + int button; + + valuator_mask_zero(&mask); + valuator_mask_set_double(&mask, 0, xwl_tablet_tool->x); + valuator_mask_set_double(&mask, 1, xwl_tablet_tool->y); + valuator_mask_set(&mask, 2, xwl_tablet_tool->pressure); + valuator_mask_set_double(&mask, 3, xwl_tablet_tool->tilt_x); + valuator_mask_set_double(&mask, 4, xwl_tablet_tool->tilt_y); + valuator_mask_set_double(&mask, 5, xwl_tablet_tool->rotation + xwl_tablet_tool->slider); + + QueuePointerEvents(xwl_tablet_tool->xdevice, MotionNotify, 0, + POINTER_ABSOLUTE | POINTER_DESKTOP, &mask); + + valuator_mask_zero(&mask); + + diff = xwl_tablet_tool->buttons_prev ^ xwl_tablet_tool->buttons_now; + released = diff & ~xwl_tablet_tool->buttons_now; + pressed = diff & xwl_tablet_tool->buttons_now; + + button = 1; + while (released) { + if (released & 0x1) + QueuePointerEvents(xwl_tablet_tool->xdevice, + ButtonRelease, button, 0, &mask); + button++; + released >>= 1; + } + + button = 1; + while (pressed) { + if (pressed & 0x1) + QueuePointerEvents(xwl_tablet_tool->xdevice, + ButtonPress, button, 0, &mask); + button++; + pressed >>= 1; + } + + xwl_tablet_tool->buttons_prev = xwl_tablet_tool->buttons_now; + + while (xwl_tablet_tool->wheel_clicks) { + if (xwl_tablet_tool->wheel_clicks < 0) { + button = 4; + xwl_tablet_tool->wheel_clicks++; + } + else { + button = 5; + xwl_tablet_tool->wheel_clicks--; + } + + QueuePointerEvents(xwl_tablet_tool->xdevice, + ButtonPress, button, 0, &mask); + QueuePointerEvents(xwl_tablet_tool->xdevice, + ButtonRelease, button, 0, &mask); + + } +} + +static const struct zwp_tablet_tool_v2_listener tablet_tool_listener = { + tablet_tool_receive_type, + tablet_tool_receive_hardware_serial, + tablet_tool_receive_hardware_id_wacom, + tablet_tool_receive_capability, + tablet_tool_receive_done, + tablet_tool_receive_removed, + tablet_tool_proximity_in, + tablet_tool_proximity_out, + tablet_tool_down, + tablet_tool_up, + tablet_tool_motion, + tablet_tool_pressure, + tablet_tool_distance, + tablet_tool_tilt, + tablet_tool_rotation, + tablet_tool_slider, + tablet_tool_wheel, + tablet_tool_button_state, + tablet_tool_frame +}; + +static void +tablet_pad_ring_destroy(struct xwl_tablet_pad_ring *ring) +{ + zwp_tablet_pad_ring_v2_destroy(ring->ring); + xorg_list_del(&ring->link); + free(ring); +} + +static void +tablet_pad_ring_source(void *data, + struct zwp_tablet_pad_ring_v2 *zwp_tablet_pad_ring_v2, + uint32_t source) +{ +} + +static void +tablet_pad_ring_angle(void *data, + struct zwp_tablet_pad_ring_v2 *zwp_tablet_pad_ring_v2, + wl_fixed_t degrees) +{ + struct xwl_tablet_pad_ring *ring = data; + struct xwl_tablet_pad *pad = ring->group->pad; + double deg = wl_fixed_to_double(degrees); + ValuatorMask mask; + + valuator_mask_zero(&mask); + valuator_mask_set(&mask, 5 + ring->index, deg/360.0 * 71); + QueuePointerEvents(pad->xdevice, MotionNotify, 0, 0, &mask); +} + +static void +tablet_pad_ring_stop(void *data, + struct zwp_tablet_pad_ring_v2 *zwp_tablet_pad_ring_v2) +{ +} + +static void +tablet_pad_ring_frame(void *data, + struct zwp_tablet_pad_ring_v2 *zwp_tablet_pad_ring_v2, + uint32_t time) +{ +} + +static const struct zwp_tablet_pad_ring_v2_listener tablet_pad_ring_listener = { + tablet_pad_ring_source, + tablet_pad_ring_angle, + tablet_pad_ring_stop, + tablet_pad_ring_frame, +}; + + +static void +tablet_pad_strip_destroy(struct xwl_tablet_pad_strip *strip) +{ + zwp_tablet_pad_strip_v2_destroy(strip->strip); + xorg_list_del(&strip->link); + free(strip); +} + +static void +tablet_pad_strip_source(void *data, + struct zwp_tablet_pad_strip_v2 *zwp_tablet_pad_strip_v2, + uint32_t source) +{ +} + +static void +tablet_pad_strip_position(void *data, + struct zwp_tablet_pad_strip_v2 *zwp_tablet_pad_strip_v2, + uint32_t position) +{ + struct xwl_tablet_pad_strip *strip = data; + struct xwl_tablet_pad *pad = strip->group->pad; + ValuatorMask mask; + + valuator_mask_zero(&mask); + valuator_mask_set(&mask, 3 + strip->index, position/65535.0 * 2048); + QueuePointerEvents(pad->xdevice, MotionNotify, 0, 0, &mask); +} + +static void +tablet_pad_strip_stop(void *data, + struct zwp_tablet_pad_strip_v2 *zwp_tablet_pad_strip_v2) +{ +} + +static void +tablet_pad_strip_frame(void *data, + struct zwp_tablet_pad_strip_v2 *zwp_tablet_pad_strip_v2, + uint32_t time) +{ +} + +static const struct zwp_tablet_pad_strip_v2_listener tablet_pad_strip_listener = { + tablet_pad_strip_source, + tablet_pad_strip_position, + tablet_pad_strip_stop, + tablet_pad_strip_frame, +}; + +static void +tablet_pad_group_destroy(struct xwl_tablet_pad_group *group) +{ + struct xwl_tablet_pad_ring *r, *tr; + struct xwl_tablet_pad_strip *s, *ts; + + xorg_list_for_each_entry_safe(r, tr, + &group->pad_group_ring_list, + link) + tablet_pad_ring_destroy(r); + + xorg_list_for_each_entry_safe(s, ts, + &group->pad_group_strip_list, + link) + tablet_pad_strip_destroy(s); + + zwp_tablet_pad_group_v2_destroy(group->group); + xorg_list_del(&group->link); + free(group); +} + +static void +tablet_pad_group_buttons(void *data, + struct zwp_tablet_pad_group_v2 *zwp_tablet_pad_group_v2, + struct wl_array *buttons) +{ + +} + +static void +tablet_pad_group_ring(void *data, + struct zwp_tablet_pad_group_v2 *zwp_tablet_pad_group_v2, + struct zwp_tablet_pad_ring_v2 *wp_ring) +{ + static unsigned int ring_index = 0; + struct xwl_tablet_pad_group *group = data; + struct xwl_tablet_pad_ring *ring; + + ring = calloc(1, sizeof *ring); + if (ring == NULL) { + ErrorF("%s ENOMEM\n", __func__); + return; + } + + ring->index = ring_index++; + ring->group = group; + ring->ring = wp_ring; + + xorg_list_add(&ring->link, &group->pad_group_ring_list); + + zwp_tablet_pad_ring_v2_add_listener(wp_ring, &tablet_pad_ring_listener, + ring); +} + +static void +tablet_pad_group_strip(void *data, + struct zwp_tablet_pad_group_v2 *zwp_tablet_pad_group_v2, + struct zwp_tablet_pad_strip_v2 *wp_strip) +{ + static unsigned int strip_index = 0; + struct xwl_tablet_pad_group *group = data; + struct xwl_tablet_pad_strip *strip; + + strip = calloc(1, sizeof *strip); + if (strip == NULL) { + ErrorF("%s ENOMEM\n", __func__); + return; + } + + strip->index = strip_index++; + strip->group = group; + strip->strip = wp_strip; + + xorg_list_add(&strip->link, &group->pad_group_strip_list); + + zwp_tablet_pad_strip_v2_add_listener(wp_strip, &tablet_pad_strip_listener, + strip); +} + +static void +tablet_pad_group_modes(void *data, + struct zwp_tablet_pad_group_v2 *zwp_tablet_pad_group_v2, + uint32_t modes) +{ + +} + +static void +tablet_pad_group_done(void *data, + struct zwp_tablet_pad_group_v2 *zwp_tablet_pad_group_v2) +{ + +} + +static void +tablet_pad_group_mode_switch(void *data, + struct zwp_tablet_pad_group_v2 *zwp_tablet_pad_group_v2, + uint32_t time, + uint32_t serial, + uint32_t mode) +{ + +} + +static struct zwp_tablet_pad_group_v2_listener tablet_pad_group_listener = { + tablet_pad_group_buttons, + tablet_pad_group_ring, + tablet_pad_group_strip, + tablet_pad_group_modes, + tablet_pad_group_done, + tablet_pad_group_mode_switch, +}; + +static int +xwl_tablet_pad_proc(DeviceIntPtr device, int what) +{ + struct xwl_tablet_pad *pad = device->public.devicePrivate; + /* Axis layout mirrors that of xf86-input-wacom to have better + compatibility with existing clients */ +#define NAXES 7 + Atom axes_labels[NAXES] = { 0 }; + BYTE map[MAX_BUTTONS + 1]; + int i = 0; + Atom btn_labels[MAX_BUTTONS] = { 0 }; /* btn labels are meaningless */ + int nbuttons; + + switch (what) { + case DEVICE_INIT: + device->public.on = FALSE; + + axes_labels[0] = XIGetKnownProperty(AXIS_LABEL_PROP_ABS_X); + axes_labels[1] = XIGetKnownProperty(AXIS_LABEL_PROP_ABS_Y); + /* The others have no good mapping */ + + if (!InitValuatorClassDeviceStruct(device, NAXES, axes_labels, + GetMotionHistorySize(), Absolute)) + return BadValue; + + for (i = 1; i <= MAX_BUTTONS; i++) + map[i] = i; + + /* We need at least 7 buttons to allow scrolling */ + nbuttons = min(max(pad->nbuttons + 4, 7), MAX_BUTTONS); + + if (!InitButtonClassDeviceStruct(device, nbuttons, + btn_labels, map)) + return BadValue; + + /* Valuators */ + InitValuatorAxisStruct(device, 0, axes_labels[0], + 0, 100, 1, 0, 1, Absolute); + InitValuatorAxisStruct(device, 1, axes_labels[1], + 0, 100, 1, 0, 1, Absolute); + /* Pressure - unused, for backwards compat only */ + InitValuatorAxisStruct(device, 2, axes_labels[2], + 0, 2048, 1, 0, 1, Absolute); + /* strip x */ + InitValuatorAxisStruct(device, 3, axes_labels[3], + 0, 2048, 1, 0, 1, Absolute); + /* strip y */ + InitValuatorAxisStruct(device, 4, axes_labels[4], + 0, 2048, 1, 0, 1, Absolute); + /* ring */ + InitValuatorAxisStruct(device, 5, axes_labels[5], + 0, 71, 1, 0, 1, Absolute); + /* ring2 */ + InitValuatorAxisStruct(device, 6, axes_labels[6], + 0, 71, 1, 0, 1, Absolute); + + if (!InitPtrFeedbackClassDeviceStruct(device, xwl_pointer_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; +#undef NAXES +} + +static void +tablet_pad_group(void *data, + struct zwp_tablet_pad_v2 *zwp_tablet_pad_v2, + struct zwp_tablet_pad_group_v2 *pad_group) +{ + struct xwl_tablet_pad *pad = data; + struct xwl_tablet_pad_group *group; + + group = calloc(1, sizeof *group); + if (pad == NULL) { + ErrorF("%s ENOMEM\n", __func__); + return; + } + + group->pad = pad; + group->group = pad_group; + xorg_list_init(&group->pad_group_ring_list); + xorg_list_init(&group->pad_group_strip_list); + + xorg_list_add(&group->link, &pad->pad_group_list); + + zwp_tablet_pad_group_v2_add_listener(pad_group, + &tablet_pad_group_listener, + group); +} + +static void +tablet_pad_path(void *data, + struct zwp_tablet_pad_v2 *zwp_tablet_pad_v2, + const char *path) +{ + +} + +static void +tablet_pad_buttons(void *data, + struct zwp_tablet_pad_v2 *zwp_tablet_pad_v2, + uint32_t buttons) +{ + struct xwl_tablet_pad *pad = data; + + pad->nbuttons = buttons; +} + +static void +tablet_pad_done(void *data, + struct zwp_tablet_pad_v2 *zwp_tablet_pad_v2) +{ + struct xwl_tablet_pad *pad = data; + + pad->xdevice = add_device(pad->seat, "xwayland-pad", + xwl_tablet_pad_proc); + pad->xdevice->public.devicePrivate = pad; + ActivateDevice(pad->xdevice, TRUE); + EnableDevice(pad->xdevice, TRUE); +} + +static void +tablet_pad_button(void *data, + struct zwp_tablet_pad_v2 *zwp_tablet_pad_v2, + uint32_t time, + uint32_t button, + uint32_t state) +{ + struct xwl_tablet_pad *pad = data; + ValuatorMask mask; + + button++; /* wayland index vs X's 1-offset */ + /* skip scroll wheel buttons 4-7 */ + button = button > 3 ? button + 4 : button; + + valuator_mask_zero(&mask); + QueuePointerEvents(pad->xdevice, + state ? ButtonPress : ButtonRelease, button, 0, &mask); +} + +static void +tablet_pad_enter(void *data, + struct zwp_tablet_pad_v2 *zwp_tablet_pad_v2, + uint32_t serial, + struct zwp_tablet_v2 *tablet, + struct wl_surface *surface) +{ + /* pairs the pad with the tablet but also to set the focus. We + * don't care about the pairing and always use X's focus */ +} + +static void +tablet_pad_leave(void *data, + struct zwp_tablet_pad_v2 *zwp_tablet_pad_v2, + uint32_t serial, + struct wl_surface *surface) +{ + /* pairs the pad with the tablet but also to set the focus. We + * don't care about the pairing and always use X's focus */ +} + +static void +tablet_pad_removed(void *data, + struct zwp_tablet_pad_v2 *zwp_tablet_pad_v2) +{ + struct xwl_tablet_pad *pad = data; + struct xwl_tablet_pad_group *g, *tg; + + xorg_list_for_each_entry_safe(g, tg, &pad->pad_group_list, link) + tablet_pad_group_destroy(g); + + RemoveDevice(pad->xdevice, TRUE); + xorg_list_del(&pad->link); + zwp_tablet_pad_v2_destroy(pad->pad); + free(pad); +} + +static const struct zwp_tablet_pad_v2_listener tablet_pad_listener = { + tablet_pad_group, + tablet_pad_path, + tablet_pad_buttons, + tablet_pad_done, + tablet_pad_button, + tablet_pad_enter, + tablet_pad_leave, + tablet_pad_removed, +}; + +static void +tablet_seat_handle_add_tablet(void *data, struct zwp_tablet_seat_v2 *tablet_seat, + struct zwp_tablet_v2 *tablet) +{ + struct xwl_seat *xwl_seat = data; + struct xwl_tablet *xwl_tablet; + + xwl_tablet = calloc(sizeof *xwl_tablet, 1); + if (xwl_tablet == NULL) { + ErrorF("%s ENOMEM\n", __func__); + return; + } + + xwl_tablet->tablet = tablet; + xwl_tablet->seat = xwl_seat; + + xorg_list_add(&xwl_tablet->link, &xwl_seat->tablets); + + zwp_tablet_v2_add_listener(tablet, &tablet_listener, xwl_tablet); +} + +static void +xwl_tablet_tool_update_cursor(struct xwl_cursor *xwl_cursor) +{ + struct xwl_tablet_tool *xwl_tablet_tool = wl_container_of(xwl_cursor, + xwl_tablet_tool, + cursor); + xwl_tablet_tool_set_cursor(xwl_tablet_tool); +} + +static void +tablet_seat_handle_add_tool(void *data, struct zwp_tablet_seat_v2 *tablet_seat, + struct zwp_tablet_tool_v2 *tool) +{ + struct xwl_seat *xwl_seat = data; + struct xwl_screen *xwl_screen = xwl_seat->xwl_screen; + struct xwl_tablet_tool *xwl_tablet_tool; + + xwl_tablet_tool = calloc(sizeof *xwl_tablet_tool, 1); + if (xwl_tablet_tool == NULL) { + ErrorF("%s ENOMEM\n", __func__); + return; + } + + xwl_tablet_tool->tool = tool; + xwl_tablet_tool->seat = xwl_seat; + xwl_cursor_init(&xwl_tablet_tool->cursor, xwl_screen, + xwl_tablet_tool_update_cursor); + + xorg_list_add(&xwl_tablet_tool->link, &xwl_seat->tablet_tools); + + zwp_tablet_tool_v2_add_listener(tool, &tablet_tool_listener, xwl_tablet_tool); +} + +static void +tablet_seat_handle_add_pad(void *data, struct zwp_tablet_seat_v2 *tablet_seat, + struct zwp_tablet_pad_v2 *pad) +{ + struct xwl_seat *xwl_seat = data; + struct xwl_tablet_pad *xwl_tablet_pad; + + xwl_tablet_pad = calloc(sizeof *xwl_tablet_pad, 1); + if (xwl_tablet_pad == NULL) { + ErrorF("%s ENOMEM\n", __func__); + return; + } + + xwl_tablet_pad->pad = pad; + xwl_tablet_pad->seat = xwl_seat; + xorg_list_init(&xwl_tablet_pad->pad_group_list); + + xorg_list_add(&xwl_tablet_pad->link, &xwl_seat->tablet_pads); + + zwp_tablet_pad_v2_add_listener(pad, &tablet_pad_listener, + xwl_tablet_pad); +} + +static const struct zwp_tablet_seat_v2_listener tablet_seat_listener = { + tablet_seat_handle_add_tablet, + tablet_seat_handle_add_tool, + tablet_seat_handle_add_pad +}; + +static void +init_tablet_manager_seat(struct xwl_screen *xwl_screen, + struct xwl_seat *xwl_seat) +{ + xorg_list_init(&xwl_seat->tablets); + xorg_list_init(&xwl_seat->tablet_tools); + xorg_list_init(&xwl_seat->tablet_pads); + + if (!xwl_screen->tablet_manager) + return; + + xwl_seat->tablet_seat = + zwp_tablet_manager_v2_get_tablet_seat(xwl_screen->tablet_manager, + xwl_seat->seat); + + zwp_tablet_seat_v2_add_listener(xwl_seat->tablet_seat, &tablet_seat_listener, xwl_seat); +} + +static void +release_tablet_manager_seat(struct xwl_seat *xwl_seat) +{ + struct xwl_tablet *xwl_tablet, *next_xwl_tablet; + struct xwl_tablet_tool *xwl_tablet_tool, *next_xwl_tablet_tool; + struct xwl_tablet_pad *xwl_tablet_pad, *next_xwl_tablet_pad; + + xorg_list_for_each_entry_safe(xwl_tablet_pad, next_xwl_tablet_pad, + &xwl_seat->tablet_pads, link) { + xorg_list_del(&xwl_tablet_pad->link); + zwp_tablet_pad_v2_destroy(xwl_tablet_pad->pad); + free(xwl_tablet_pad); + } + + xorg_list_for_each_entry_safe(xwl_tablet_tool, next_xwl_tablet_tool, + &xwl_seat->tablet_tools, link) { + xorg_list_del(&xwl_tablet_tool->link); + zwp_tablet_tool_v2_destroy(xwl_tablet_tool->tool); + free(xwl_tablet_tool); + } + + xorg_list_for_each_entry_safe(xwl_tablet, next_xwl_tablet, + &xwl_seat->tablets, link) { + xorg_list_del(&xwl_tablet->link); + zwp_tablet_v2_destroy(xwl_tablet->tablet); + free(xwl_tablet); + } + + if (xwl_seat->tablet_seat) { + zwp_tablet_seat_v2_destroy(xwl_seat->tablet_seat); + xwl_seat->tablet_seat = NULL; + } +} + +static void +init_tablet_manager(struct xwl_screen *xwl_screen, uint32_t id, uint32_t version) +{ + struct xwl_seat *xwl_seat; + + xwl_screen->tablet_manager = wl_registry_bind(xwl_screen->registry, + id, + &zwp_tablet_manager_v2_interface, + min(version,1)); + + xorg_list_for_each_entry(xwl_seat, &xwl_screen->seat_list, link) { + init_tablet_manager_seat(xwl_screen, xwl_seat); + } +} + +void +xwl_screen_release_tablet_manager(struct xwl_screen *xwl_screen) +{ + if (xwl_screen->tablet_manager) { + zwp_tablet_manager_v2_destroy(xwl_screen->tablet_manager); + xwl_screen->tablet_manager = NULL; + } +} + +static void init_relative_pointer_manager(struct xwl_screen *xwl_screen, uint32_t id, uint32_t version) { @@ -1198,6 +2440,27 @@ init_pointer_constraints(struct xwl_screen *xwl_screen, } static void +init_keyboard_grab(struct xwl_screen *xwl_screen, + uint32_t id, uint32_t version) +{ + struct xwl_seat *xwl_seat; + DeviceIntPtr master; + + xwl_screen->wp_grab = + wl_registry_bind(xwl_screen->registry, id, + &zwp_xwayland_keyboard_grab_manager_v1_interface, + 1); + + xorg_list_for_each_entry(xwl_seat, &xwl_screen->seat_list, link) { + if (xwl_seat->keyboard) { + master = GetMaster(xwl_seat->keyboard, MASTER_KEYBOARD); + if (master) + setup_keyboard_grab_handler(master); + } + } +} + +static void input_handler(void *data, struct wl_registry *registry, uint32_t id, const char *interface, uint32_t version) { @@ -1210,6 +2473,10 @@ input_handler(void *data, struct wl_registry *registry, uint32_t id, init_relative_pointer_manager(xwl_screen, id, version); } else if (strcmp(interface, "zwp_pointer_constraints_v1") == 0) { init_pointer_constraints(xwl_screen, id, version); + } else if (strcmp(interface, "zwp_tablet_manager_v2") == 0) { + init_tablet_manager(xwl_screen, id, version); + } else if (strcmp(interface, "zwp_xwayland_keyboard_grab_manager_v1") == 0) { + init_keyboard_grab(xwl_screen, id, version); } } @@ -1457,9 +2724,9 @@ xwl_pointer_warp_emulator_create(struct xwl_seat *xwl_seat) { struct xwl_pointer_warp_emulator *warp_emulator; - warp_emulator = calloc(sizeof *warp_emulator, 1); + warp_emulator = calloc(1, sizeof *warp_emulator); if (!warp_emulator) { - ErrorF("%s: ENOMEM", __func__); + ErrorF("%s: ENOMEM\n", __func__); return NULL; } |