qt: Add xkbcommon keyboard support for Wayland
This commit is contained in:
236
src/qt/xkbcommon_wl_keyboard.cpp
Normal file
236
src/qt/xkbcommon_wl_keyboard.cpp
Normal file
@@ -0,0 +1,236 @@
|
||||
/*
|
||||
* 86Box A hypervisor and IBM PC system emulator that specializes in
|
||||
* running old operating systems and software designed for IBM
|
||||
* PC systems and compatibles from 1981 through fairly recent
|
||||
* system designs based on the PCI bus.
|
||||
*
|
||||
* This file is part of the 86Box distribution.
|
||||
*
|
||||
* xkbcommon Wayland keyboard input module.
|
||||
*
|
||||
* Heavily inspired by libxkbcommon interactive-wayland.c
|
||||
*
|
||||
*
|
||||
*
|
||||
* Authors: RichardG, <richardg867@gmail.com>
|
||||
*
|
||||
* Copyright 2023 RichardG.
|
||||
*/
|
||||
extern "C" {
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
#include <86box/86box.h>
|
||||
};
|
||||
#include "xkbcommon_keyboard.hpp"
|
||||
#include <wayland-client.h>
|
||||
#include <wayland-util.h>
|
||||
|
||||
#include <qpa/qplatformnativeinterface.h>
|
||||
#include <QtDebug>
|
||||
#include <QGuiApplication>
|
||||
|
||||
typedef struct {
|
||||
struct wl_seat *wl_seat;
|
||||
struct wl_keyboard *wl_kbd;
|
||||
uint32_t version;
|
||||
|
||||
struct xkb_keymap *keymap;
|
||||
|
||||
struct wl_list link;
|
||||
} seat_t;
|
||||
|
||||
static bool wl_init_ok = false;
|
||||
static struct wl_list seats;
|
||||
static struct xkb_context *ctx;
|
||||
|
||||
static void
|
||||
xkbcommon_wl_set_keymap()
|
||||
{
|
||||
/* Grab keymap from the first seat with one. */
|
||||
seat_t *seat, *tmp;
|
||||
wl_list_for_each_safe(seat, tmp, &seats, link) {
|
||||
if (seat->keymap) {
|
||||
xkbcommon_init(seat->keymap);
|
||||
return;
|
||||
}
|
||||
}
|
||||
xkbcommon_close();
|
||||
}
|
||||
|
||||
static void
|
||||
kbd_keymap(void *data, struct wl_keyboard *wl_kbd, uint32_t format,
|
||||
int fd, uint32_t size)
|
||||
{
|
||||
seat_t *seat = (seat_t *) data;
|
||||
|
||||
char *buf = (char *) mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
|
||||
if (!buf) {
|
||||
qWarning() << "XKB Keyboard: Failed to mmap keymap with error" << errno;
|
||||
return;
|
||||
}
|
||||
|
||||
seat->keymap = xkb_keymap_new_from_buffer(ctx, buf, size - 1,
|
||||
XKB_KEYMAP_FORMAT_TEXT_V1,
|
||||
XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||
munmap(buf, size);
|
||||
close(fd);
|
||||
if (!seat->keymap) {
|
||||
qWarning() << "XKB Keyboard: Keymap compilation failed";
|
||||
return;
|
||||
}
|
||||
|
||||
xkbcommon_wl_set_keymap();
|
||||
}
|
||||
|
||||
static void
|
||||
kbd_enter(void *data, struct wl_keyboard *wl_kbd, uint32_t serial,
|
||||
struct wl_surface *surf, struct wl_array *keys)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
kbd_leave(void *data, struct wl_keyboard *wl_kbd, uint32_t serial,
|
||||
struct wl_surface *surf)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
kbd_key(void *data, struct wl_keyboard *wl_kbd, uint32_t serial, uint32_t time,
|
||||
uint32_t key, uint32_t state)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
kbd_modifiers(void *data, struct wl_keyboard *wl_kbd, uint32_t serial,
|
||||
uint32_t mods_depressed, uint32_t mods_latched,
|
||||
uint32_t mods_locked, uint32_t group)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
kbd_repeat_info(void *data, struct wl_keyboard *wl_kbd, int32_t rate,
|
||||
int32_t delay)
|
||||
{
|
||||
}
|
||||
|
||||
static const struct wl_keyboard_listener kbd_listener = {
|
||||
kbd_keymap,
|
||||
kbd_enter,
|
||||
kbd_leave,
|
||||
kbd_key,
|
||||
kbd_modifiers,
|
||||
kbd_repeat_info
|
||||
};
|
||||
|
||||
static void
|
||||
seat_capabilities(void *data, struct wl_seat *wl_seat, uint32_t caps)
|
||||
{
|
||||
seat_t *seat = (seat_t *) data;
|
||||
|
||||
if (!seat->wl_kbd && (caps & WL_SEAT_CAPABILITY_KEYBOARD)) {
|
||||
seat->wl_kbd = wl_seat_get_keyboard(seat->wl_seat);
|
||||
wl_keyboard_add_listener(seat->wl_kbd, &kbd_listener, seat);
|
||||
} else if (seat->wl_kbd && !(caps & WL_SEAT_CAPABILITY_KEYBOARD)) {
|
||||
if (seat->version >= WL_SEAT_RELEASE_SINCE_VERSION)
|
||||
wl_keyboard_release(seat->wl_kbd);
|
||||
else
|
||||
wl_keyboard_destroy(seat->wl_kbd);
|
||||
|
||||
static struct xkb_keymap *keymap = seat->keymap;
|
||||
seat->keymap = NULL;
|
||||
xkbcommon_wl_set_keymap();
|
||||
xkb_keymap_unref(keymap);
|
||||
|
||||
seat->wl_kbd = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
seat_name(void *data, struct wl_seat *wl_seat, const char *name)
|
||||
{
|
||||
}
|
||||
|
||||
static const struct wl_seat_listener seat_listener = {
|
||||
seat_capabilities,
|
||||
seat_name
|
||||
};
|
||||
|
||||
static void
|
||||
display_handle_global(void *data, struct wl_registry *wl_registry, uint32_t id,
|
||||
const char *interface, uint32_t version)
|
||||
{
|
||||
if (!strcmp(interface, "wl_seat")) {
|
||||
seat_t *seat = (seat_t *) malloc(sizeof(seat_t));
|
||||
memset(seat, 0, sizeof(seat_t));
|
||||
|
||||
seat->wl_seat = (wl_seat *) wl_registry_bind(wl_registry, id, &wl_seat_interface, MIN(version, 5));
|
||||
wl_seat_add_listener(seat->wl_seat, &seat_listener, seat);
|
||||
wl_list_insert(&seats, &seat->link);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
display_global_remove(void *data, struct wl_registry *wl_registry, uint32_t id)
|
||||
{
|
||||
xkbcommon_close();
|
||||
|
||||
seat_t *seat, *tmp;
|
||||
wl_list_for_each_safe(seat, tmp, &seats, link) {
|
||||
if (seat->wl_kbd) {
|
||||
if (seat->version >= WL_SEAT_RELEASE_SINCE_VERSION)
|
||||
wl_keyboard_release(seat->wl_kbd);
|
||||
else
|
||||
wl_keyboard_destroy(seat->wl_kbd);
|
||||
|
||||
xkb_keymap_unref(seat->keymap);
|
||||
}
|
||||
|
||||
if (seat->version >= WL_SEAT_RELEASE_SINCE_VERSION)
|
||||
wl_seat_release(seat->wl_seat);
|
||||
else
|
||||
wl_seat_destroy(seat->wl_seat);
|
||||
|
||||
wl_list_remove(&seat->link);
|
||||
free(seat);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct wl_registry_listener registry_listener = {
|
||||
display_handle_global,
|
||||
display_global_remove
|
||||
};
|
||||
|
||||
void
|
||||
xkbcommon_wl_init()
|
||||
{
|
||||
if (wl_init_ok)
|
||||
return;
|
||||
|
||||
wl_list_init(&seats);
|
||||
|
||||
ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
||||
if (!ctx) {
|
||||
qWarning() << "XKB Keyboard: XKB context creation failed";
|
||||
return;
|
||||
}
|
||||
|
||||
wl_display *display = (wl_display *) QGuiApplication::platformNativeInterface()->nativeResourceForIntegration("wl_display");
|
||||
if (display) {
|
||||
auto registry = wl_display_get_registry(display);
|
||||
if (registry) {
|
||||
wl_registry_add_listener(registry, ®istry_listener, nullptr);
|
||||
wl_display_roundtrip(display);
|
||||
wl_display_roundtrip(display);
|
||||
} else {
|
||||
goto err_ctx;
|
||||
}
|
||||
} else {
|
||||
goto err_ctx;
|
||||
}
|
||||
wl_init_ok = true;
|
||||
return;
|
||||
|
||||
err_ctx:
|
||||
xkb_context_unref(ctx);
|
||||
}
|
||||
Reference in New Issue
Block a user