Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

x11: Support xcb-keysyms as fallback when XKB support not available #679

Merged
merged 3 commits into from
Nov 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/subprojects/packagecache/
/subprojects/xcb-util-keysyms-0.4.1/
2 changes: 1 addition & 1 deletion meson.build
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
project('cog', 'c',
meson_version: '>=0.53.2',
meson_version: '>=0.55',
default_options: [
'buildtype=debugoptimized',
'b_ndebug=if-release',
Expand Down
9 changes: 9 additions & 0 deletions meson_options.txt
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,15 @@ option(
description: 'Build content-protection support in the Wayland platform plug-in'
)

# X11 platform-specifig features
option(
'x11_keyboard',
type: 'array',
value: ['xkb'],
choices: ['xkb', 'xcb-keysyms'],
description: 'Keyboard handler (xkb recommended)',
)

# Additional cog/cogctl options
option(
'cog_appid',
Expand Down
206 changes: 154 additions & 52 deletions platform/x11/cog-platform-x11.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,18 @@
#include <X11/Xlib.h>
#include <xcb/xcb.h>
#include <xcb/xcb_cursor.h>
#include <xkbcommon/xkbcommon-x11.h>

#ifdef COG_X11_USE_XCB_KEYSYMS
# if __has_include(<xcb/xcb_keysyms.h>)
# include <xcb/xcb_keysyms.h>
# else
# include <xcb_keysyms.h>
# endif
#endif /* COG_X11_USE_XCB_KEYSYMS */

#ifdef COG_X11_USE_XKB
# include <xkbcommon/xkbcommon-x11.h>
#endif

#if COG_HAVE_LIBPORTAL
# include "../common/cog-file-chooser.h"
Expand Down Expand Up @@ -82,6 +93,7 @@ struct CogX11Display {
GSource *source;
} xcb;

#ifdef COG_X11_USE_XKB
struct {
int32_t device_id;
struct xkb_context *context;
Expand All @@ -93,6 +105,11 @@ struct CogX11Display {
xkb_mod_mask_t num_lock;
xkb_mod_mask_t caps_lock;
} xkb;
#endif /* COG_X11_USE_XKB */

#ifdef COG_X11_USE_XCB_KEYSYMS
xcb_key_symbols_t *xcb_keysyms;
#endif /* COG_X11_USE_XCB_KEYSYMS */

struct {
PFNEGLGETPLATFORMDISPLAYEXTPROC get_platform_display;
Expand Down Expand Up @@ -185,6 +202,7 @@ xcb_paint_image (struct wpe_fdo_egl_exported_image *image)
eglSwapBuffers(s_display->egl.display, s_window->egl.surface);
}

#ifdef COG_X11_USE_XKB
static uint32_t
xcb_update_xkb_modifiers(uint32_t event_state)
{
Expand All @@ -211,36 +229,88 @@ xcb_update_xkb_modifiers(uint32_t event_state)
xkb_state_update_mask(s_display->xkb.state, depressed_mods, 0, locked_mods, 0, 0, 0);
return wpe_modifiers;
}
#endif /* COG_X11_USE_XKB */

#if COG_X11_USE_XCB_KEYSYMS
/*
* Convert XCB modifiers to WPE modifiers
*
* - wpe constants from <wpe/input.h>,
* - XCB constants from <xcb/xproto.h>
*
* Far from ideal implementation, but it avoids the need to link
* with xcb-keysyms (which is not often packaged by linux distributions)
* or requiring the X11/XKB extension (which is not always available, e.g.
* in VNC).
*/
static uint32_t
xcb_state_to_wpe_modifiers(uint16_t xcb_state)
{
uint32_t out = 0;

/* SHIFT, CONTROL, ALT/META keys */
if (xcb_state & XCB_MOD_MASK_SHIFT)
out |= wpe_input_keyboard_modifier_shift;

if (xcb_state & XCB_MOD_MASK_CONTROL)
out |= wpe_input_keyboard_modifier_control;

if (xcb_state & XCB_MOD_MASK_1)
out |= wpe_input_keyboard_modifier_alt;

/* Mouse buttons */
if (xcb_state & XCB_BUTTON_MASK_1)
out |= wpe_input_pointer_modifier_button1;

if (xcb_state & XCB_BUTTON_MASK_2)
out |= wpe_input_pointer_modifier_button2;

if (xcb_state & XCB_BUTTON_MASK_3)
out |= wpe_input_pointer_modifier_button3;

if (xcb_state & XCB_BUTTON_MASK_4)
out |= wpe_input_pointer_modifier_button4;

return out;
}
#endif /* COG_X11_USE_XCB_KEYSYMS */

static void
xcb_handle_key_press(CogView *view, xcb_key_press_event_t *event)
key_event_fill(struct wpe_input_keyboard_event *wpe_event, xcb_key_press_event_t *event)
{
uint32_t modifiers = xcb_update_xkb_modifiers(event->state);
uint32_t keysym = xkb_state_key_get_one_sym(s_display->xkb.state, event->detail);
wpe_event->time = event->time;
wpe_event->hardware_key_code = event->detail;

struct wpe_input_keyboard_event input_event = {
.time = event->time,
.key_code = keysym,
.hardware_key_code = event->detail,
.pressed = true,
.modifiers = modifiers,
};
#if COG_X11_USE_XKB
if (s_display->xkb.device_id >= 0 && s_display->xkb.state) {
wpe_event->modifiers = xcb_update_xkb_modifiers(event->state);
wpe_event->key_code = xkb_state_key_get_one_sym(s_display->xkb.state, event->detail);
return;
}
#endif /* COG_X11_USE_XKB */

#if COG_X11_USE_XCB_KEYSYMS
if (s_display->xcb_keysyms) {
wpe_event->modifiers = xcb_state_to_wpe_modifiers(event->state);
wpe_event->key_code = xcb_key_symbols_get_keysym(s_display->xcb_keysyms, event->detail, 0);
return;
}
#endif /* COG_X11_USE_XCB_KEYSYMS */
}

static void
xcb_handle_key_press(CogView *view, xcb_key_press_event_t *event)
{
struct wpe_input_keyboard_event input_event = {.pressed = true};
key_event_fill(&input_event, event);
cog_view_handle_key_event(view, &input_event);
}

static void
xcb_handle_key_release(CogView *view, xcb_key_press_event_t *event)
{
uint32_t modifiers = xcb_update_xkb_modifiers(event->state);
uint32_t keysym = xkb_state_key_get_one_sym(s_display->xkb.state, event->detail);

struct wpe_input_keyboard_event input_event = {
.time = event->time,
.key_code = keysym,
.hardware_key_code = event->detail,
.pressed = false,
.modifiers = modifiers,
};
struct wpe_input_keyboard_event input_event = {.pressed = false};
key_event_fill(&input_event, event);
cog_view_handle_key_event(view, &input_event);
}

Expand Down Expand Up @@ -604,44 +674,79 @@ clear_xcb (void)
XCloseDisplay (s_display->display);
}

#ifdef COG_X11_USE_XKB
static gboolean
init_xkb (void)
{
s_display->xkb.device_id = xkb_x11_get_core_keyboard_device_id (s_display->xcb.connection);
if (s_display->xkb.device_id == -1)
return FALSE;

s_display->xkb.context = xkb_context_new (0);
if (!s_display->xkb.context)
return FALSE;

s_display->xkb.keymap = xkb_x11_keymap_new_from_device (s_display->xkb.context, s_display->xcb.connection, s_display->xkb.device_id, XKB_KEYMAP_COMPILE_NO_FLAGS);
if (!s_display->xkb.keymap)
return FALSE;
if (((s_display->xkb.device_id = xkb_x11_get_core_keyboard_device_id(s_display->xcb.connection)) == -1) ||
!(s_display->xkb.context = xkb_context_new(0)) ||
!(s_display->xkb.keymap =
xkb_x11_keymap_new_from_device(s_display->xkb.context, s_display->xcb.connection,
s_display->xkb.device_id, XKB_KEYMAP_COMPILE_NO_FLAGS)))
goto no_xkb_cleanup;

s_display->xkb.shift = 1 << xkb_keymap_mod_get_index(s_display->xkb.keymap, "Shift");
s_display->xkb.control = 1 << xkb_keymap_mod_get_index(s_display->xkb.keymap, "Control");
s_display->xkb.alt = 1 << xkb_keymap_mod_get_index(s_display->xkb.keymap, "Mod1");
s_display->xkb.caps_lock = 1 << xkb_keymap_mod_get_index(s_display->xkb.keymap, "Lock");
s_display->xkb.num_lock = 1 << xkb_keymap_mod_get_index(s_display->xkb.keymap, "NumLock");

s_display->xkb.state =
xkb_x11_state_new_from_device(s_display->xkb.keymap, s_display->xcb.connection, s_display->xkb.device_id);
if (!s_display->xkb.state)
return FALSE;
if (!(s_display->xkb.state = xkb_x11_state_new_from_device(s_display->xkb.keymap, s_display->xcb.connection,
s_display->xkb.device_id)))
goto no_xkb_cleanup;

return TRUE;

no_xkb_cleanup:
g_clear_pointer(&s_display->xkb.keymap, xkb_keymap_unref);
g_clear_pointer(&s_display->xkb.context, xkb_context_unref);
s_display->xkb.device_id = -1;
return FALSE;
}
#endif /* COG_X11_USE_XKB */

static gboolean
init_keyboard(GError **error)
{
g_autoptr(GString) tried_impl = NULL;

#ifdef COG_X11_USE_XKB
tried_impl = g_string_new("XKB");
if (init_xkb()) {
g_debug("%s: Using XKB", G_STRFUNC);
return TRUE;
}
#endif /* COG_X11_USE_XKB */

#ifdef COG_X11_USE_XCB_KEYSYMS
tried_impl = tried_impl ? g_string_append(tried_impl, ", XCB-Keysyms") : g_string_new("XCB-Keysyms");

if ((s_display->xcb_keysyms = xcb_key_symbols_alloc(s_display->xcb.connection))) {
g_debug("%s: Using XCB-Keysyms", G_STRFUNC);
return TRUE;
}
#endif /* COG_X11_USE_XCB_KEYSYMS */

g_set_error(error,
COG_PLATFORM_WPE_ERROR,
COG_PLATFORM_WPE_ERROR_INIT,
"Could not initialize keyboard, tried %s",
tried_impl ? tried_impl->str : "no implementations");
return FALSE;
}

static void
clear_xkb (void)
clear_keyboard(void)
{
if (s_display->xkb.state)
xkb_state_unref (s_display->xkb.state);
if (s_display->xkb.keymap)
xkb_keymap_unref (s_display->xkb.keymap);
if (s_display->xkb.context)
xkb_context_unref (s_display->xkb.context);
#ifdef COG_X11_USE_XKB
g_clear_pointer(&s_display->xkb.state, xkb_state_unref);
g_clear_pointer(&s_display->xkb.keymap, xkb_keymap_unref);
g_clear_pointer(&s_display->xkb.context, xkb_context_unref);
#endif /* COG_X11_USE_XKB */

#ifdef COG_X11_USE_XCB_KEYSYMS
g_clear_pointer(&s_display->xcb_keysyms, xcb_key_symbols_free);
#endif /* COG_X11_USE_XCB_KEYSYMS */
}

static gboolean
Expand Down Expand Up @@ -798,12 +903,9 @@ cog_x11_platform_setup(CogPlatform *platform, CogShell *shell G_GNUC_UNUSED, con
return FALSE;
}

if (!init_xkb ()) {
g_set_error_literal (error,
COG_PLATFORM_WPE_ERROR,
COG_PLATFORM_WPE_ERROR_INIT,
"Failed to initialize XKB");
return FALSE;
if (!init_keyboard(error)) {
aperezdc marked this conversation as resolved.
Show resolved Hide resolved
g_warning("Running without proper keyboard support: %s", (*error)->message);
g_clear_error(error);
}

if (!init_egl ()) {
Expand Down Expand Up @@ -842,9 +944,9 @@ cog_x11_platform_finalize(GObject *object)
{
clear_glib ();
cog_gl_renderer_finalize(&s_display->gl_render);
clear_egl ();
clear_xkb ();
clear_xcb ();
clear_egl();
clear_keyboard();
clear_xcb();

g_clear_pointer (&s_window, free);
g_clear_pointer (&s_display, free);
Expand Down
40 changes: 30 additions & 10 deletions platform/x11/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,39 @@ if libportal_dep.found()
x11_platform_sources += ['cog-xdp-parent-x11.c']
endif

x11_platform_c_args = platform_c_args + [
'-DG_LOG_DOMAIN="Cog-X11"',
]
x11_platform_dependencies = [
wpebackend_fdo_dep,
cogplatformcommon_dep,
dependency('egl'),
dependency('xcb'),
dependency('xkbcommon-x11'),
dependency('x11-xcb'),
dependency('xcb-cursor'),
]

x11_platform_keyboard = get_option('x11_keyboard')
if x11_platform_keyboard.length() == 0
warning('No X11 keyboard support chosen, keyboard input will NOT work')
endif

x11_platform_keyboard_dep_names = {
'xkb': 'xkbcommon-x11',
'xcb-keysyms': 'xcb-keysyms',
}

foreach item : x11_platform_keyboard
x11_platform_dependencies += [dependency(x11_platform_keyboard_dep_names[item])]
x11_platform_c_args += ['-DCOG_X11_USE_@0@=1'.format(item.underscorify().to_upper())]
endforeach

x11_platform_plugin = shared_module('cogplatform-x11',
'cog-platform-x11.c',
x11_platform_sources,
c_args: platform_c_args + ['-DG_LOG_DOMAIN="Cog-X11"'],
dependencies: [
wpebackend_fdo_dep,
cogplatformcommon_dep,
dependency('egl'),
dependency('xcb'),
dependency('xkbcommon-x11'),
dependency('x11-xcb'),
dependency('xcb-cursor'),
],
c_args: x11_platform_c_args,
dependencies: x11_platform_dependencies,
gnu_symbol_visibility: 'hidden',
install_dir: plugin_path,
install: true,
Expand Down
24 changes: 24 additions & 0 deletions subprojects/packagefiles/xcb-util-keysyms-0.4.1/meson.build
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
project('xcb-util-keysyms', 'c',
version: '0.4.1',
default_options: [
],
)

xcb_dep = dependency('xcb', version: '>=1.4')
xcb_util_keysyms_dependencies = [xcb_dep, dependency('xproto', version: '>=7.0.8')]

xcb_proto_version = xcb_dep.get_variable('xcbproto_version')
assert(xcb_proto_version.version_compare('>=1.6'),
'libxcb was compiled against xcb-proto @0@, it needs to be compiled against 1.6 or newer'.format(xcb_proto_version))

xcb_util_keysyms_lib = static_library('xcb-util-keysyms',
'keysyms/keysyms.c',
dependencies: xcb_util_keysyms_dependencies,
pic: true,
)

xcb_util_keysyms_dep = declare_dependency(
link_with: xcb_util_keysyms_lib,
dependencies: xcb_util_keysyms_dependencies,
include_directories: include_directories('keysyms'),
)
9 changes: 9 additions & 0 deletions subprojects/xcb-keysyms.wrap
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[wrap-file]
directory = xcb-util-keysyms-0.4.1
source_url = https://xcb.freedesktop.org/dist/xcb-util-keysyms-0.4.1.tar.xz
source_filename = xcb-util-keysyms-0.4.1.tar.xz
source_hash = 7c260a5294412aed429df1da2f8afd3bd07b7cba3fec772fba15a613a6d5c638
patch_directory = xcb-util-keysyms-0.4.1

[provide]
xcb-keysyms = xcb_util_keysyms_dep