fix: repair Wayland key lifecycle
This commit is contained in:
@@ -5,9 +5,9 @@ LIBS = -lthread -lbio
|
|||||||
|
|
||||||
PROG = unit_test
|
PROG = unit_test
|
||||||
TESTSRC = unit_test.c test_util.c str_test.c hash_test.c trie_test.c \
|
TESTSRC = unit_test.c test_util.c str_test.c hash_test.c trie_test.c \
|
||||||
ko_test.c vi_test.c engine_test.c dict_test.c ipc_test.c
|
ko_test.c vi_test.c engine_test.c dict_test.c ipc_test.c wayland_test.c
|
||||||
TESTOBJ = $(TESTSRC:.c=.o)
|
TESTOBJ = $(TESTSRC:.c=.o)
|
||||||
PARENTSRC = str.c hash.c trie.c dict.c ko.c vi.c ipc.c
|
PARENTSRC = str.c hash.c trie.c dict.c ko.c vi.c ipc.c wayland_state.c
|
||||||
PARENTOBJ = $(PARENTSRC:%.c=unit_%.o)
|
PARENTOBJ = $(PARENTSRC:%.c=unit_%.o)
|
||||||
OBJS = $(TESTOBJ) $(PARENTOBJ)
|
OBJS = $(TESTOBJ) $(PARENTOBJ)
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,9 @@ void hmap_long_utf8_keys(struct ct*);
|
|||||||
void hmap_binary_keys_and_invalid_lengths(struct ct*);
|
void hmap_binary_keys_and_invalid_lengths(struct ct*);
|
||||||
void trie_exact_prefix_and_duplicate(struct ct*);
|
void trie_exact_prefix_and_duplicate(struct ct*);
|
||||||
void trie_optional_outputs_and_invalid_lengths(struct ct*);
|
void trie_optional_outputs_and_invalid_lengths(struct ct*);
|
||||||
|
void wayland_press_release_state(struct ct*);
|
||||||
|
void wayland_repeat_state(struct ct*);
|
||||||
|
void wayland_repeat_cancellation(struct ct*);
|
||||||
void production_maps_load(struct ct*);
|
void production_maps_load(struct ct*);
|
||||||
void transmap_states(struct ct*);
|
void transmap_states(struct ct*);
|
||||||
void korean_sequences(struct ct*);
|
void korean_sequences(struct ct*);
|
||||||
|
|||||||
@@ -72,6 +72,9 @@ static const struct ct_test tests[] = {
|
|||||||
{ "hmap/binary-invalid-lengths", hmap_binary_keys_and_invalid_lengths },
|
{ "hmap/binary-invalid-lengths", hmap_binary_keys_and_invalid_lengths },
|
||||||
{ "trie/exact-prefix-duplicate", trie_exact_prefix_and_duplicate },
|
{ "trie/exact-prefix-duplicate", trie_exact_prefix_and_duplicate },
|
||||||
{ "trie/optional-invalid-lengths", trie_optional_outputs_and_invalid_lengths },
|
{ "trie/optional-invalid-lengths", trie_optional_outputs_and_invalid_lengths },
|
||||||
|
{ "wayland/press-release", wayland_press_release_state },
|
||||||
|
{ "wayland/repeat", wayland_repeat_state },
|
||||||
|
{ "wayland/repeat-cancellation", wayland_repeat_cancellation },
|
||||||
{ "map/production-lifecycle", production_maps_load },
|
{ "map/production-lifecycle", production_maps_load },
|
||||||
{ "transmap/states", transmap_states },
|
{ "transmap/states", transmap_states },
|
||||||
{ "hangul/sequences", korean_sequences },
|
{ "hangul/sequences", korean_sequences },
|
||||||
|
|||||||
67
tests/wayland_test.c
Normal file
67
tests/wayland_test.c
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
#include "test.h"
|
||||||
|
#include "../wayland_state.h"
|
||||||
|
|
||||||
|
void
|
||||||
|
wayland_press_release_state(struct ct *t)
|
||||||
|
{
|
||||||
|
Wlstate s;
|
||||||
|
|
||||||
|
wlstateinit(&s);
|
||||||
|
CT_CHECK(t, wlstatepress(&s, 30, 1, 0, 0));
|
||||||
|
CT_CHECK(t, !wlstaterelease(&s, 30));
|
||||||
|
CT_CHECK(t, wlstaterelease(&s, 30));
|
||||||
|
CT_CHECK(t, wlstatepress(&s, 31, 0, 0, 0));
|
||||||
|
CT_CHECK(t, wlstaterelease(&s, 31));
|
||||||
|
CT_CHECK(t, !wlstatepress(&s, Wlkeymax, 1, 0, 0));
|
||||||
|
CT_CHECK(t, wlstaterelease(&s, Wlkeymax));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
wayland_repeat_state(struct ct *t)
|
||||||
|
{
|
||||||
|
Wlstate s;
|
||||||
|
u32int key;
|
||||||
|
|
||||||
|
wlstateinit(&s);
|
||||||
|
wlstaterepeatinfo(&s, 25, 400);
|
||||||
|
CT_CHECK(t, wlstatepress(&s, 14, 1, 1, 100));
|
||||||
|
CT_EQ_INT(t, 400, wlstatetimeout(&s, 100));
|
||||||
|
CT_CHECK(t, !wlstaterepeat(&s, 499, &key));
|
||||||
|
CT_CHECK(t, wlstaterepeat(&s, 500, &key));
|
||||||
|
CT_EQ_UINT(t, 14, key);
|
||||||
|
CT_EQ_INT(t, 40, wlstatetimeout(&s, 500));
|
||||||
|
CT_CHECK(t, !wlstaterelease(&s, 14));
|
||||||
|
CT_EQ_INT(t, -1, wlstatetimeout(&s, 500));
|
||||||
|
|
||||||
|
CT_CHECK(t, wlstatepress(&s, 15, 1, 0, 600));
|
||||||
|
CT_EQ_INT(t, -1, wlstatetimeout(&s, 600));
|
||||||
|
CT_CHECK(t, !wlstaterelease(&s, 15));
|
||||||
|
CT_CHECK(t, wlstatepress(&s, 16, 0, 1, 600));
|
||||||
|
CT_EQ_INT(t, -1, wlstatetimeout(&s, 600));
|
||||||
|
CT_CHECK(t, wlstaterelease(&s, 16));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
wayland_repeat_cancellation(struct ct *t)
|
||||||
|
{
|
||||||
|
Wlstate s;
|
||||||
|
|
||||||
|
wlstateinit(&s);
|
||||||
|
wlstaterepeatinfo(&s, 20, 300);
|
||||||
|
CT_CHECK(t, wlstatepress(&s, 20, 1, 1, 0));
|
||||||
|
CT_CHECK(t, wlstatepress(&s, 19, 0, 0, 10));
|
||||||
|
CT_EQ_INT(t, -1, wlstatetimeout(&s, 10));
|
||||||
|
CT_CHECK(t, !wlstaterelease(&s, 20));
|
||||||
|
CT_CHECK(t, wlstaterelease(&s, 19));
|
||||||
|
CT_CHECK(t, wlstatepress(&s, 20, 1, 1, 0));
|
||||||
|
wlstaterepeatinfo(&s, 30, 200);
|
||||||
|
CT_EQ_INT(t, -1, wlstatetimeout(&s, 0));
|
||||||
|
CT_CHECK(t, !wlstaterelease(&s, 20));
|
||||||
|
CT_CHECK(t, wlstatepress(&s, 21, 1, 1, 0));
|
||||||
|
wlstatecancelrepeat(&s);
|
||||||
|
CT_EQ_INT(t, -1, wlstatetimeout(&s, 0));
|
||||||
|
CT_CHECK(t, !wlstaterelease(&s, 21));
|
||||||
|
CT_CHECK(t, wlstatepress(&s, 22, 1, 1, 0));
|
||||||
|
wlstateclear(&s);
|
||||||
|
CT_CHECK(t, wlstaterelease(&s, 22));
|
||||||
|
}
|
||||||
371
wayland.c
371
wayland.c
@@ -1,18 +1,20 @@
|
|||||||
#include "dat.h"
|
#include "dat.h"
|
||||||
#include "fn.h"
|
#include "fn.h"
|
||||||
|
#include "wayland_state.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <errno.h>
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
|
#include <string.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <unistd.h>
|
||||||
#include <wayland-client.h>
|
#include <wayland-client.h>
|
||||||
#include <xkbcommon/xkbcommon.h>
|
#include <xkbcommon/xkbcommon.h>
|
||||||
#include "input-method-unstable-v2-client-protocol.h"
|
#include "input-method-unstable-v2-client-protocol.h"
|
||||||
#include "virtual-keyboard-unstable-v1-client-protocol.h"
|
#include "virtual-keyboard-unstable-v1-client-protocol.h"
|
||||||
|
|
||||||
static struct wl_display *dpy;
|
static struct wl_display *dpy;
|
||||||
|
static struct wl_registry *reg;
|
||||||
static struct wl_seat *seat;
|
static struct wl_seat *seat;
|
||||||
static struct zwp_input_method_manager_v2 *immgr;
|
static struct zwp_input_method_manager_v2 *immgr;
|
||||||
static struct zwp_virtual_keyboard_manager_v1 *vkmgr;
|
static struct zwp_virtual_keyboard_manager_v1 *vkmgr;
|
||||||
@@ -24,9 +26,25 @@ static struct xkb_keymap *keymap;
|
|||||||
static struct xkb_state *xkbst;
|
static struct xkb_state *xkbst;
|
||||||
static int active;
|
static int active;
|
||||||
static int pending;
|
static int pending;
|
||||||
|
static int newactivation;
|
||||||
|
static int running;
|
||||||
static u32int imserial;
|
static u32int imserial;
|
||||||
|
static u32int seatname;
|
||||||
|
static u32int immgrname;
|
||||||
|
static u32int vkmgrname;
|
||||||
static Channel *replyc;
|
static Channel *replyc;
|
||||||
static uvlong owner;
|
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
|
static void
|
||||||
sendkey(u32int ks, u32int mod, Keyres *res)
|
sendkey(u32int ks, u32int mod, Keyres *res)
|
||||||
@@ -45,17 +63,20 @@ sendkey(u32int ks, u32int mod, Keyres *res)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
sendrelease(void)
|
releaseowner(void)
|
||||||
{
|
{
|
||||||
Keyreq kr;
|
Keyreq kr;
|
||||||
Keyres res;
|
Keyres res;
|
||||||
|
|
||||||
|
if(owner == 0)
|
||||||
|
return;
|
||||||
memset(&kr, 0, sizeof kr);
|
memset(&kr, 0, sizeof kr);
|
||||||
kr.owner = owner;
|
kr.owner = owner;
|
||||||
kr.op = Keyrelease;
|
kr.op = Keyrelease;
|
||||||
kr.reply = replyc;
|
kr.reply = replyc;
|
||||||
chansend(keyc, &kr);
|
chansend(keyc, &kr);
|
||||||
chanrecv(replyc, &res);
|
chanrecv(replyc, &res);
|
||||||
|
owner = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static u32int
|
static u32int
|
||||||
@@ -95,47 +116,101 @@ mget(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
kpress(uint32_t time, uint32_t keycode, uint32_t state)
|
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;
|
xkb_keysym_t ks;
|
||||||
u32int k, mod;
|
u32int k, mod;
|
||||||
Keyres res;
|
Keyres res;
|
||||||
char commit[Maxutf], preedit[Maxutf];
|
int repeatable;
|
||||||
int plen;
|
|
||||||
|
|
||||||
if(state != WL_KEYBOARD_KEY_STATE_PRESSED || xkbst == nil){
|
if(xkbst == nil || keycode >= Wlkeymax){
|
||||||
zwp_virtual_keyboard_v1_key(vk, time, keycode, state);
|
forwardkey(time, keycode, WL_KEYBOARD_KEY_STATE_PRESSED);
|
||||||
|
wlstatepress(&keys, keycode, 0, 0, nowms());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ks = xkb_state_key_get_one_sym(xkbst, keycode + 8);
|
ks = xkb_state_key_get_one_sym(xkbst, keycode + 8);
|
||||||
k = kget(ks);
|
k = kget(ks);
|
||||||
if(k == 0){
|
if(k == 0){
|
||||||
zwp_virtual_keyboard_v1_key(vk, time, keycode, state);
|
forwardkey(time, keycode, WL_KEYBOARD_KEY_STATE_PRESSED);
|
||||||
|
wlstatepress(&keys, keycode, 0, 0, nowms());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mod = mget();
|
mod = mget();
|
||||||
sendkey(k, mod, &res);
|
sendkey(k, mod, &res);
|
||||||
stoutf(&res.commit, commit, sizeof commit);
|
putresult(&res);
|
||||||
stoutf(&res.preedit, preedit, sizeof preedit);
|
repeatable = keymap != nil &&
|
||||||
if(commit[0] != '\0'){
|
xkb_keymap_key_repeats(keymap, keycode + 8);
|
||||||
zwp_input_method_v2_commit_string(im, commit);
|
wlstatepress(&keys, keycode, res.eaten, repeatable, nowms());
|
||||||
zwp_input_method_v2_commit(im, imserial);
|
|
||||||
}
|
|
||||||
plen = strlen(preedit);
|
|
||||||
zwp_input_method_v2_set_preedit_string(im, preedit, plen, plen);
|
|
||||||
zwp_input_method_v2_commit(im, imserial);
|
|
||||||
if(!res.eaten)
|
if(!res.eaten)
|
||||||
zwp_virtual_keyboard_v1_key(vk, time, keycode, state);
|
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
|
static void
|
||||||
kg_keymap(void *data, struct zwp_input_method_keyboard_grab_v2 *g,
|
kg_keymap(void *data, struct zwp_input_method_keyboard_grab_v2 *g,
|
||||||
uint32_t format, int32_t fd, uint32_t size)
|
u32int format, int32_t fd, u32int size)
|
||||||
{
|
{
|
||||||
char *s;
|
char *s;
|
||||||
|
|
||||||
(void)data;
|
(void)data;
|
||||||
(void)g;
|
(void)g;
|
||||||
|
wlstatecancelrepeat(&keys);
|
||||||
if(format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1){
|
if(format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1){
|
||||||
close(fd);
|
close(fd);
|
||||||
return;
|
return;
|
||||||
@@ -155,13 +230,14 @@ kg_keymap(void *data, struct zwp_input_method_keyboard_grab_v2 *g,
|
|||||||
xkbst = xkb_state_new(keymap);
|
xkbst = xkb_state_new(keymap);
|
||||||
munmap(s, size);
|
munmap(s, size);
|
||||||
}
|
}
|
||||||
zwp_virtual_keyboard_v1_keymap(vk, format, fd, size);
|
if(vk != nil)
|
||||||
|
zwp_virtual_keyboard_v1_keymap(vk, format, fd, size);
|
||||||
close(fd);
|
close(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
kg_key(void *data, struct zwp_input_method_keyboard_grab_v2 *g,
|
kg_key(void *data, struct zwp_input_method_keyboard_grab_v2 *g,
|
||||||
uint32_t serial, uint32_t time, uint32_t key, uint32_t state)
|
u32int serial, u32int time, u32int key, u32int state)
|
||||||
{
|
{
|
||||||
(void)data;
|
(void)data;
|
||||||
(void)g;
|
(void)g;
|
||||||
@@ -171,15 +247,15 @@ kg_key(void *data, struct zwp_input_method_keyboard_grab_v2 *g,
|
|||||||
|
|
||||||
static void
|
static void
|
||||||
kg_mods(void *data, struct zwp_input_method_keyboard_grab_v2 *g,
|
kg_mods(void *data, struct zwp_input_method_keyboard_grab_v2 *g,
|
||||||
uint32_t serial, uint32_t dep, uint32_t lat, uint32_t lck,
|
u32int serial, u32int dep, u32int lat, u32int lck, u32int group)
|
||||||
uint32_t group)
|
|
||||||
{
|
{
|
||||||
(void)data;
|
(void)data;
|
||||||
(void)g;
|
(void)g;
|
||||||
(void)serial;
|
(void)serial;
|
||||||
if(xkbst != nil)
|
if(xkbst != nil)
|
||||||
xkb_state_update_mask(xkbst, dep, lat, lck, 0, 0, group);
|
xkb_state_update_mask(xkbst, dep, lat, lck, 0, 0, group);
|
||||||
zwp_virtual_keyboard_v1_modifiers(vk, dep, lat, lck, group);
|
if(vk != nil)
|
||||||
|
zwp_virtual_keyboard_v1_modifiers(vk, dep, lat, lck, group);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -188,8 +264,7 @@ kg_repeat(void *data, struct zwp_input_method_keyboard_grab_v2 *g,
|
|||||||
{
|
{
|
||||||
(void)data;
|
(void)data;
|
||||||
(void)g;
|
(void)g;
|
||||||
(void)rate;
|
wlstaterepeatinfo(&keys, rate, delay);
|
||||||
(void)delay;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct zwp_input_method_keyboard_grab_v2_listener
|
static const struct zwp_input_method_keyboard_grab_v2_listener
|
||||||
@@ -206,6 +281,7 @@ im_activate(void *data, struct zwp_input_method_v2 *m)
|
|||||||
(void)data;
|
(void)data;
|
||||||
(void)m;
|
(void)m;
|
||||||
pending = 1;
|
pending = 1;
|
||||||
|
newactivation = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -214,11 +290,12 @@ im_deactivate(void *data, struct zwp_input_method_v2 *m)
|
|||||||
(void)data;
|
(void)data;
|
||||||
(void)m;
|
(void)m;
|
||||||
pending = 0;
|
pending = 0;
|
||||||
|
wlstateclear(&keys);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
im_surrounding(void *data, struct zwp_input_method_v2 *m,
|
im_surrounding(void *data, struct zwp_input_method_v2 *m,
|
||||||
const char *text, uint32_t cursor, uint32_t anchor)
|
const char *text, u32int cursor, u32int anchor)
|
||||||
{
|
{
|
||||||
(void)data;
|
(void)data;
|
||||||
(void)m;
|
(void)m;
|
||||||
@@ -228,7 +305,7 @@ im_surrounding(void *data, struct zwp_input_method_v2 *m,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
im_textchange(void *data, struct zwp_input_method_v2 *m, uint32_t cause)
|
im_textchange(void *data, struct zwp_input_method_v2 *m, u32int cause)
|
||||||
{
|
{
|
||||||
(void)data;
|
(void)data;
|
||||||
(void)m;
|
(void)m;
|
||||||
@@ -237,7 +314,7 @@ im_textchange(void *data, struct zwp_input_method_v2 *m, uint32_t cause)
|
|||||||
|
|
||||||
static void
|
static void
|
||||||
im_content(void *data, struct zwp_input_method_v2 *m,
|
im_content(void *data, struct zwp_input_method_v2 *m,
|
||||||
uint32_t hint, uint32_t purpose)
|
u32int hint, u32int purpose)
|
||||||
{
|
{
|
||||||
(void)data;
|
(void)data;
|
||||||
(void)m;
|
(void)m;
|
||||||
@@ -245,28 +322,42 @@ im_content(void *data, struct zwp_input_method_v2 *m,
|
|||||||
(void)purpose;
|
(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
|
static void
|
||||||
im_done(void *data, struct zwp_input_method_v2 *m)
|
im_done(void *data, struct zwp_input_method_v2 *m)
|
||||||
{
|
{
|
||||||
(void)data;
|
(void)data;
|
||||||
(void)m;
|
(void)m;
|
||||||
imserial++;
|
imserial++;
|
||||||
if(pending && !active){
|
if(newactivation && pending)
|
||||||
active = 1;
|
beginactivation();
|
||||||
owner = ownernew();
|
else if(!pending && active)
|
||||||
grab = zwp_input_method_v2_grab_keyboard(im);
|
endactivation();
|
||||||
if(grab != nil)
|
newactivation = 0;
|
||||||
zwp_input_method_keyboard_grab_v2_add_listener(grab,
|
|
||||||
&grab_listener, nil);
|
|
||||||
}else if(!pending && active){
|
|
||||||
active = 0;
|
|
||||||
sendrelease();
|
|
||||||
owner = 0;
|
|
||||||
if(grab != nil){
|
|
||||||
zwp_input_method_keyboard_grab_v2_release(grab);
|
|
||||||
grab = nil;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -274,6 +365,8 @@ im_unavail(void *data, struct zwp_input_method_v2 *m)
|
|||||||
{
|
{
|
||||||
(void)data;
|
(void)data;
|
||||||
(void)m;
|
(void)m;
|
||||||
|
wlstateclear(&keys);
|
||||||
|
running = 0;
|
||||||
fprint(2, "strans: wayland: input-method unavailable\n");
|
fprint(2, "strans: wayland: input-method unavailable\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -288,28 +381,34 @@ static const struct zwp_input_method_v2_listener im_listener = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static void
|
static void
|
||||||
reg_global(void *data, struct wl_registry *r, uint32_t name,
|
reg_global(void *data, struct wl_registry *r, u32int name,
|
||||||
const char *iface, uint32_t version)
|
const char *iface, u32int version)
|
||||||
{
|
{
|
||||||
(void)data;
|
(void)data;
|
||||||
if(strcmp(iface, wl_seat_interface.name) == 0)
|
if(strcmp(iface, wl_seat_interface.name) == 0 && seat == nil){
|
||||||
|
seatname = name;
|
||||||
seat = wl_registry_bind(r, name, &wl_seat_interface,
|
seat = wl_registry_bind(r, name, &wl_seat_interface,
|
||||||
version < 5 ? version : 5);
|
version < 5 ? version : 5);
|
||||||
else if(strcmp(iface, zwp_input_method_manager_v2_interface.name) == 0)
|
}else if(strcmp(iface,
|
||||||
|
zwp_input_method_manager_v2_interface.name) == 0 && immgr == nil){
|
||||||
|
immgrname = name;
|
||||||
immgr = wl_registry_bind(r, name,
|
immgr = wl_registry_bind(r, name,
|
||||||
&zwp_input_method_manager_v2_interface, 1);
|
&zwp_input_method_manager_v2_interface, 1);
|
||||||
else if(strcmp(iface,
|
}else if(strcmp(iface,
|
||||||
zwp_virtual_keyboard_manager_v1_interface.name) == 0)
|
zwp_virtual_keyboard_manager_v1_interface.name) == 0 && vkmgr == nil){
|
||||||
|
vkmgrname = name;
|
||||||
vkmgr = wl_registry_bind(r, name,
|
vkmgr = wl_registry_bind(r, name,
|
||||||
&zwp_virtual_keyboard_manager_v1_interface, 1);
|
&zwp_virtual_keyboard_manager_v1_interface, 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
reg_remove(void *data, struct wl_registry *r, uint32_t name)
|
reg_remove(void *data, struct wl_registry *r, u32int name)
|
||||||
{
|
{
|
||||||
(void)data;
|
(void)data;
|
||||||
(void)r;
|
(void)r;
|
||||||
(void)name;
|
if(name == seatname || name == immgrname || name == vkmgrname)
|
||||||
|
running = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct wl_registry_listener reg_listener = {
|
static const struct wl_registry_listener reg_listener = {
|
||||||
@@ -317,37 +416,159 @@ static const struct wl_registry_listener reg_listener = {
|
|||||||
.global_remove = reg_remove,
|
.global_remove = reg_remove,
|
||||||
};
|
};
|
||||||
|
|
||||||
void
|
static int
|
||||||
waylandthread(void *_)
|
dispatch(void)
|
||||||
{
|
{
|
||||||
struct wl_registry *reg;
|
struct pollfd pfd;
|
||||||
|
u32int key;
|
||||||
|
uvlong now;
|
||||||
|
int flush, timeout, n;
|
||||||
|
|
||||||
(void)_;
|
pfd.fd = wl_display_get_fd(dpy);
|
||||||
threadsetname("wayland");
|
while(running){
|
||||||
while((dpy = wl_display_connect(nil)) == nil)
|
if(wl_display_dispatch_pending(dpy) < 0)
|
||||||
poll(nil, 0, 1000);
|
return -1;
|
||||||
xkb = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
if(!running)
|
||||||
if(xkb == nil){
|
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);
|
wl_display_disconnect(dpy);
|
||||||
dpy = nil;
|
dpy = nil;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
reg = wl_display_get_registry(dpy);
|
||||||
|
if(reg == nil)
|
||||||
|
return;
|
||||||
wl_registry_add_listener(reg, ®_listener, nil);
|
wl_registry_add_listener(reg, ®_listener, nil);
|
||||||
wl_display_roundtrip(dpy);
|
if(wl_display_roundtrip(dpy) < 0)
|
||||||
|
return;
|
||||||
|
if(!running)
|
||||||
|
return;
|
||||||
if(seat == nil || immgr == nil || vkmgr == nil){
|
if(seat == nil || immgr == nil || vkmgr == nil){
|
||||||
fprint(2,
|
fprint(2,
|
||||||
"strans: wayland: compositor missing input-method-v2\n");
|
"strans: wayland: compositor missing input-method-v2\n");
|
||||||
wl_display_disconnect(dpy);
|
|
||||||
dpy = nil;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
im = zwp_input_method_manager_v2_get_input_method(immgr, seat);
|
im = zwp_input_method_manager_v2_get_input_method(immgr, seat);
|
||||||
zwp_input_method_v2_add_listener(im, &im_listener, nil);
|
|
||||||
vk = zwp_virtual_keyboard_manager_v1_create_virtual_keyboard(vkmgr, seat);
|
vk = zwp_virtual_keyboard_manager_v1_create_virtual_keyboard(vkmgr, seat);
|
||||||
replyc = chancreate(sizeof(Keyres), 0);
|
if(im == nil || vk == nil)
|
||||||
while(wl_display_dispatch(dpy) != -1)
|
return;
|
||||||
;
|
zwp_input_method_v2_add_listener(im, &im_listener, nil);
|
||||||
chanfree(replyc);
|
dispatch();
|
||||||
replyc = nil;
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
waylandthread(void *_)
|
||||||
|
{
|
||||||
|
(void)_;
|
||||||
|
threadsetname("wayland");
|
||||||
|
replyc = chancreate(sizeof(Keyres), 0);
|
||||||
|
for(;;){
|
||||||
|
connectonce();
|
||||||
|
cleanup();
|
||||||
|
poll(nil, 0, 1000);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
107
wayland_state.c
Normal file
107
wayland_state.c
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
#include "wayland_state.h"
|
||||||
|
|
||||||
|
#include <limits.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
enum {
|
||||||
|
Downforward = 1,
|
||||||
|
Downeaten = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
wlstateinit(Wlstate *s)
|
||||||
|
{
|
||||||
|
memset(s, 0, sizeof *s);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
wlstatecancelrepeat(Wlstate *s)
|
||||||
|
{
|
||||||
|
s->repeating = 0;
|
||||||
|
s->repeatkey = 0;
|
||||||
|
s->repeatat = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
wlstateclear(Wlstate *s)
|
||||||
|
{
|
||||||
|
memset(s->down, 0, sizeof s->down);
|
||||||
|
wlstatecancelrepeat(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
wlstaterepeatinfo(Wlstate *s, int rate, int delay)
|
||||||
|
{
|
||||||
|
/* Settings changes end the old schedule; a later press starts a new one. */
|
||||||
|
wlstatecancelrepeat(s);
|
||||||
|
s->rate = rate > 0 ? rate : 0;
|
||||||
|
s->delay = delay > 0 ? delay : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
wlstatepress(Wlstate *s, uint32_t key, int eaten, int repeatable,
|
||||||
|
uint64_t now)
|
||||||
|
{
|
||||||
|
uint64_t delay;
|
||||||
|
|
||||||
|
if(key >= Wlkeymax)
|
||||||
|
return 0;
|
||||||
|
s->down[key] = eaten ? Downeaten : Downforward;
|
||||||
|
wlstatecancelrepeat(s);
|
||||||
|
if(eaten && repeatable && s->rate > 0){
|
||||||
|
delay = s->delay;
|
||||||
|
if(UINT64_MAX - now < delay)
|
||||||
|
s->repeatat = UINT64_MAX;
|
||||||
|
else
|
||||||
|
s->repeatat = now + delay;
|
||||||
|
s->repeatkey = key;
|
||||||
|
s->repeating = 1;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
wlstaterelease(Wlstate *s, uint32_t key)
|
||||||
|
{
|
||||||
|
int forward;
|
||||||
|
|
||||||
|
if(key >= Wlkeymax)
|
||||||
|
return 1;
|
||||||
|
forward = s->down[key] != Downeaten;
|
||||||
|
s->down[key] = 0;
|
||||||
|
if(s->repeating && s->repeatkey == key)
|
||||||
|
wlstatecancelrepeat(s);
|
||||||
|
return forward;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
wlstatetimeout(Wlstate *s, uint64_t now)
|
||||||
|
{
|
||||||
|
uint64_t d;
|
||||||
|
|
||||||
|
if(!s->repeating)
|
||||||
|
return -1;
|
||||||
|
if(now >= s->repeatat)
|
||||||
|
return 0;
|
||||||
|
d = s->repeatat - now;
|
||||||
|
return d > INT_MAX ? INT_MAX : (int)d;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
wlstaterepeat(Wlstate *s, uint64_t now, uint32_t *key)
|
||||||
|
{
|
||||||
|
uint64_t step;
|
||||||
|
|
||||||
|
if(!s->repeating || now < s->repeatat)
|
||||||
|
return 0;
|
||||||
|
if(key != 0)
|
||||||
|
*key = s->repeatkey;
|
||||||
|
step = 1000 / s->rate;
|
||||||
|
if(step == 0)
|
||||||
|
step = 1;
|
||||||
|
if(UINT64_MAX - now < step)
|
||||||
|
s->repeatat = UINT64_MAX;
|
||||||
|
else
|
||||||
|
s->repeatat = now + step;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
31
wayland_state.h
Normal file
31
wayland_state.h
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
#ifndef WAYLAND_STATE_H
|
||||||
|
#define WAYLAND_STATE_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
enum {
|
||||||
|
/* Wayland key codes are Linux input-event codes. */
|
||||||
|
Wlkeymax = 0x300,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct Wlstate Wlstate;
|
||||||
|
struct Wlstate
|
||||||
|
{
|
||||||
|
unsigned char down[Wlkeymax];
|
||||||
|
uint32_t repeatkey;
|
||||||
|
uint64_t repeatat;
|
||||||
|
int rate;
|
||||||
|
int delay;
|
||||||
|
int repeating;
|
||||||
|
};
|
||||||
|
|
||||||
|
void wlstateinit(Wlstate*);
|
||||||
|
int wlstatepress(Wlstate*, uint32_t, int, int, uint64_t);
|
||||||
|
int wlstaterelease(Wlstate*, uint32_t);
|
||||||
|
void wlstaterepeatinfo(Wlstate*, int, int);
|
||||||
|
void wlstatecancelrepeat(Wlstate*);
|
||||||
|
void wlstateclear(Wlstate*);
|
||||||
|
int wlstatetimeout(Wlstate*, uint64_t);
|
||||||
|
int wlstaterepeat(Wlstate*, uint64_t, uint32_t*);
|
||||||
|
|
||||||
|
#endif
|
||||||
Reference in New Issue
Block a user