diff --git a/WINGs/wclient_wl.c b/WINGs/wclient_wl.c index 8f5a7ae..b0ec296 100644 --- a/WINGs/wclient_wl.c +++ b/WINGs/wclient_wl.c @@ -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,