Files
diya-shell/src/input.c

287 lines
8.3 KiB
C

#include "input.h"
#include <wayland-client-protocol.h>
#include <gdk/wayland/gdkwayland.h>
#include <assert.h>
#include <xkbcommon/xkbcommon.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/mman.h>
#include <gio/gio.h>
#include "wlr-foreign-toplevel-management-unstable-v1.h"
#include "virtual-keyboard-unstable-v1.h"
#include "wayland.h"
struct _DiyaInput
{
DiyaShellObject parent;
gchar *name;
struct wl_keyboard *keyboard;
struct wl_seat *seat;
struct xkb_state *xkb_state;
struct xkb_context *xkb_context;
struct xkb_keymap *xkb_keymap;
};
G_DEFINE_FINAL_TYPE(DiyaInput, diya_input, DIYA_TYPE_SHELL_OBJECT)
static void diya_input_dispose(GObject *object)
{
g_debug("diya_input_dispose: %s", diya_object_to_string(object));
DiyaInput *self = DIYA_INPUT(object);
if (self->name)
{
g_free(self->name);
self->name = NULL;
}
if (self->keyboard)
{
wl_keyboard_release(self->keyboard);
self->keyboard = NULL;
}
if (self->xkb_keymap)
{
xkb_keymap_unref(self->xkb_keymap);
self->xkb_keymap = NULL;
}
if (self->xkb_state)
{
xkb_state_unref(self->xkb_state);
self->xkb_state = NULL;
}
if (self->xkb_context)
{
xkb_context_unref(self->xkb_context);
self->xkb_context = NULL;
}
G_OBJECT_CLASS(diya_input_parent_class)->dispose(object);
}
static void diya_input_init(DiyaInput *self)
{
self->name = NULL;
self->keyboard = NULL;
self->xkb_state = NULL;
self->xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
self->xkb_keymap = NULL;
}
static const gchar *diya_input_to_string(DiyaObject *object)
{
DiyaInput *self = DIYA_INPUT(object);
if (self->name)
{
return self->name;
}
return "unknown";
}
static void diya_input_class_init(DiyaInputClass *class)
{
GObjectClass *gobject_class = G_OBJECT_CLASS(class);
DiyaObjectClass *base_class = DIYA_OBJECT_CLASS(class);
gobject_class->dispose = diya_input_dispose;
// gobject_class->set_property = diya_input_set_property;
// gobject_class->get_property = diya_input_get_property;
base_class->to_string = diya_input_to_string;
g_signal_new(DIYA_SIGNAL_INPUT_KEYBOARD_ENTER,
DIYA_TYPE_SHELL,
G_SIGNAL_DETAILED |
G_SIGNAL_ACTION |
G_SIGNAL_RUN_FIRST,
0,
NULL,
NULL,
NULL,
G_TYPE_NONE,
1,
G_TYPE_POINTER);
g_signal_new(DIYA_SIGNAL_INPUT_KEYBOARD_LEAVE,
DIYA_TYPE_SHELL,
G_SIGNAL_DETAILED |
G_SIGNAL_ACTION |
G_SIGNAL_RUN_FIRST,
0,
NULL,
NULL,
NULL,
G_TYPE_NONE,
0);
g_signal_new(DIYA_SIGNAL_INPUT_KEY_PRESSED,
DIYA_TYPE_SHELL,
G_SIGNAL_DETAILED |
G_SIGNAL_ACTION |
G_SIGNAL_RUN_FIRST,
0,
NULL,
NULL,
NULL,
G_TYPE_NONE,
3,
G_TYPE_UINT,
G_TYPE_UINT,
G_TYPE_UINT);
g_signal_new(DIYA_SIGNAL_INPUT_KEY_RELEASED,
DIYA_TYPE_SHELL,
G_SIGNAL_DETAILED |
G_SIGNAL_ACTION |
G_SIGNAL_RUN_FIRST,
0,
NULL,
NULL,
NULL,
G_TYPE_NONE,
3,
G_TYPE_UINT,
G_TYPE_UINT,
G_TYPE_UINT);
}
static void wl_keyboard_keymap(void *data, struct wl_keyboard *keyboard, uint32_t format, int32_t fd, uint32_t size)
{
(void)keyboard;
DiyaInput *self = data;
assert(format == WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1);
char *map_shm = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
assert(map_shm != MAP_FAILED);
struct xkb_keymap *xkb_keymap = xkb_keymap_new_from_string(self->xkb_context, map_shm, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
munmap(map_shm, size);
close(fd);
struct xkb_state *xkb_state = xkb_state_new(xkb_keymap);
xkb_keymap_unref(self->xkb_keymap);
xkb_state_unref(self->xkb_state);
self->xkb_keymap = xkb_keymap;
self->xkb_state = xkb_state;
}
static void wl_keyboard_enter(void *data, struct wl_keyboard *keyboard,
uint32_t serial, struct wl_surface *surface,
struct wl_array *keys)
{
(void)keyboard;
(void)serial;
(void)surface;
DiyaInput *self = data;
g_warning("keyboard enter; keys pressed are:");
uint32_t *key;
wl_array_for_each(key, keys)
{
char buf[128];
xkb_keysym_t sym = xkb_state_key_get_one_sym(self->xkb_state, *key + 8);
xkb_keysym_get_name(sym, buf, sizeof(buf));
g_warning("\tsym: %-12s (%d), ", buf, sym);
xkb_state_key_get_utf8(self->xkb_state, *key + 8, buf, sizeof(buf));
g_warning("\tutf8: '%s'", buf);
}
}
static void wl_keyboard_key(void *data, struct wl_keyboard *keyboard,
uint32_t serial, uint32_t time, uint32_t key, uint32_t state)
{
(void)keyboard;
(void)serial;
(void)time;
DiyaInput *self = data;
char buf[128];
uint32_t keycode = key + 8;
xkb_keysym_t sym = xkb_state_key_get_one_sym(self->xkb_state, keycode);
xkb_keysym_get_name(sym, buf, sizeof(buf));
const char *action = state == WL_KEYBOARD_KEY_STATE_PRESSED ? "press" : "release";
g_warning("wl_keyboard_key %s: sym: %-12s (%d), ", action, buf, sym);
xkb_state_key_get_utf8(self->xkb_state, keycode, buf, sizeof(buf));
g_warning("\tutf8: '%s'", buf);
}
static void wl_keyboard_leave(void *data, struct wl_keyboard *keyboard,
uint32_t serial, struct wl_surface *surface)
{
(void)data;
(void)keyboard;
(void)serial;
(void)surface;
g_warning("keyboard leave");
}
static void wl_keyboard_modifiers(void *data, struct wl_keyboard *keyboard,
uint32_t serial, uint32_t mods_depressed,
uint32_t mods_latched, uint32_t mods_locked,
uint32_t group)
{
(void)keyboard;
(void)serial;
DiyaInput *self = data;
xkb_state_update_mask(self->xkb_state, mods_depressed, mods_latched, mods_locked, 0, 0, group);
}
static void wl_keyboard_repeat_info(void *data, struct wl_keyboard *keyboard, int32_t rate, int32_t delay)
{
(void)data;
(void)keyboard;
(void)rate;
(void)delay;
}
static const struct wl_keyboard_listener wl_keyboard_listener = {
.keymap = wl_keyboard_keymap,
.enter = wl_keyboard_enter,
.leave = wl_keyboard_leave,
.key = wl_keyboard_key,
.modifiers = wl_keyboard_modifiers,
.repeat_info = wl_keyboard_repeat_info,
};
static void wl_seat_capabilities(void *data, struct wl_seat *seat, uint32_t capabilities)
{
DiyaInput *self = data;
bool have_keyboard = capabilities & WL_SEAT_CAPABILITY_KEYBOARD;
if (have_keyboard && self->keyboard == NULL)
{
self->keyboard = wl_seat_get_keyboard(seat);
wl_keyboard_add_listener(self->keyboard, &wl_keyboard_listener, self);
}
else if (!have_keyboard && self->keyboard != NULL)
{
wl_keyboard_release(self->keyboard);
self->keyboard = NULL;
}
}
static void wl_seat_name(void *data, struct wl_seat *seat, const char *name)
{
(void)seat;
DiyaInput *self = data;
if (self->name)
{
return;
}
g_info("Attach to seat: %s", name);
self->name = g_strdup(name);
}
static const struct wl_seat_listener wl_seat_listener = {
.capabilities = wl_seat_capabilities,
.name = wl_seat_name,
};
DiyaInput *diya_input_new(DiyaShell *shell)
{
assert(shell);
DiyaInput *self = g_object_new(DIYA_TYPE_INPUT, "shell", shell, NULL);
DiyaWayland *wayland = diya_shell_get_wayland(shell);
assert(wayland);
struct wl_seat *seat = diya_wayland_get_seat(wayland);
wl_seat_add_listener(seat, &wl_seat_listener, self);
return self;
}