wings(wayland-client): wl_pointer + wl_keyboard input dispatch
Task 5.
wclient_wl.c subscribes to wl_seat.capabilities. On POINTER capability
it binds wl_pointer with a listener that translates enter/leave/motion/
button events into WMEvent (WME_ENTER, WME_LEAVE, WME_MOTION,
WME_BUTTON_PRESS/RELEASE). Linux evdev button codes (BTN_LEFT, etc.)
map to X-style numbers (1, 2, 3, 8, 9). Pointer focus is tracked per
wl_surface and resolved to a top-level W_View via wlc_top_for_surface.
On KEYBOARD capability it binds wl_keyboard. The keymap event mmaps
the fd, compiles via xkb_keymap_new_from_string, and stashes the result
in WLClientCtx (xkb_keymap + xkb_state). Subsequent key events use
xkb_state_key_get_one_sym for the keysym and xkb_state_key_get_utf8 for
text — these populate WMEKey.{keycode, keysym, utf8, utf8_len}.
Modifiers update xkb_state and a cached X-style mod mask
(ShiftMask, ControlMask, Mod1Mask, Mod4Mask) used in WMEKey.state and
WMEButton.state.
Focus enter/leave on the keyboard side dispatches WME_FOCUS_IN /
WME_FOCUS_OUT so WINGs widgets get focus tracking.
Events flow via W_WL_EnqueueEvent, the same ring buffer wevent_wl.c
already drains for the compositor side — no separate event-pump
required. Axis (scroll) events are stubbed out for now and will be
synthesized as button 4/5 in a follow-up.
This commit is contained in:
parent
97b2d03a34
commit
da3bf4c096
|
|
@ -472,6 +472,361 @@ wl_client_toplevel_destroy(struct W_View *view)
|
|||
wl_client_view_destroy_backing((unsigned long)view->window);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Input: wl_pointer / wl_keyboard listeners */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
#include "WMEvent.h"
|
||||
|
||||
/* Map an X11-style state mask from xkbcommon modifier state. */
|
||||
static unsigned int
|
||||
wlc_xkb_mod_mask(struct xkb_state *state)
|
||||
{
|
||||
if (!state) return 0;
|
||||
unsigned int mask = 0;
|
||||
if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_SHIFT,
|
||||
XKB_STATE_MODS_EFFECTIVE) > 0)
|
||||
mask |= ShiftMask;
|
||||
if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_CTRL,
|
||||
XKB_STATE_MODS_EFFECTIVE) > 0)
|
||||
mask |= ControlMask;
|
||||
if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_ALT,
|
||||
XKB_STATE_MODS_EFFECTIVE) > 0)
|
||||
mask |= Mod1Mask;
|
||||
if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_LOGO,
|
||||
XKB_STATE_MODS_EFFECTIVE) > 0)
|
||||
mask |= Mod4Mask;
|
||||
return mask;
|
||||
}
|
||||
|
||||
/* Pointer state */
|
||||
static double wlc_ptr_x, wlc_ptr_y;
|
||||
static uint32_t wlc_ptr_focus_serial;
|
||||
static WLClientToplevel *wlc_ptr_focused;
|
||||
static unsigned int wlc_ptr_button_state;
|
||||
static unsigned int wlc_ptr_mods;
|
||||
|
||||
static WLClientToplevel *
|
||||
wlc_top_for_surface(struct wl_surface *surf)
|
||||
{
|
||||
for (int i = 0; i < WLC_TOPTABLE_SIZE; i++) {
|
||||
if (wlc_toptable[i].id && wlc_toptable[i].top &&
|
||||
wlc_toptable[i].top->surface == surf)
|
||||
return wlc_toptable[i].top;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
pointer_handle_enter(void *data, struct wl_pointer *p, uint32_t serial,
|
||||
struct wl_surface *surface, wl_fixed_t sx, wl_fixed_t sy)
|
||||
{
|
||||
(void)p;
|
||||
WLClientCtx *ctx = data;
|
||||
wlc_ptr_focus_serial = serial;
|
||||
wlc_ptr_focused = wlc_top_for_surface(surface);
|
||||
wlc_ptr_x = wl_fixed_to_double(sx);
|
||||
wlc_ptr_y = wl_fixed_to_double(sy);
|
||||
if (!wlc_ptr_focused) return;
|
||||
|
||||
WMEvent ev;
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.type = WME_ENTER;
|
||||
ev.window = wlc_ptr_focused->view->window;
|
||||
ev.u.crossing.window = ev.window;
|
||||
ev.u.crossing.x = (int)wlc_ptr_x;
|
||||
ev.u.crossing.y = (int)wlc_ptr_y;
|
||||
ev.u.crossing.x_root = (int)wlc_ptr_x;
|
||||
ev.u.crossing.y_root = (int)wlc_ptr_y;
|
||||
ev.u.crossing.mode = NotifyNormal;
|
||||
ev.u.crossing.detail = NotifyAncestor;
|
||||
W_WL_EnqueueEvent(ctx->scr, &ev, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
pointer_handle_leave(void *data, struct wl_pointer *p, uint32_t serial,
|
||||
struct wl_surface *surface)
|
||||
{
|
||||
(void)p; (void)serial; (void)surface;
|
||||
WLClientCtx *ctx = data;
|
||||
if (!wlc_ptr_focused) return;
|
||||
|
||||
WMEvent ev;
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.type = WME_LEAVE;
|
||||
ev.window = wlc_ptr_focused->view->window;
|
||||
ev.u.crossing.window = ev.window;
|
||||
ev.u.crossing.mode = NotifyNormal;
|
||||
ev.u.crossing.detail = NotifyAncestor;
|
||||
W_WL_EnqueueEvent(ctx->scr, &ev, NULL);
|
||||
wlc_ptr_focused = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
pointer_handle_motion(void *data, struct wl_pointer *p, uint32_t time,
|
||||
wl_fixed_t sx, wl_fixed_t sy)
|
||||
{
|
||||
(void)p;
|
||||
WLClientCtx *ctx = data;
|
||||
wlc_ptr_x = wl_fixed_to_double(sx);
|
||||
wlc_ptr_y = wl_fixed_to_double(sy);
|
||||
if (!wlc_ptr_focused) return;
|
||||
|
||||
WMEvent ev;
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.type = WME_MOTION;
|
||||
ev.window = wlc_ptr_focused->view->window;
|
||||
ev.u.motion.window = ev.window;
|
||||
ev.u.motion.time = time;
|
||||
ev.u.motion.x = (int)wlc_ptr_x;
|
||||
ev.u.motion.y = (int)wlc_ptr_y;
|
||||
ev.u.motion.x_root = (int)wlc_ptr_x;
|
||||
ev.u.motion.y_root = (int)wlc_ptr_y;
|
||||
ev.u.motion.state = wlc_ptr_button_state | wlc_ptr_mods;
|
||||
W_WL_EnqueueEvent(ctx->scr, &ev, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
pointer_handle_button(void *data, struct wl_pointer *p, uint32_t serial,
|
||||
uint32_t time, uint32_t button, uint32_t state)
|
||||
{
|
||||
(void)p; (void)serial;
|
||||
WLClientCtx *ctx = data;
|
||||
if (!wlc_ptr_focused) return;
|
||||
|
||||
/* Linux input event codes -> X-style button numbers. */
|
||||
unsigned int wmb;
|
||||
switch (button) {
|
||||
case 0x110 /*BTN_LEFT*/: wmb = 1; break;
|
||||
case 0x112 /*BTN_MIDDLE*/: wmb = 2; break;
|
||||
case 0x111 /*BTN_RIGHT*/: wmb = 3; break;
|
||||
case 0x113 /*BTN_SIDE*/: wmb = 8; break;
|
||||
case 0x114 /*BTN_EXTRA*/: wmb = 9; break;
|
||||
default: wmb = 0; break;
|
||||
}
|
||||
if (!wmb) return;
|
||||
|
||||
unsigned int mask = (wmb == 1) ? Button1Mask :
|
||||
(wmb == 2) ? Button2Mask :
|
||||
(wmb == 3) ? Button3Mask : 0;
|
||||
if (state == WL_POINTER_BUTTON_STATE_PRESSED)
|
||||
wlc_ptr_button_state |= mask;
|
||||
else
|
||||
wlc_ptr_button_state &= ~mask;
|
||||
|
||||
WMEvent ev;
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.type = (state == WL_POINTER_BUTTON_STATE_PRESSED)
|
||||
? WME_BUTTON_PRESS : WME_BUTTON_RELEASE;
|
||||
ev.window = wlc_ptr_focused->view->window;
|
||||
ev.u.button.window = ev.window;
|
||||
ev.u.button.button = wmb;
|
||||
ev.u.button.state = wlc_ptr_button_state | wlc_ptr_mods;
|
||||
ev.u.button.time = time;
|
||||
ev.u.button.x = (int)wlc_ptr_x;
|
||||
ev.u.button.y = (int)wlc_ptr_y;
|
||||
ev.u.button.x_root = (int)wlc_ptr_x;
|
||||
ev.u.button.y_root = (int)wlc_ptr_y;
|
||||
W_WL_EnqueueEvent(ctx->scr, &ev, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
pointer_handle_axis(void *data, struct wl_pointer *p, uint32_t time,
|
||||
uint32_t axis, wl_fixed_t value)
|
||||
{
|
||||
(void)data; (void)p; (void)time; (void)axis; (void)value;
|
||||
/* TODO: synthesize button 4/5 events for vertical scroll once we
|
||||
* verify the basic flow. For now scroll is dropped. */
|
||||
}
|
||||
|
||||
static void pointer_handle_frame(void *d, struct wl_pointer *p) { (void)d; (void)p; }
|
||||
static void pointer_handle_axis_source(void *d, struct wl_pointer *p, uint32_t s) { (void)d; (void)p; (void)s; }
|
||||
static void pointer_handle_axis_stop(void *d, struct wl_pointer *p, uint32_t t, uint32_t a) { (void)d; (void)p; (void)t; (void)a; }
|
||||
static void pointer_handle_axis_discrete(void *d, struct wl_pointer *p, uint32_t a, int32_t v) { (void)d; (void)p; (void)a; (void)v; }
|
||||
|
||||
static const struct wl_pointer_listener pointer_listener = {
|
||||
.enter = pointer_handle_enter,
|
||||
.leave = pointer_handle_leave,
|
||||
.motion = pointer_handle_motion,
|
||||
.button = pointer_handle_button,
|
||||
.axis = pointer_handle_axis,
|
||||
.frame = pointer_handle_frame,
|
||||
.axis_source = pointer_handle_axis_source,
|
||||
.axis_stop = pointer_handle_axis_stop,
|
||||
.axis_discrete = pointer_handle_axis_discrete,
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Keyboard */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
static WLClientToplevel *wlc_kbd_focused;
|
||||
|
||||
static void
|
||||
keyboard_handle_keymap(void *data, struct wl_keyboard *k,
|
||||
uint32_t format, int fd, uint32_t size)
|
||||
{
|
||||
(void)k;
|
||||
WLClientCtx *ctx = data;
|
||||
if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
char *map = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
close(fd);
|
||||
if (map == MAP_FAILED) return;
|
||||
|
||||
struct xkb_keymap *km = xkb_keymap_new_from_string(ctx->xkb_ctx, map,
|
||||
XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||
munmap(map, size);
|
||||
if (!km) return;
|
||||
|
||||
if (ctx->xkb_state) xkb_state_unref(ctx->xkb_state);
|
||||
if (ctx->xkb_keymap) xkb_keymap_unref(ctx->xkb_keymap);
|
||||
ctx->xkb_keymap = km;
|
||||
ctx->xkb_state = xkb_state_new(km);
|
||||
}
|
||||
|
||||
static void
|
||||
keyboard_handle_enter(void *data, struct wl_keyboard *k, uint32_t serial,
|
||||
struct wl_surface *surface, struct wl_array *keys)
|
||||
{
|
||||
(void)k; (void)serial; (void)keys;
|
||||
WLClientCtx *ctx = data;
|
||||
wlc_kbd_focused = wlc_top_for_surface(surface);
|
||||
if (!wlc_kbd_focused) return;
|
||||
|
||||
WMEvent ev;
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.type = WME_FOCUS_IN;
|
||||
ev.window = wlc_kbd_focused->view->window;
|
||||
ev.u.focus.window = ev.window;
|
||||
W_WL_EnqueueEvent(ctx->scr, &ev, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
keyboard_handle_leave(void *data, struct wl_keyboard *k, uint32_t serial,
|
||||
struct wl_surface *surface)
|
||||
{
|
||||
(void)k; (void)serial; (void)surface;
|
||||
WLClientCtx *ctx = data;
|
||||
if (!wlc_kbd_focused) return;
|
||||
|
||||
WMEvent ev;
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.type = WME_FOCUS_OUT;
|
||||
ev.window = wlc_kbd_focused->view->window;
|
||||
ev.u.focus.window = ev.window;
|
||||
W_WL_EnqueueEvent(ctx->scr, &ev, NULL);
|
||||
wlc_kbd_focused = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
keyboard_handle_key(void *data, struct wl_keyboard *k, uint32_t serial,
|
||||
uint32_t time, uint32_t key, uint32_t state)
|
||||
{
|
||||
(void)k; (void)serial;
|
||||
WLClientCtx *ctx = data;
|
||||
if (!wlc_kbd_focused || !ctx->xkb_state) return;
|
||||
|
||||
/* wl_keyboard.key gives a Linux evdev keycode; xkbcommon expects +8. */
|
||||
uint32_t xkb_keycode = key + 8;
|
||||
xkb_keysym_t sym = xkb_state_key_get_one_sym(ctx->xkb_state, xkb_keycode);
|
||||
|
||||
char utf8[8] = {0};
|
||||
int utf8_len = 0;
|
||||
if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
|
||||
utf8_len = xkb_state_key_get_utf8(ctx->xkb_state, xkb_keycode,
|
||||
utf8, sizeof(utf8));
|
||||
if (utf8_len < 0) utf8_len = 0;
|
||||
if (utf8_len > 7) utf8_len = 7;
|
||||
}
|
||||
|
||||
WMEvent ev;
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.type = (state == WL_KEYBOARD_KEY_STATE_PRESSED)
|
||||
? WME_KEY_PRESS : WME_KEY_RELEASE;
|
||||
ev.window = wlc_kbd_focused->view->window;
|
||||
ev.u.key.window = ev.window;
|
||||
ev.u.key.keycode = xkb_keycode;
|
||||
ev.u.key.state = wlc_ptr_button_state | wlc_ptr_mods;
|
||||
ev.u.key.time = time;
|
||||
ev.u.key.keysym = sym;
|
||||
if (utf8_len > 0) {
|
||||
memcpy(ev.u.key.utf8, utf8, utf8_len);
|
||||
ev.u.key.utf8_len = utf8_len;
|
||||
}
|
||||
W_WL_EnqueueEvent(ctx->scr, &ev, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
keyboard_handle_modifiers(void *data, struct wl_keyboard *k, uint32_t serial,
|
||||
uint32_t mods_depressed, uint32_t mods_latched,
|
||||
uint32_t mods_locked, uint32_t group)
|
||||
{
|
||||
(void)k; (void)serial;
|
||||
WLClientCtx *ctx = data;
|
||||
if (ctx->xkb_state) {
|
||||
xkb_state_update_mask(ctx->xkb_state,
|
||||
mods_depressed, mods_latched, mods_locked,
|
||||
0, 0, group);
|
||||
wlc_ptr_mods = wlc_xkb_mod_mask(ctx->xkb_state);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
keyboard_handle_repeat_info(void *data, struct wl_keyboard *k,
|
||||
int32_t rate, int32_t delay)
|
||||
{
|
||||
(void)data; (void)k; (void)rate; (void)delay;
|
||||
}
|
||||
|
||||
static const struct wl_keyboard_listener keyboard_listener = {
|
||||
.keymap = keyboard_handle_keymap,
|
||||
.enter = keyboard_handle_enter,
|
||||
.leave = keyboard_handle_leave,
|
||||
.key = keyboard_handle_key,
|
||||
.modifiers = keyboard_handle_modifiers,
|
||||
.repeat_info = keyboard_handle_repeat_info,
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Seat capabilities */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
static void
|
||||
seat_handle_capabilities(void *data, struct wl_seat *seat, uint32_t caps)
|
||||
{
|
||||
WLClientCtx *ctx = data;
|
||||
|
||||
if ((caps & WL_SEAT_CAPABILITY_POINTER) && !ctx->pointer) {
|
||||
ctx->pointer = wl_seat_get_pointer(seat);
|
||||
wl_pointer_add_listener(ctx->pointer, &pointer_listener, ctx);
|
||||
} else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && ctx->pointer) {
|
||||
wl_pointer_release(ctx->pointer);
|
||||
ctx->pointer = NULL;
|
||||
}
|
||||
|
||||
if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !ctx->keyboard) {
|
||||
ctx->keyboard = wl_seat_get_keyboard(seat);
|
||||
wl_keyboard_add_listener(ctx->keyboard, &keyboard_listener, ctx);
|
||||
} else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && ctx->keyboard) {
|
||||
wl_keyboard_release(ctx->keyboard);
|
||||
ctx->keyboard = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
seat_handle_name(void *data, struct wl_seat *seat, const char *name)
|
||||
{
|
||||
(void)data; (void)seat; (void)name;
|
||||
}
|
||||
|
||||
static const struct wl_seat_listener seat_listener = {
|
||||
.capabilities = seat_handle_capabilities,
|
||||
.name = seat_handle_name,
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* xdg_wm_base ping */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
|
@ -507,8 +862,7 @@ registry_handle_global(void *data, struct wl_registry *registry,
|
|||
uint32_t v = version < 5 ? version : 5;
|
||||
ctx->seat = wl_registry_bind(registry, name,
|
||||
&wl_seat_interface, v);
|
||||
/* Pointer/keyboard probes happen lazily on the first capabilities event;
|
||||
* we'll add a wl_seat listener below in W_WLClientInit. */
|
||||
wl_seat_add_listener(ctx->seat, &seat_listener, ctx);
|
||||
} else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
|
||||
uint32_t v = version < 3 ? version : 3;
|
||||
ctx->wm_base = wl_registry_bind(registry, name,
|
||||
|
|
|
|||
Loading…
Reference in New Issue