From bbbe0ec88d4dff35843f7acdaeab253151241deb Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Sat, 8 Apr 2023 00:31:25 -0300 Subject: [PATCH] qt: Add xkbcommon keyboard support for Wayland --- src/qt/CMakeLists.txt | 3 + src/qt/qt_mainwindow.cpp | 14 +- src/qt/xkbcommon_keyboard.cpp | 9 +- src/qt/xkbcommon_keyboard.hpp | 1 + src/qt/xkbcommon_wl_keyboard.cpp | 236 ++++++++++++++++++++++++++++++ src/qt/xkbcommon_wl_keyboard.hpp | 17 +++ src/qt/xkbcommon_x11_keyboard.cpp | 1 + 7 files changed, 279 insertions(+), 2 deletions(-) create mode 100644 src/qt/xkbcommon_wl_keyboard.cpp create mode 100644 src/qt/xkbcommon_wl_keyboard.hpp diff --git a/src/qt/CMakeLists.txt b/src/qt/CMakeLists.txt index 0cce392a9..dd3bc6f79 100644 --- a/src/qt/CMakeLists.txt +++ b/src/qt/CMakeLists.txt @@ -402,6 +402,9 @@ if (UNIX AND NOT APPLE AND NOT HAIKU) ecm_add_wayland_client_protocol(WL_SOURCE_VAR PROTOCOL ${CMAKE_SOURCE_DIR}/wl_protocols/pointer-constraints-unstable-v1.xml BASENAME pointer-constraints-unstable-v1) target_include_directories(ui PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ${Qt${QT_MAJOR}Gui_PRIVATE_INCLUDE_DIRS}) target_sources(ui PRIVATE ${WL_SOURCE_VAR} wl_mouse.cpp) + if (XKBCOMMON_FOUND) + target_sources(ui PRIVATE xkbcommon_wl_keyboard.cpp) + endif() target_compile_definitions(ui PRIVATE WAYLAND) endif() endif() diff --git a/src/qt/qt_mainwindow.cpp b/src/qt/qt_mainwindow.cpp index de7bceafb..40f01c2f6 100644 --- a/src/qt/qt_mainwindow.cpp +++ b/src/qt/qt_mainwindow.cpp @@ -101,6 +101,9 @@ extern int qt_nvr_save(void); # ifdef XKBCOMMON_X11 # include "xkbcommon_x11_keyboard.hpp" # endif +# ifdef WAYLAND +# include "xkbcommon_wl_keyboard.hpp" +# endif # endif # include # include @@ -652,9 +655,18 @@ MainWindow::MainWindow(QWidget *parent) ui->actionCursor_Puck->setChecked(true); } -#ifdef XKBCOMMON_X11 +#ifdef XKBCOMMON +# ifdef XKBCOMMON_X11 if (QApplication::platformName().contains("xcb")) xkbcommon_x11_init(); + else +# endif +# ifdef WAYLAND + if (QApplication::platformName().contains("wayland")) + xkbcommon_wl_init(); + else +# endif + {} #endif } diff --git a/src/qt/xkbcommon_keyboard.cpp b/src/qt/xkbcommon_keyboard.cpp index 1b4998b1f..87bc02aa5 100644 --- a/src/qt/xkbcommon_keyboard.cpp +++ b/src/qt/xkbcommon_keyboard.cpp @@ -228,7 +228,14 @@ struct xkb_keymap *xkbcommon_keymap = nullptr; void xkbcommon_init(struct xkb_keymap *keymap) { - xkbcommon_keymap = keymap; + if (keymap) + xkbcommon_keymap = keymap; +} + +void +xkbcommon_close() +{ + xkbcommon_keymap = NULL; } uint16_t diff --git a/src/qt/xkbcommon_keyboard.hpp b/src/qt/xkbcommon_keyboard.hpp index afcba41bb..c3b28eeec 100644 --- a/src/qt/xkbcommon_keyboard.hpp +++ b/src/qt/xkbcommon_keyboard.hpp @@ -16,4 +16,5 @@ */ extern void *xkbcommon_keymap; void xkbcommon_init(struct xkb_keymap *keymap); +void xkbcommon_close(); uint16_t xkbcommon_translate(uint32_t keycode); diff --git a/src/qt/xkbcommon_wl_keyboard.cpp b/src/qt/xkbcommon_wl_keyboard.cpp new file mode 100644 index 000000000..2dda6368a --- /dev/null +++ b/src/qt/xkbcommon_wl_keyboard.cpp @@ -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, + * + * Copyright 2023 RichardG. + */ +extern "C" { +#include +#include +#include +#include <86box/86box.h> +}; +#include "xkbcommon_keyboard.hpp" +#include +#include + +#include +#include +#include + +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); +} diff --git a/src/qt/xkbcommon_wl_keyboard.hpp b/src/qt/xkbcommon_wl_keyboard.hpp new file mode 100644 index 000000000..a70cb9e9b --- /dev/null +++ b/src/qt/xkbcommon_wl_keyboard.hpp @@ -0,0 +1,17 @@ +/* + * 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. + * + * Definitions for xkbcommon Wayland keyboard input module. + * + * + * + * Authors: RichardG, + * + * Copyright 2023 RichardG. + */ +void xkbcommon_wl_init(); diff --git a/src/qt/xkbcommon_x11_keyboard.cpp b/src/qt/xkbcommon_x11_keyboard.cpp index b9c16cc9b..8cba2c4d1 100644 --- a/src/qt/xkbcommon_x11_keyboard.cpp +++ b/src/qt/xkbcommon_x11_keyboard.cpp @@ -77,6 +77,7 @@ xkbcommon_x11_init() } xkbcommon_init(keymap); + goto err_conn; err_ctx: xkb_context_unref(ctx);