#include "dat.h" #include "fn.h" #include #include #include #include #include #include #include #include #include "input-method-unstable-v2-client-protocol.h" #include "virtual-keyboard-unstable-v1-client-protocol.h" static struct wl_display *dpy; static struct wl_registry *reg; static struct wl_seat *seat; static struct zwp_input_method_manager_v2 *immgr; static struct zwp_virtual_keyboard_manager_v1 *vkmgr; static struct zwp_input_method_v2 *im; static struct zwp_input_method_keyboard_grab_v2 *grab; static struct zwp_virtual_keyboard_v1 *vk; static struct xkb_context *xkb; static struct xkb_keymap *keymap; static struct xkb_state *xkbst; static int active; static int pending; static int newactivation; static int running; static u32int imserial; static u32int seatname; static u32int immgrname; static u32int vkmgrname; static Channel *replyc; static uvlong owner; static Wlstate keys; static uvlong nowms(void) { struct timespec ts; if(clock_gettime(CLOCK_MONOTONIC, &ts) < 0) return 0; return (uvlong)ts.tv_sec * 1000 + ts.tv_nsec / 1000000; } static void sendkey(u32int ks, u32int mod, Keyres *res) { Keyreq kr; memset(&kr, 0, sizeof kr); kr.owner = owner; kr.op = Keypress; kr.ks = ks; kr.mod = mod; kr.want = 1; kr.reply = replyc; chansend(keyc, &kr); chanrecv(replyc, res); } static void releaseowner(void) { Keyreq kr; Keyres res; if(owner == 0) return; memset(&kr, 0, sizeof kr); kr.owner = owner; kr.op = Keyrelease; kr.reply = replyc; chansend(keyc, &kr); chanrecv(replyc, &res); owner = 0; } static u32int kget(xkb_keysym_t ks) { u32int c; c = xkb_keysym_to_utf32(ks); if(c >= ' ' && c != 0x7f) return c; if(ks >= 0xff00 && ks <= 0xffff) return Kspec + (ks - 0xff00); return c; } static u32int mget(void) { u32int m; m = 0; if(xkbst == nil) return 0; if(xkb_state_mod_name_is_active(xkbst, XKB_MOD_NAME_SHIFT, XKB_STATE_MODS_EFFECTIVE) > 0) m |= Mshift; if(xkb_state_mod_name_is_active(xkbst, XKB_MOD_NAME_CTRL, XKB_STATE_MODS_EFFECTIVE) > 0) m |= Mctrl; if(xkb_state_mod_name_is_active(xkbst, XKB_MOD_NAME_ALT, XKB_STATE_MODS_EFFECTIVE) > 0) m |= Malt; if(xkb_state_mod_name_is_active(xkbst, XKB_MOD_NAME_LOGO, XKB_STATE_MODS_EFFECTIVE) > 0) m |= Msuper; return m; } static void putresult(Keyres *res) { char commit[Maxutf], preedit[Maxutf]; int plen; if(im == nil) return; stoutf(&res->commit, commit, sizeof commit); stoutf(&res->preedit, preedit, sizeof preedit); if(commit[0] != '\0') zwp_input_method_v2_commit_string(im, commit); plen = strlen(preedit); zwp_input_method_v2_set_preedit_string(im, preedit, plen, plen); zwp_input_method_v2_commit(im, imserial); } static void forwardkey(u32int time, u32int keycode, u32int state) { if(vk != nil) zwp_virtual_keyboard_v1_key(vk, time, keycode, state); } static void keypress(u32int time, u32int keycode) { xkb_keysym_t ks; u32int k, mod; Keyres res; int repeatable; if(xkbst == nil || keycode >= Wlkeymax){ forwardkey(time, keycode, WL_KEYBOARD_KEY_STATE_PRESSED); wlstatepress(&keys, keycode, 0, 0, nowms()); return; } ks = xkb_state_key_get_one_sym(xkbst, keycode + 8); k = kget(ks); if(k == 0){ forwardkey(time, keycode, WL_KEYBOARD_KEY_STATE_PRESSED); wlstatepress(&keys, keycode, 0, 0, nowms()); return; } mod = mget(); sendkey(k, mod, &res); putresult(&res); repeatable = keymap != nil && xkb_keymap_key_repeats(keymap, keycode + 8); wlstatepress(&keys, keycode, res.eaten, repeatable, nowms()); if(!res.eaten) forwardkey(time, keycode, WL_KEYBOARD_KEY_STATE_PRESSED); } static void keyrepeat(u32int time, u32int keycode) { xkb_keysym_t ks; u32int k; Keyres res; if(xkbst == nil) return; ks = xkb_state_key_get_one_sym(xkbst, keycode + 8); k = kget(ks); if(k == 0) return; sendkey(k, mget(), &res); putresult(&res); /* The original eaten press still owns its eventual physical release. */ if(!res.eaten){ forwardkey(time, keycode, WL_KEYBOARD_KEY_STATE_PRESSED); forwardkey(time, keycode, WL_KEYBOARD_KEY_STATE_RELEASED); } } static void kpress(u32int time, u32int keycode, u32int state) { if(state == WL_KEYBOARD_KEY_STATE_PRESSED){ keypress(time, keycode); return; } if(wlstaterelease(&keys, keycode)) forwardkey(time, keycode, state); } static void kg_keymap(void *data, struct zwp_input_method_keyboard_grab_v2 *g, u32int format, int32_t fd, u32int size) { char *s; (void)data; (void)g; wlstatecancelrepeat(&keys); if(format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1){ close(fd); return; } s = mmap(nil, size, PROT_READ, MAP_PRIVATE, fd, 0); if(s != MAP_FAILED){ if(xkbst != nil){ xkb_state_unref(xkbst); xkbst = nil; } if(keymap != nil) xkb_keymap_unref(keymap); keymap = xkb_keymap_new_from_string(xkb, s, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS); if(keymap != nil) xkbst = xkb_state_new(keymap); munmap(s, size); } if(vk != nil) zwp_virtual_keyboard_v1_keymap(vk, format, fd, size); close(fd); } static void kg_key(void *data, struct zwp_input_method_keyboard_grab_v2 *g, u32int serial, u32int time, u32int key, u32int state) { (void)data; (void)g; (void)serial; kpress(time, key, state); } static void kg_mods(void *data, struct zwp_input_method_keyboard_grab_v2 *g, u32int serial, u32int dep, u32int lat, u32int lck, u32int group) { (void)data; (void)g; (void)serial; if(xkbst != nil) xkb_state_update_mask(xkbst, dep, lat, lck, 0, 0, group); if(vk != nil) zwp_virtual_keyboard_v1_modifiers(vk, dep, lat, lck, group); } static void kg_repeat(void *data, struct zwp_input_method_keyboard_grab_v2 *g, int32_t rate, int32_t delay) { (void)data; (void)g; wlstaterepeatinfo(&keys, rate, delay); } static const struct zwp_input_method_keyboard_grab_v2_listener grab_listener = { .keymap = kg_keymap, .key = kg_key, .modifiers = kg_mods, .repeat_info = kg_repeat, }; static void im_activate(void *data, struct zwp_input_method_v2 *m) { (void)data; (void)m; pending = 1; newactivation = 1; } static void im_deactivate(void *data, struct zwp_input_method_v2 *m) { (void)data; (void)m; pending = 0; wlstateclear(&keys); } static void im_surrounding(void *data, struct zwp_input_method_v2 *m, const char *text, u32int cursor, u32int anchor) { (void)data; (void)m; (void)text; (void)cursor; (void)anchor; } static void im_textchange(void *data, struct zwp_input_method_v2 *m, u32int cause) { (void)data; (void)m; (void)cause; } static void im_content(void *data, struct zwp_input_method_v2 *m, u32int hint, u32int purpose) { (void)data; (void)m; (void)hint; (void)purpose; } static void endactivation(void) { wlstateclear(&keys); active = 0; releaseowner(); if(grab != nil){ zwp_input_method_keyboard_grab_v2_release(grab); grab = nil; } } static void beginactivation(void) { if(active) endactivation(); active = 1; owner = ownernew(); grab = zwp_input_method_v2_grab_keyboard(im); if(grab != nil) zwp_input_method_keyboard_grab_v2_add_listener(grab, &grab_listener, nil); } static void im_done(void *data, struct zwp_input_method_v2 *m) { (void)data; (void)m; imserial++; if(newactivation && pending) beginactivation(); else if(!pending && active) endactivation(); newactivation = 0; } static void im_unavail(void *data, struct zwp_input_method_v2 *m) { (void)data; (void)m; wlstateclear(&keys); running = 0; fprint(2, "strans: wayland: input-method unavailable\n"); } static const struct zwp_input_method_v2_listener im_listener = { .activate = im_activate, .deactivate = im_deactivate, .surrounding_text = im_surrounding, .text_change_cause = im_textchange, .content_type = im_content, .done = im_done, .unavailable = im_unavail, }; static void reg_global(void *data, struct wl_registry *r, u32int name, const char *iface, u32int version) { (void)data; if(strcmp(iface, wl_seat_interface.name) == 0 && seat == nil){ seatname = name; seat = wl_registry_bind(r, name, &wl_seat_interface, version < 5 ? version : 5); }else if(strcmp(iface, zwp_input_method_manager_v2_interface.name) == 0 && immgr == nil){ immgrname = name; immgr = wl_registry_bind(r, name, &zwp_input_method_manager_v2_interface, 1); }else if(strcmp(iface, zwp_virtual_keyboard_manager_v1_interface.name) == 0 && vkmgr == nil){ vkmgrname = name; vkmgr = wl_registry_bind(r, name, &zwp_virtual_keyboard_manager_v1_interface, 1); } } static void reg_remove(void *data, struct wl_registry *r, u32int name) { (void)data; (void)r; if(name == seatname || name == immgrname || name == vkmgrname) running = 0; } static const struct wl_registry_listener reg_listener = { .global = reg_global, .global_remove = reg_remove, }; static int dispatch(void) { struct pollfd pfd; u32int key; uvlong now; int flush, timeout, n; pfd.fd = wl_display_get_fd(dpy); while(running){ if(wl_display_dispatch_pending(dpy) < 0) return -1; if(!running) break; now = nowms(); if(wlstaterepeat(&keys, now, &key)) keyrepeat((u32int)now, key); while(wl_display_prepare_read(dpy) != 0) if(wl_display_dispatch_pending(dpy) < 0) return -1; flush = wl_display_flush(dpy); if(flush < 0 && errno != EAGAIN){ wl_display_cancel_read(dpy); return -1; } pfd.events = POLLIN; if(flush < 0) pfd.events |= POLLOUT; pfd.revents = 0; timeout = wlstatetimeout(&keys, nowms()); n = poll(&pfd, 1, timeout); if(n < 0){ wl_display_cancel_read(dpy); if(errno == EINTR) continue; return -1; } if(n == 0){ wl_display_cancel_read(dpy); continue; } if(pfd.revents & POLLIN){ if(wl_display_read_events(dpy) < 0) return -1; }else wl_display_cancel_read(dpy); if(pfd.revents & (POLLERR | POLLHUP | POLLNVAL)) return -1; if(pfd.revents & POLLOUT) wl_display_flush(dpy); } return 0; } static void resetconnection(void) { dpy = nil; reg = nil; seat = nil; immgr = nil; vkmgr = nil; im = nil; grab = nil; vk = nil; xkb = nil; keymap = nil; xkbst = nil; active = 0; pending = 0; newactivation = 0; running = 1; imserial = 0; seatname = 0; immgrname = 0; vkmgrname = 0; owner = 0; wlstateinit(&keys); } static void cleanup(void) { running = 0; wlstateclear(&keys); releaseowner(); active = 0; if(xkbst != nil) xkb_state_unref(xkbst); if(keymap != nil) xkb_keymap_unref(keymap); if(xkb != nil) xkb_context_unref(xkb); if(grab != nil) wl_proxy_destroy((struct wl_proxy*)grab); if(vk != nil) wl_proxy_destroy((struct wl_proxy*)vk); if(im != nil) wl_proxy_destroy((struct wl_proxy*)im); if(vkmgr != nil) wl_proxy_destroy((struct wl_proxy*)vkmgr); if(immgr != nil) wl_proxy_destroy((struct wl_proxy*)immgr); if(seat != nil) wl_proxy_destroy((struct wl_proxy*)seat); if(reg != nil) wl_registry_destroy(reg); if(dpy != nil){ wl_display_disconnect(dpy); dpy = nil; } } static void connectonce(void) { resetconnection(); dpy = wl_display_connect(nil); if(dpy == nil) return; xkb = xkb_context_new(XKB_CONTEXT_NO_FLAGS); if(xkb == nil) return; reg = wl_display_get_registry(dpy); if(reg == nil) return; wl_registry_add_listener(reg, ®_listener, nil); if(wl_display_roundtrip(dpy) < 0) return; if(!running) return; if(seat == nil || immgr == nil || vkmgr == nil){ fprint(2, "strans: wayland: compositor missing input-method-v2\n"); return; } im = zwp_input_method_manager_v2_get_input_method(immgr, seat); vk = zwp_virtual_keyboard_manager_v1_create_virtual_keyboard(vkmgr, seat); if(im == nil || vk == nil) return; zwp_input_method_v2_add_listener(im, &im_listener, nil); dispatch(); } void waylandthread(void *_) { (void)_; threadsetname("wayland"); replyc = chancreate(sizeof(Keyres), 0); for(;;){ connectonce(); cleanup(); poll(nil, 0, 1000); } }