add wayland support
This commit is contained in:
350
wayland.c
Normal file
350
wayland.c
Normal file
@@ -0,0 +1,350 @@
|
||||
#include "dat.h"
|
||||
#include "fn.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
#include <wayland-client.h>
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
#include "input-method-unstable-v2-client-protocol.h"
|
||||
#include "virtual-keyboard-unstable-v1-client-protocol.h"
|
||||
|
||||
static struct wl_display *dpy;
|
||||
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 u32int imserial;
|
||||
|
||||
static void
|
||||
sendkey(u32int ks, u32int mod, char *com, int csz, char *pre, int psz,
|
||||
int *eaten)
|
||||
{
|
||||
Keyreq kr;
|
||||
int p[2];
|
||||
uchar hdr[2];
|
||||
uchar pl;
|
||||
int n;
|
||||
|
||||
*eaten = 0;
|
||||
com[0] = '\0';
|
||||
pre[0] = '\0';
|
||||
if(pipe(p) < 0)
|
||||
return;
|
||||
kr.fd = p[1];
|
||||
kr.ks = ks;
|
||||
kr.mod = mod;
|
||||
kr.want = 1;
|
||||
chansend(keyc, &kr);
|
||||
if(read(p[0], hdr, 2) != 2)
|
||||
goto out;
|
||||
*eaten = hdr[0];
|
||||
n = hdr[1];
|
||||
if(n > 0 && n < csz){
|
||||
if(read(p[0], com, n) != n)
|
||||
goto out;
|
||||
com[n] = '\0';
|
||||
}
|
||||
if(read(p[0], &pl, 1) != 1)
|
||||
goto out;
|
||||
if(pl > 0 && pl < psz){
|
||||
if(read(p[0], pre, pl) != pl)
|
||||
goto out;
|
||||
pre[pl] = '\0';
|
||||
}
|
||||
out:
|
||||
close(p[0]);
|
||||
close(p[1]);
|
||||
}
|
||||
|
||||
static void
|
||||
sendreset(void)
|
||||
{
|
||||
char com[64], pre[256];
|
||||
int eaten;
|
||||
|
||||
sendkey(Kesc, 0, com, sizeof(com), pre, sizeof(pre), &eaten);
|
||||
}
|
||||
|
||||
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
|
||||
kpress(uint32_t time, uint32_t keycode, uint32_t state)
|
||||
{
|
||||
xkb_keysym_t ks;
|
||||
u32int k, mod;
|
||||
char com[64], pre[256];
|
||||
int eaten, plen;
|
||||
|
||||
if(state != WL_KEYBOARD_KEY_STATE_PRESSED || xkbst == nil){
|
||||
zwp_virtual_keyboard_v1_key(vk, time, keycode, state);
|
||||
return;
|
||||
}
|
||||
ks = xkb_state_key_get_one_sym(xkbst, keycode + 8);
|
||||
k = ks >= 0xff00 ? Kspec + (ks - 0xff00) : xkb_keysym_to_utf32(ks);
|
||||
if(k == 0){
|
||||
zwp_virtual_keyboard_v1_key(vk, time, keycode, state);
|
||||
return;
|
||||
}
|
||||
mod = mget();
|
||||
sendkey(k, mod, com, sizeof(com), pre, sizeof(pre), &eaten);
|
||||
if(com[0] != '\0')
|
||||
zwp_input_method_v2_commit_string(im, com);
|
||||
plen = strlen(pre);
|
||||
zwp_input_method_v2_set_preedit_string(im, pre, plen, plen);
|
||||
zwp_input_method_v2_commit(im, imserial);
|
||||
if(!eaten)
|
||||
zwp_virtual_keyboard_v1_key(vk, time, keycode, state);
|
||||
}
|
||||
|
||||
static void
|
||||
kg_keymap(void *data, struct zwp_input_method_keyboard_grab_v2 *g,
|
||||
uint32_t format, int32_t fd, uint32_t size)
|
||||
{
|
||||
char *s;
|
||||
|
||||
(void)data;
|
||||
(void)g;
|
||||
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);
|
||||
}
|
||||
zwp_virtual_keyboard_v1_keymap(vk, format, fd, size);
|
||||
}
|
||||
|
||||
static void
|
||||
kg_key(void *data, struct zwp_input_method_keyboard_grab_v2 *g,
|
||||
uint32_t serial, uint32_t time, uint32_t key, uint32_t 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,
|
||||
uint32_t serial, uint32_t dep, uint32_t lat, uint32_t lck,
|
||||
uint32_t group)
|
||||
{
|
||||
(void)data;
|
||||
(void)g;
|
||||
(void)serial;
|
||||
if(xkbst != nil)
|
||||
xkb_state_update_mask(xkbst, dep, lat, lck, 0, 0, group);
|
||||
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;
|
||||
(void)rate;
|
||||
(void)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;
|
||||
}
|
||||
|
||||
static void
|
||||
im_deactivate(void *data, struct zwp_input_method_v2 *m)
|
||||
{
|
||||
(void)data;
|
||||
(void)m;
|
||||
pending = 0;
|
||||
if(active){
|
||||
active = 0;
|
||||
sendreset();
|
||||
if(grab != nil){
|
||||
zwp_input_method_keyboard_grab_v2_release(grab);
|
||||
grab = nil;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
im_surrounding(void *data, struct zwp_input_method_v2 *m,
|
||||
const char *text, uint32_t cursor, uint32_t anchor)
|
||||
{
|
||||
(void)data;
|
||||
(void)m;
|
||||
(void)text;
|
||||
(void)cursor;
|
||||
(void)anchor;
|
||||
}
|
||||
|
||||
static void
|
||||
im_textchange(void *data, struct zwp_input_method_v2 *m, uint32_t cause)
|
||||
{
|
||||
(void)data;
|
||||
(void)m;
|
||||
(void)cause;
|
||||
}
|
||||
|
||||
static void
|
||||
im_content(void *data, struct zwp_input_method_v2 *m,
|
||||
uint32_t hint, uint32_t purpose)
|
||||
{
|
||||
(void)data;
|
||||
(void)m;
|
||||
(void)hint;
|
||||
(void)purpose;
|
||||
}
|
||||
|
||||
static void
|
||||
im_done(void *data, struct zwp_input_method_v2 *m)
|
||||
{
|
||||
(void)data;
|
||||
(void)m;
|
||||
imserial++;
|
||||
if(pending && !active){
|
||||
active = 1;
|
||||
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_unavail(void *data, struct zwp_input_method_v2 *m)
|
||||
{
|
||||
(void)data;
|
||||
(void)m;
|
||||
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, uint32_t name,
|
||||
const char *iface, uint32_t version)
|
||||
{
|
||||
(void)data;
|
||||
(void)version;
|
||||
if(strcmp(iface, wl_seat_interface.name) == 0)
|
||||
seat = wl_registry_bind(r, name, &wl_seat_interface, 5);
|
||||
else if(strcmp(iface, zwp_input_method_manager_v2_interface.name) == 0)
|
||||
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 = wl_registry_bind(r, name,
|
||||
&zwp_virtual_keyboard_manager_v1_interface, 1);
|
||||
}
|
||||
|
||||
static void
|
||||
reg_remove(void *data, struct wl_registry *r, uint32_t name)
|
||||
{
|
||||
(void)data;
|
||||
(void)r;
|
||||
(void)name;
|
||||
}
|
||||
|
||||
static const struct wl_registry_listener reg_listener = {
|
||||
.global = reg_global,
|
||||
.global_remove = reg_remove,
|
||||
};
|
||||
|
||||
void
|
||||
waylandthread(void *_)
|
||||
{
|
||||
struct wl_registry *reg;
|
||||
|
||||
(void)_;
|
||||
threadsetname("wayland");
|
||||
dpy = wl_display_connect(nil);
|
||||
if(dpy == nil){
|
||||
fprint(2, "strans: wayland: cannot connect to display\n");
|
||||
return;
|
||||
}
|
||||
xkb = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
||||
if(xkb == nil){
|
||||
wl_display_disconnect(dpy);
|
||||
dpy = nil;
|
||||
return;
|
||||
}
|
||||
reg = wl_display_get_registry(dpy);
|
||||
wl_registry_add_listener(reg, ®_listener, nil);
|
||||
wl_display_roundtrip(dpy);
|
||||
if(seat == nil || immgr == nil || vkmgr == nil){
|
||||
fprint(2,
|
||||
"strans: wayland: compositor missing input-method-v2\n");
|
||||
wl_display_disconnect(dpy);
|
||||
dpy = nil;
|
||||
return;
|
||||
}
|
||||
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);
|
||||
while(wl_display_dispatch(dpy) != -1)
|
||||
;
|
||||
}
|
||||
Reference in New Issue
Block a user