diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/cocoa/window.m | 78 | ||||
| -rw-r--r-- | src/wayland/internal.h | 1 | ||||
| -rw-r--r-- | src/wayland/pointer.c | 26 | ||||
| -rw-r--r-- | src/wayland/surface.c | 2 | ||||
| -rw-r--r-- | src/x11/window.c | 239 |
5 files changed, 313 insertions, 33 deletions
diff --git a/src/cocoa/window.m b/src/cocoa/window.m index 6e1e457..6235b77 100644 --- a/src/cocoa/window.m +++ b/src/cocoa/window.m @@ -91,6 +91,8 @@ struct cocoa_surface { uint32_t state; uint32_t mods; enum yasl_cursor cursor; + enum yasl_pointer_device tablet_device; + bool tablet_down; char title[160]; struct yasl_frame_clock clock; @@ -106,6 +108,7 @@ struct cocoa_display { char *app_id; struct yasl_loop *loop; id key_up_monitor; + NSCursor *hidden_cursor; YaslAppDelegate *delegate; NSPoint cascade; struct cocoa_surface *dead; @@ -284,6 +287,18 @@ static void push_pointer(struct cocoa_surface *w, NSEvent *ev, enum yasl_pointer { if (!w || w->closed || button < 0) return; + bool proximity = ev.type == NSEventTypeTabletProximity || + ev.subtype == NSEventSubtypeTabletProximity; + if (proximity) { + if (ev.isEnteringProximity) { + w->tablet_device = ev.pointingDeviceType == NSPointingDeviceTypeEraser ? + YASL_PTR_DEV_ERASER : ev.pointingDeviceType == NSPointingDeviceTypePen ? + YASL_PTR_DEV_PEN : YASL_PTR_DEV_MOUSE; + return; + } + kind = YASL_PTR_LEAVE; + button = 3; + } w->mods = cocoa_mods(ev.modifierFlags); if (kind != YASL_PTR_LEAVE) w->pointer = event_pos(w, ev); @@ -291,20 +306,26 @@ static void push_pointer(struct cocoa_surface *w, NSEvent *ev, enum yasl_pointer enum yasl_pointer_device device = YASL_PTR_DEV_MOUSE; struct yasl_pointer_pen pen = {0}; - float press = (float)ev.pressure; - if (ev.subtype == NSEventSubtypeTabletPoint || press > 0.0f) { - if (ev.pointingDeviceType == NSPointingDeviceTypeEraser) - device = YASL_PTR_DEV_ERASER; - else if (ev.pointingDeviceType == NSPointingDeviceTypePen) - device = YASL_PTR_DEV_PEN; - + if (kind == YASL_PTR_LEAVE && w->tablet_device != YASL_PTR_DEV_MOUSE) { + device = w->tablet_device; + w->tablet_down = false; + } else if (ev.type == NSEventTypeTabletPoint || ev.subtype == NSEventSubtypeTabletPoint) { + /* pointingDeviceType belongs to proximity events. Remember that + * event's tool so erasers and zero-pressure hover stay classified. */ + device = w->tablet_device == YASL_PTR_DEV_ERASER ? YASL_PTR_DEV_ERASER : YASL_PTR_DEV_PEN; + float press = (float)ev.pressure; if (press > 1.0f) press = 1.0f; if (press > 0.0f) pen.pressure = (uint16_t)(press * 65535.0f); NSPoint tilt = ev.tilt; - pen.tilt_x = (int8_t)(tilt.x * 90.0); - pen.tilt_y = (int8_t)(tilt.y * 90.0); + pen.tilt_x = (int8_t)lround(fmax(-1, fmin(1, tilt.x)) * 90.0); + pen.tilt_y = (int8_t)lround(fmax(-1, fmin(1, -tilt.y)) * 90.0); + if (button == 0 && kind == YASL_PTR_PRESS) w->tablet_down = true; + if (button == 0 && kind == YASL_PTR_RELEASE) { + w->tablet_down = false; + pen.pressure = 0; + } } struct yasl_input_event input_ev = { @@ -320,6 +341,10 @@ static void push_pointer(struct cocoa_surface *w, NSEvent *ev, enum yasl_pointer }, }; input_queue_push(&w->input, &input_ev); + if (proximity) { + w->tablet_device = YASL_PTR_DEV_MOUSE; + w->tablet_down = false; + } } static NSCursor *cocoa_cursor(enum yasl_cursor cursor) @@ -328,6 +353,17 @@ static NSCursor *cocoa_cursor(enum yasl_cursor cursor) case YASL_CURSOR_TEXT: return [NSCursor IBeamCursor]; case YASL_CURSOR_LINK: return [NSCursor pointingHandCursor]; case YASL_CURSOR_CROSSHAIR: return [NSCursor crosshairCursor]; + case YASL_CURSOR_HIDDEN: + if (!darwin_ctx.hidden_cursor) { + NSImage *image = [[NSImage alloc] initWithSize:NSMakeSize(1, 1)]; + [image lockFocus]; + [[NSColor clearColor] set]; + NSRectFillUsingOperation(NSMakeRect(0, 0, 1, 1), NSCompositingOperationCopy); + [image unlockFocus]; + darwin_ctx.hidden_cursor = [[NSCursor alloc] initWithImage:image hotSpot:NSZeroPoint]; + [image release]; + } + return darwin_ctx.hidden_cursor; case YASL_CURSOR_ARROW: default: return [NSCursor arrowCursor]; } @@ -566,6 +602,21 @@ static NSCursor *cocoa_cursor(enum yasl_cursor cursor) push_pointer(self.surface, event, YASL_PTR_LEAVE, 3); } +- (void)tabletProximity:(NSEvent *)event +{ + push_pointer(self.surface, event, YASL_PTR_MOTION, 3); +} + +- (void)tabletPoint:(NSEvent *)event +{ + struct cocoa_surface *w = self.surface; + if (!w) return; + bool down = (event.buttonMask & NSEventButtonMaskPenTip) != 0; + enum yasl_pointer_kind kind = down == w->tablet_down ? YASL_PTR_MOTION : + down ? YASL_PTR_PRESS : YASL_PTR_RELEASE; + push_pointer(w, event, kind, 0); +} + - (void)scrollWheel:(NSEvent *)event { struct cocoa_surface *w = self.surface; @@ -1042,6 +1093,7 @@ void cocoa_display_close(struct cocoa_display *d) NSApp.delegate = nil; [d->delegate release]; d->delegate = nil; + [d->hidden_cursor release]; free(d->app_id); free(d->windows); *d = (struct cocoa_display){0}; @@ -1050,7 +1102,7 @@ void cocoa_display_close(struct cocoa_display *d) uint32_t cocoa_display_caps(const struct cocoa_display *d) { return d ? (YASL_CAP_PLACE_WINDOW | YASL_CAP_CSD | YASL_CAP_NATIVE_GRAPHICS | - YASL_CAP_FRAME_PACING) : 0; + YASL_CAP_FRAME_PACING | YASL_CAP_TABLET) : 0; } struct cocoa_surface *cocoa_surface_create_toplevel(struct cocoa_display *d, @@ -1230,12 +1282,8 @@ void cocoa_surface_set_background_color(struct cocoa_surface *w, uint32_t bg) void cocoa_surface_set_cursor(struct cocoa_surface *w, enum yasl_cursor cursor) { - if (!w || w->closed || cursor == w->cursor) return; + if (!w || w->closed || cursor == w->cursor || (unsigned)cursor > YASL_CURSOR_HIDDEN) return; w->cursor = cursor; - if (cursor == YASL_CURSOR_NONE) { - [NSCursor setHiddenUntilMouseMoves:YES]; - return; - } NSPoint p = [w->view convertPoint:w->window.mouseLocationOutsideOfEventStream fromView:nil]; if (w->window.isKeyWindow && NSPointInRect(p, w->view.bounds)) [cocoa_cursor(cursor) set]; diff --git a/src/wayland/internal.h b/src/wayland/internal.h index 249fc8d..04142f7 100644 --- a/src/wayland/internal.h +++ b/src/wayland/internal.h @@ -284,6 +284,7 @@ struct wayland_surface { struct input_queue input; /* 4-byte integers */ + enum yasl_cursor cursor; int ime_cursor_end; int ime_shown_begin, ime_shown_end; int floating_width, floating_height; diff --git a/src/wayland/pointer.c b/src/wayland/pointer.c index 3b3ad73..3341d35 100644 --- a/src/wayland/pointer.c +++ b/src/wayland/pointer.c @@ -164,7 +164,7 @@ static void set_cursor_theme_fallback(struct seat *s, struct wayland_surface *w, {"ew-resize", "ew-resize"}, {"ns-resize", "ns-resize"}, }; - int idx = shape < YASL_CURSOR_NONE ? (int)shape : 0; + int idx = (unsigned)shape < YASL_CURSOR_HIDDEN ? (int)shape : 0; struct wl_cursor *cursor = wl_cursor_theme_get_cursor(c->cursor_theme, names[idx][0]); if (!cursor) cursor = wl_cursor_theme_get_cursor(c->cursor_theme, names[idx][1]); if (!cursor || !cursor->image_count) return; @@ -211,7 +211,7 @@ static void seat_set_cursor(struct seat *s, struct wayland_surface *w, enum yasl if (!s->pointer || !s->pointer_serial || s->pointer_focus != w || s->cursor_shape == (int)shape) return; - if (shape == YASL_CURSOR_NONE) + if (shape == YASL_CURSOR_HIDDEN) wl_pointer_set_cursor(s->pointer, s->pointer_serial, NULL, 0, 0); else if (s->cursor_shape_dev) wp_cursor_shape_device_v1_set_shape(s->cursor_shape_dev, @@ -225,7 +225,7 @@ static void tool_set_cursor(struct tablet_tool *t, enum yasl_cursor shape) { if (!t->proximity_serial) return; - if (shape == YASL_CURSOR_NONE) + if (shape == YASL_CURSOR_HIDDEN) zwp_tablet_tool_v2_set_cursor(t->proxy, t->proximity_serial, NULL, 0, 0); else if (t->shape_dev) wp_cursor_shape_device_v1_set_shape(t->shape_dev, t->proximity_serial, @@ -234,7 +234,8 @@ static void tool_set_cursor(struct tablet_tool *t, enum yasl_cursor shape) void wayland_surface_set_cursor(struct wayland_surface *w, enum yasl_cursor shape) { - if (!w || !w->conn) return; + if (!w || !w->conn || w->destroyed || (unsigned)shape > YASL_CURSOR_HIDDEN) return; + w->cursor = shape; for (struct seat *s = w->conn->seats; s; s = s->next) { seat_set_cursor(s, w, shape); if (s->tool && s->tablet_focus == w) @@ -325,7 +326,7 @@ static void pointer_enter(void *data, struct wl_pointer *pointer, s->pointer_pos = device_from_fixed(w, surface_x, surface_y); s->pointer_serial = serial; s->cursor_shape = -1; - seat_set_cursor(s, w, w->parent ? YASL_CURSOR_ARROW : YASL_CURSOR_TEXT); + seat_set_cursor(s, w, w->cursor); s->motion_pending = true; pointer_event_done(s); } @@ -741,7 +742,7 @@ static void tablet_tool_proximity_in(void *data, if (s->conn->cursor_shape_mgr && !t->shape_dev) t->shape_dev = wp_cursor_shape_manager_v1_get_tablet_tool_v2( s->conn->cursor_shape_mgr, t->proxy); - tool_set_cursor(t, w->parent ? YASL_CURSOR_ARROW : YASL_CURSOR_TEXT); + tool_set_cursor(t, w->cursor); s->tool_motion = true; } @@ -753,6 +754,7 @@ static void tablet_tool_proximity_out(void *data, struct zwp_tablet_tool_v2 *too struct wayland_surface *w = s->tablet_focus; if (w) { press_queue_flush(s, &s->tool_presses, NULL, &s->tool_buttons, tool_event(s)); + s->pen.pressure = 0; struct yasl_mouse_event ev = tool_event(s); ev.pos = s->tool_pos; ev.button = 3; @@ -763,6 +765,8 @@ static void tablet_tool_proximity_out(void *data, struct zwp_tablet_tool_v2 *too s->tablet_focus = NULL; s->tool = NULL; s->tool_motion = false; + s->tool_wheel = 0; + s->tool_buttons = 0; t->proximity_serial = 0; } @@ -867,7 +871,11 @@ static void tool_frame(void *d, struct zwp_tablet_tool_v2 *t, uint32_t time) } press_queue_flush(s, &s->tool_presses, NULL, &s->tool_buttons, like); if (s->tool_wheel) { - report_wheel(s, w, s->tool_pos, s->tool_wheel > 0 ? 5 : 4, -s->tool_wheel, 0.0f); + struct yasl_mouse_event ev = like; + ev.pos = s->tool_pos; + ev.kind = YASL_PTR_SCROLL; + ev.scroll_lines = -s->tool_wheel; + push_mouse(s, w, ev); s->tool_wheel = 0; } } @@ -877,9 +885,7 @@ static void tablet_tool_removed(void *data, struct zwp_tablet_tool_v2 *tool) struct tablet_tool *t = data; struct seat *s = t->seat; if (s->tool == t) { - s->tool = NULL; - s->tablet_focus = NULL; - s->tool_presses.n = 0; + tablet_tool_proximity_out(t, tool); } struct tablet_tool **link = &s->tablet_tools; while (*link && *link != t) diff --git a/src/wayland/surface.c b/src/wayland/surface.c index f291190..ce1764b 100644 --- a/src/wayland/surface.c +++ b/src/wayland/surface.c @@ -284,6 +284,7 @@ uint32_t wayland_display_caps(const struct wayland_display *c) { if (!c) return 0; uint32_t caps = YASL_CAP_POPUPS | YASL_CAP_CSD | YASL_CAP_FRAME_PACING; + if (c->tablet_mgr) caps |= YASL_CAP_TABLET; struct seat *s = conn_active_seat(c); const struct wl_data_device *device = s ? clipboard_data_device(s->clipboard) : NULL; if (device && wl_data_device_get_version((struct wl_data_device *)device) >= 3) @@ -346,6 +347,7 @@ struct wayland_surface *wayland_surface_create_toplevel(struct wayland_display * display_enter(c); conn_link(c, w); wayland_surface_set_callbacks(w, NULL, NULL); + w->cursor = YASL_CURSOR_TEXT; w->req_width = width; w->req_height = height; w->logical_width = width; diff --git a/src/x11/window.c b/src/x11/window.c index 67276cf..85f1d22 100644 --- a/src/x11/window.c +++ b/src/x11/window.c @@ -9,14 +9,17 @@ #include <X11/Xutil.h> #include <X11/cursorfont.h> #include <X11/keysym.h> +#include <X11/extensions/XInput2.h> #include <xkbcommon/xkbcommon.h> #include <assert.h> #include <errno.h> #include <limits.h> +#include <math.h> #include <poll.h> #include <stdlib.h> #include <string.h> +#include <strings.h> #define TEXT_LIMIT (16 * 1024 * 1024) @@ -53,6 +56,18 @@ struct paste_request { Window window; }; +struct tablet_axis { + int number; + double min, max, value; +}; + +struct x11_pointer { + struct x11_pointer *next; + int id; + enum yasl_pointer_device device; + struct tablet_axis pressure, tilt_x, tilt_y; +}; + struct x11_display { struct yasl_display base; Display *native; @@ -70,6 +85,10 @@ struct x11_display { uint32_t mods; char *app_id; bool owns_loop; + int xi_opcode; + bool xi2; + Atom abs_pressure, abs_tilt_x, abs_tilt_y, tool_type, eraser; + struct x11_pointer *pointers; }; static struct x11_display *display_of(const struct yasl_surface *s) @@ -113,6 +132,176 @@ static uint32_t modifiers(unsigned state) ((state & LockMask) ? YASL_MOD_CAPS : 0); } +static void pointer_info(struct x11_display *d, struct x11_pointer *p, + const XIDeviceInfo *info) +{ + p->id = info->deviceid; + p->device = YASL_PTR_DEV_MOUSE; + p->pressure.number = p->tilt_x.number = p->tilt_y.number = -1; + bool touch = false; + for (int i = 0; i < info->num_classes; i++) { + XIAnyClassInfo *c = info->classes[i]; + if (c->type == XITouchClass) touch = true; + if (c->type != XIValuatorClass) continue; + XIValuatorClassInfo *v = (XIValuatorClassInfo *)c; + if (v->mode != XIModeAbsolute) continue; + struct tablet_axis *axis = v->label == d->abs_pressure ? &p->pressure : + v->label == d->abs_tilt_x ? &p->tilt_x : + v->label == d->abs_tilt_y ? &p->tilt_y : NULL; + if (axis) *axis = (struct tablet_axis){v->number, v->min, v->max, v->value}; + } + if (!touch && p->pressure.number >= 0) { + p->device = YASL_PTR_DEV_PEN; + /* Xorg's Wacom and libinput drivers expose separate eraser devices. + * Use the tool property when available, with the device name as a + * fallback for servers (including Xwayland) without that property. */ + if (info->name) { + for (const char *s = info->name; *s; s++) + if (!strncasecmp(s, "eraser", 6)) { p->device = YASL_PTR_DEV_ERASER; break; } + } + } +} + +static struct x11_pointer *pointer_for(struct x11_display *d, int id) +{ + for (struct x11_pointer *p = d->pointers; p; p = p->next) + if (p->id == id) return p; + int count = 0; + XIDeviceInfo *info = XIQueryDevice(d->native, id, &count); + if (!info) return NULL; + struct x11_pointer *p = count ? calloc(1, sizeof(*p)) : NULL; + if (p) { + pointer_info(d, p, info); + if (p->device == YASL_PTR_DEV_PEN || p->device == YASL_PTR_DEV_ERASER) { + Atom type; + int format; + unsigned long n, after; + unsigned char *value = NULL; + if (XIGetProperty(d->native, id, d->tool_type, 0, 1, False, + XA_ATOM, &type, &format, &n, &after, &value) == Success && + type == XA_ATOM && format == 32 && n == 1 && value) + /* XIGetProperty packs format-32 values as uint32_t, unlike + * XGetWindowProperty's array of native-sized longs. */ + p->device = *(uint32_t *)value == d->eraser ? YASL_PTR_DEV_ERASER : p->device; + if (value) XFree(value); + } + p->next = d->pointers; + d->pointers = p; + } + XIFreeDeviceInfo(info); + return p; +} + +static void clear_pointers(struct x11_display *d) +{ + while (d->pointers) { + struct x11_pointer *p = d->pointers; + d->pointers = p->next; + free(p); + } +} + +static void update_axes(struct x11_pointer *p, const XIValuatorState *state) +{ + const double *value = state->values; + for (int i = 0; i < state->mask_len * 8; i++) { + if (!XIMaskIsSet(state->mask, i)) continue; + struct tablet_axis *axes[] = {&p->pressure, &p->tilt_x, &p->tilt_y}; + for (unsigned a = 0; a < sizeof(axes) / sizeof(axes[0]); a++) + if (axes[a]->number == i && isfinite(*value)) axes[a]->value = *value; + value++; + } +} + +static uint16_t pressure_value(const struct tablet_axis *a) +{ + if (a->number < 0 || !(a->max > a->min) || !isfinite(a->value)) return 0; + double v = (a->value - a->min) / (a->max - a->min); + return (uint16_t)lround(fmax(0, fmin(1, v)) * 65535); +} + +static int8_t tilt_value(const struct tablet_axis *a) +{ + /* Xorg tablet tilt valuators are already in degrees. */ + return a->number < 0 || !isfinite(a->value) ? 0 : + (int8_t)lround(fmax(-90, fmin(90, a->value))); +} + +static int pointer_button(unsigned button) +{ + return button == 1 ? 0 : button == 2 ? 1 : button == 3 ? 2 : + button >= 8 && button <= INT16_MAX ? (int)button - 5 : 3; +} + +static void xi_pointer_event(struct x11_surface *s, const XIDeviceEvent *ev) +{ + struct x11_display *d = display_of(&s->base); + struct x11_pointer *p = pointer_for(d, ev->sourceid); + struct yasl_mouse_event mouse = { + .pos = {yasl_scale_to_device(ev->event_x, YASL_SCALE_UNIT), + yasl_scale_to_device(ev->event_y, YASL_SCALE_UNIT)}, + .device = p ? p->device : YASL_PTR_DEV_MOUSE, + .mods = modifiers(ev->mods.effective), + .button = 3, + }; + if (p && (p->device == YASL_PTR_DEV_PEN || p->device == YASL_PTR_DEV_ERASER)) { + update_axes(p, &ev->valuators); + if (ev->evtype == XI_ButtonRelease && ev->detail == 1) + p->pressure.value = p->pressure.min; + mouse.pen = (struct yasl_pointer_pen){ + pressure_value(&p->pressure), tilt_value(&p->tilt_x), tilt_value(&p->tilt_y)}; + } + if (ev->evtype == XI_Motion) mouse.kind = YASL_PTR_MOTION; + else if (ev->detail >= 4 && ev->detail <= 7) { + if (ev->evtype == XI_ButtonRelease) return; + mouse.kind = YASL_PTR_SCROLL; + mouse.scroll_lines = (ev->detail == 4 || ev->detail == 6) ? -1 : 1; + } else { + mouse.kind = ev->evtype == XI_ButtonPress ? YASL_PTR_PRESS : YASL_PTR_RELEASE; + mouse.button = pointer_button(ev->detail); + } + d->mods = mouse.mods; + push(s, (struct yasl_input_event){.kind = YASL_INPUT_MOUSE, .mouse = mouse}, NULL, 0); +} + +static void xi_event(struct x11_display *d, XGenericEventCookie *cookie) +{ + if (cookie->evtype == XI_HierarchyChanged || cookie->evtype == XI_DeviceChanged) { + if (cookie->evtype == XI_DeviceChanged && + ((XIDeviceChangedEvent *)cookie->data)->reason == XISlaveSwitch) return; + clear_pointers(d); + if (cookie->evtype == XI_HierarchyChanged && + (((XIHierarchyEvent *)cookie->data)->flags & + (XISlaveRemoved | XIDeviceDisabled | XISlaveDetached | XIMasterRemoved))) { + /* Removal/disable may interrupt a drag before a release arrives. */ + for (struct x11_surface *s = d->surfaces; s; s = s->next) + push(s, (struct yasl_input_event){.kind = YASL_INPUT_RESET}, NULL, 0); + } + return; + } + bool crossing = cookie->evtype == XI_Enter || cookie->evtype == XI_Leave; + if (!crossing && cookie->evtype != XI_Motion && cookie->evtype != XI_ButtonPress && + cookie->evtype != XI_ButtonRelease) return; + Window window = crossing ? ((XIEnterEvent *)cookie->data)->event : + ((XIDeviceEvent *)cookie->data)->event; + struct x11_surface *s = d->surfaces; + while (s && s->window != window) s = s->next; + if (!s) return; + if (!crossing) xi_pointer_event(s, cookie->data); + else { + const XIEnterEvent *ev = cookie->data; + struct x11_pointer *p = pointer_for(d, ev->sourceid); + if (p) p->pressure.value = p->pressure.min; + push(s, (struct yasl_input_event){.kind = YASL_INPUT_MOUSE, .mouse = { + .pos = {yasl_scale_to_device(ev->event_x, YASL_SCALE_UNIT), + yasl_scale_to_device(ev->event_y, YASL_SCALE_UNIT)}, + .device = p ? p->device : YASL_PTR_DEV_MOUSE, + .button = 3, .mods = modifiers(ev->mods.effective), + .kind = cookie->evtype == XI_Leave ? YASL_PTR_LEAVE : YASL_PTR_MOTION, + }}, NULL, 0); + } +} + static bool resize(struct x11_surface *s, struct yasl_vector2i size) { if (size.x <= 0 || size.y <= 0 || size.x > 32767 || size.y > 32767 || @@ -233,8 +422,19 @@ static struct yasl_surface *create_toplevel(struct yasl_display *base, 0, 0, (unsigned)spec->size.x, (unsigned)spec->size.y, 0, 0, 0); s->gc = XCreateGC(d->native, s->window, 0, NULL); XSelectInput(d->native, s->window, ExposureMask | StructureNotifyMask | - FocusChangeMask | KeyPressMask | KeyReleaseMask | ButtonPressMask | - ButtonReleaseMask | PointerMotionMask | LeaveWindowMask | PropertyChangeMask); + FocusChangeMask | KeyPressMask | KeyReleaseMask | PropertyChangeMask | + (d->xi2 ? 0 : ButtonPressMask | ButtonReleaseMask | + PointerMotionMask | LeaveWindowMask)); + if (d->xi2) { + unsigned char bits[XIMaskLen(XI_LASTEVENT)] = {0}; + XISetMask(bits, XI_Motion); + XISetMask(bits, XI_ButtonPress); + XISetMask(bits, XI_ButtonRelease); + XISetMask(bits, XI_Enter); + XISetMask(bits, XI_Leave); + XIEventMask mask = {XIAllMasterDevices, sizeof(bits), bits}; + XISelectEvents(d->native, s->window, &mask, 1); + } XSetWMProtocols(d->native, s->window, &d->delete_window, 1); XClassHint hint = { .res_name = d->app_id, .res_class = d->app_id }; XSetClassHint(d->native, s->window, &hint); @@ -382,6 +582,12 @@ static void dispatch(struct yasl_display *base) while (XPending(d->native)) { XEvent event; XNextEvent(d->native, &event); + if (event.type == GenericEvent && d->xi2 && + event.xcookie.extension == d->xi_opcode && XGetEventData(d->native, &event.xcookie)) { + xi_event(d, &event.xcookie); + XFreeEventData(d->native, &event.xcookie); + continue; + } if (XFilterEvent(&event, None)) continue; if (event.type == SelectionRequest) { selection_request(d, &event.xselectionrequest); @@ -456,8 +662,7 @@ static void dispatch(struct yasl_display *base) mouse.scroll_lines = (button == 4 || button == 6) ? -1 : 1; } else { mouse.kind = event.type == ButtonPress ? YASL_PTR_PRESS : YASL_PTR_RELEASE; - mouse.button = button == 1 ? 0x110 : button == 2 ? 0x112 : - button == 3 ? 0x111 : (int)button + 0x10b; + mouse.button = pointer_button(button); } } d->mods = mouse.mods = modifiers(state); @@ -577,6 +782,23 @@ static struct yasl_display *display_open(struct yasl_loop *loop, d->native = XOpenDisplay(NULL); if (!d->native) { free(d); errno = ENODEV; return NULL; } d->screen = DefaultScreen(d->native); + int xi_event_base, xi_error_base; + int major = 2, minor = 0; + d->xi2 = XQueryExtension(d->native, "XInputExtension", &d->xi_opcode, + &xi_event_base, &xi_error_base) && + XIQueryVersion(d->native, &major, &minor) == Success && major >= 2; + if (d->xi2) { + d->abs_pressure = XInternAtom(d->native, "Abs Pressure", False); + d->abs_tilt_x = XInternAtom(d->native, "Abs Tilt X", False); + d->abs_tilt_y = XInternAtom(d->native, "Abs Tilt Y", False); + d->tool_type = XInternAtom(d->native, "Wacom Tool Type", False); + d->eraser = XInternAtom(d->native, "ERASER", False); + unsigned char bits[XIMaskLen(XI_LASTEVENT)] = {0}; + XISetMask(bits, XI_HierarchyChanged); + XISetMask(bits, XI_DeviceChanged); + XIEventMask mask = {XIAllDevices, sizeof(bits), bits}; + XISelectEvents(d->native, RootWindow(d->native, d->screen), &mask, 1); + } if (!loop) { d->loop = yasl_loop_create(); d->owns_loop = true; @@ -635,6 +857,7 @@ static void display_close(struct yasl_display *base) d->base.loop = NULL; } if (d->im) XCloseIM(d->im); + clear_pointers(d); XDestroyWindow(d->native, d->selection_window); XCloseDisplay(d->native); free(d->selection[0]); @@ -645,8 +868,8 @@ static void display_close(struct yasl_display *base) static uint32_t caps(const struct yasl_display *d) { - (void)d; - return YASL_CAP_PLACE_WINDOW | YASL_CAP_PRIMARY_SELECTION; + return YASL_CAP_PLACE_WINDOW | YASL_CAP_PRIMARY_SELECTION | + (((const struct x11_display *)d)->xi2 ? YASL_CAP_TABLET : 0); } static void copy(struct yasl_display *base, enum yasl_selection sel, const char *text) @@ -734,10 +957,10 @@ static void cursor(struct yasl_surface *base, enum yasl_cursor cursor) XC_fleur, XC_bottom_right_corner, XC_bottom_left_corner, XC_sb_h_double_arrow, XC_sb_v_double_arrow}; struct x11_surface *s = (struct x11_surface *)base; - if (s->destroyed || (unsigned)cursor > YASL_CURSOR_NONE) return; + if (s->destroyed || (unsigned)cursor > YASL_CURSOR_HIDDEN) return; struct x11_display *d = display_of(base); Cursor native; - if (cursor == YASL_CURSOR_NONE) { + if (cursor == YASL_CURSOR_HIDDEN) { char bits[] = {0}; Pixmap pixmap = XCreateBitmapFromData(d->native, s->window, bits, 1, 1); XColor color = {0}; |
