[vlc-commits] xcb/window: implement XKeyboard using xkbcommon

Rémi Denis-Courmont git at videolan.org
Tue May 29 21:00:47 CEST 2018


vlc | branch: master | Rémi Denis-Courmont <remi at remlab.net> | Tue May 29 21:52:25 2018 +0300| [d06b1c458f47b70f489c4d1aa02e554d6c3bc2a6] | committer: Rémi Denis-Courmont

xcb/window: implement XKeyboard using xkbcommon

> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=d06b1c458f47b70f489c4d1aa02e554d6c3bc2a6
---

 configure.ac                      |  14 ++-
 modules/video_output/Makefile.am  |  10 +-
 modules/video_output/xcb/window.c | 233 ++++++++++++++++++++++++++++----------
 3 files changed, 193 insertions(+), 64 deletions(-)

diff --git a/configure.ac b/configure.ac
index 078a588aca..40d78663d5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3077,6 +3077,7 @@ have_xcb="no"
 have_xcb_keysyms="no"
 have_xcb_randr="no"
 have_xcb_xvideo="no"
+have_xcb_xkb="no"
 AS_IF([test "${enable_xcb}" != "no"], [
   dnl libxcb
   PKG_CHECK_MODULES(XCB, [xcb >= 1.6])
@@ -3091,15 +3092,26 @@ AS_IF([test "${enable_xcb}" != "no"], [
 
   PKG_CHECK_MODULES(XCB_RANDR, [xcb-randr >= 1.3], [have_xcb_randr="yes"])
 
+  PKG_CHECK_MODULES([XCB_XKB], [xcb-xkb], [
+    PKG_CHECK_MODULES([XKBCOMMON_X11], [xkbcommon-x11], [
+      have_xcb_xkb="yes"
+    ], [
+      AC_MSG_WARN([${XKBCOMMON_X11_PKG_ERRORS}. Hotkeys will not work.])
+    ])
+  ], [
+    AC_MSG_WARN([${XCB_XKB_PKG_ERRORS}. Hotkeys will not work.])
+  ])
+
   dnl xcb-utils
   PKG_CHECK_MODULES(XCB_KEYSYMS, [xcb-keysyms >= 0.3.4], [have_xcb_keysyms="yes"], [
-    AC_MSG_WARN([${XCB_KEYSYMS_PKG_ERRORS}. Hotkeys will not work.])
+    AC_MSG_WARN([${XCB_KEYSYMS_PKG_ERRORS}])
   ])
 ])
 AM_CONDITIONAL([HAVE_XCB], [test "${have_xcb}" = "yes"])
 AM_CONDITIONAL([HAVE_XCB_KEYSYMS], [test "${have_xcb_keysyms}" = "yes"])
 AM_CONDITIONAL([HAVE_XCB_RANDR], [test "${have_xcb_randr}" = "yes"])
 AM_CONDITIONAL([HAVE_XCB_XVIDEO], [test "${have_xcb_xvideo}" = "yes"])
+AM_CONDITIONAL([HAVE_XCB_XKB], [test "${have_xcb_xkb}" = "yes"])
 
 
 dnl
diff --git a/modules/video_output/Makefile.am b/modules/video_output/Makefile.am
index a6ad7fba02..2f27c27735 100644
--- a/modules/video_output/Makefile.am
+++ b/modules/video_output/Makefile.am
@@ -150,8 +150,10 @@ libxcb_xv_plugin_la_LIBADD = libvlc_xcb_events.la \
 libxcb_window_plugin_la_SOURCES = video_output/xcb/window.c
 libxcb_window_plugin_la_CFLAGS = $(AM_CFLAGS) \
 	$(CFLAGS_xcb_window) \
-	$(XPROTO_CFLAGS) $(XCB_CFLAGS) $(XCB_KEYSYMS_CFLAGS)
-libxcb_window_plugin_la_LIBADD = $(XPROTO_LIBS) $(XCB_LIBS) $(XCB_KEYSYMS_LIBS)
+	$(XPROTO_CFLAGS) $(XCB_CFLAGS) \
+	$(XCB_XKB_CFLAGS) $(XKBCOMMON_X11_CFLAGS)
+libxcb_window_plugin_la_LIBADD = $(XPROTO_LIBS) $(XCB_LIBS) \
+	$(XCB_XKB_LIBS) $(XKBCOMMON_X11_LIBS)
 
 libegl_x11_plugin_la_SOURCES = video_output/opengl/egl.c
 libegl_x11_plugin_la_CPPFLAGS = $(AM_CPPFLAGS) -DUSE_PLATFORM_X11=1
@@ -165,11 +167,11 @@ libglx_plugin_la_LIBADD = $(GL_LIBS) $(X_LIBS) $(X_PRE_LIBS) -lX11
 if HAVE_XCB
 pkglib_LTLIBRARIES += libvlc_xcb_events.la
 vout_LTLIBRARIES += libxcb_x11_plugin.la libxcb_window_plugin.la
-if HAVE_XCB_KEYSYMS
+if HAVE_XCB_XKB
 libxcb_window_plugin_la_SOURCES += \
 	video_output/xcb/keysym.h video_output/xcb/xcb_keysym.h \
 	video_output/xcb/vlc_xkb.h video_output/xcb/xkb.c
-libxcb_window_plugin_la_CFLAGS += -DHAVE_XCB_KEYSYMS
+libxcb_window_plugin_la_CFLAGS += -DHAVE_XCB_XKB -DHAVE_XKBCOMMON
 endif
 if HAVE_XCB_XVIDEO
 vout_LTLIBRARIES += libxcb_xv_plugin.la
diff --git a/modules/video_output/xcb/window.c b/modules/video_output/xcb/window.c
index 1e4eae8a06..134d313c12 100644
--- a/modules/video_output/xcb/window.c
+++ b/modules/video_output/xcb/window.c
@@ -31,8 +31,9 @@
 #include <limits.h> /* _POSIX_HOST_NAME_MAX */
 
 #include <xcb/xcb.h>
-#ifdef HAVE_XCB_KEYSYMS
-# include <xcb/xcb_keysyms.h>
+#ifdef HAVE_XCB_XKB
+# include <xcb/xkb.h>
+# include <xkbcommon/xkbcommon-x11.h>
 # include "vlc_xkb.h"
 #endif
 typedef xcb_atom_t Atom;
@@ -47,9 +48,6 @@ typedef xcb_atom_t Atom;
 struct vout_window_sys_t
 {
     xcb_connection_t *conn;
-#ifdef HAVE_XCB_KEYSYMS
-    xcb_key_symbols_t *keys;
-#endif
     vlc_thread_t thread;
 
     xcb_window_t root;
@@ -58,9 +56,162 @@ struct vout_window_sys_t
     xcb_atom_t wm_state_below;
     xcb_atom_t wm_state_fullscreen;
 
+#ifdef HAVE_XCB_XKB
+    struct
+    {
+        struct xkb_context *ctx;
+        struct xkb_keymap *map;
+        struct xkb_state *state;
+        uint8_t base;
+    } xkb;
+#endif
+
     bool embedded;
 };
 
+#ifdef HAVE_XCB_XKB
+static int InitKeyboard(vout_window_t *wnd)
+{
+    vout_window_sys_t *sys = wnd->sys;
+    xcb_connection_t *conn = sys->conn;
+
+    int32_t core = xkb_x11_get_core_keyboard_device_id(conn);
+    if (core == -1)
+        return -1;
+
+    sys->xkb.map = xkb_x11_keymap_new_from_device(sys->xkb.ctx, conn, core,
+                                                  XKB_KEYMAP_COMPILE_NO_FLAGS);
+    if (unlikely(sys->xkb.map == NULL))
+        return -1;
+
+    sys->xkb.state = xkb_x11_state_new_from_device(sys->xkb.map, conn, core);
+    if (unlikely(sys->xkb.state == NULL))
+    {
+        xkb_keymap_unref(sys->xkb.map);
+        sys->xkb.map = NULL;
+        return -1;
+    }
+    return 0;
+}
+
+static void DeinitKeyboard(vout_window_t *wnd)
+{
+    vout_window_sys_t *sys = wnd->sys;
+
+    if (sys->xkb.map == NULL)
+        return;
+
+    xkb_state_unref(sys->xkb.state);
+    xkb_keymap_unref(sys->xkb.map);
+    sys->xkb.map = NULL;
+}
+
+static void ProcessKeyboardEvent(vout_window_t *wnd,
+                                 const xcb_generic_event_t *ev)
+{
+    vout_window_sys_t *sys = wnd->sys;
+
+    switch (ev->pad0)
+    {
+        case XCB_XKB_NEW_KEYBOARD_NOTIFY:
+        case XCB_XKB_MAP_NOTIFY:
+            msg_Dbg(wnd, "refreshing keyboard mapping");
+            DeinitKeyboard(wnd);
+            InitKeyboard(wnd);
+            break;
+
+        case XCB_XKB_STATE_NOTIFY:
+            if (sys->xkb.map != NULL)
+            {
+                const xcb_xkb_state_notify_event_t *ne = (void *)ev;
+
+                xkb_state_update_mask(sys->xkb.state, ne->baseMods,
+                                      ne->latchedMods, ne->lockedMods,
+                                      ne->baseGroup, ne->latchedGroup,
+                                      ne->lockedGroup);
+            }
+            break;
+    }
+}
+
+static int InitKeyboardExtension(vout_window_t *wnd)
+{
+    vout_window_sys_t *sys = wnd->sys;
+    xcb_connection_t *conn = sys->conn;
+    uint16_t maj, min;
+
+    if (!xkb_x11_setup_xkb_extension(conn, XKB_X11_MIN_MAJOR_XKB_VERSION,
+                                     XKB_X11_MIN_MINOR_XKB_VERSION,
+                                     XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS,
+                                     &maj, &min, &sys->xkb.base, NULL))
+    {
+        msg_Err(wnd, "XKeyboard initialization error");
+        return 0;
+    }
+
+    msg_Dbg(wnd, "XKeyboard v%"PRIu16".%"PRIu16" initialized", maj, min);
+    sys->xkb.ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
+    if (unlikely(sys->xkb.ctx == NULL))
+        return -1;
+
+    /* Events: new KB, keymap change, state (modifiers) change */
+    const uint16_t affect = XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY
+                          | XCB_XKB_EVENT_TYPE_MAP_NOTIFY
+                          | XCB_XKB_EVENT_TYPE_STATE_NOTIFY;
+    /* Map event sub-types: everything except key behaviours */
+    const uint16_t map_parts = XCB_XKB_MAP_PART_KEY_TYPES
+                             | XCB_XKB_MAP_PART_KEY_SYMS
+                             | XCB_XKB_MAP_PART_MODIFIER_MAP
+                             | XCB_XKB_MAP_PART_EXPLICIT_COMPONENTS
+                             | XCB_XKB_MAP_PART_KEY_ACTIONS
+                             | XCB_XKB_MAP_PART_VIRTUAL_MODS
+                             | XCB_XKB_MAP_PART_VIRTUAL_MOD_MAP;
+    static const xcb_xkb_select_events_details_t details =
+    {
+        /* New keyboard details */
+        .affectNewKeyboard = XCB_XKB_NKN_DETAIL_KEYCODES,
+        .newKeyboardDetails = XCB_XKB_NKN_DETAIL_KEYCODES,
+        /* State event sub-types: as per xkb_state_update_mask() */
+#define STATE_PARTS  XCB_XKB_STATE_PART_MODIFIER_BASE \
+                   | XCB_XKB_STATE_PART_MODIFIER_LATCH \
+                   | XCB_XKB_STATE_PART_MODIFIER_LOCK \
+                   | XCB_XKB_STATE_PART_GROUP_BASE \
+                   | XCB_XKB_STATE_PART_GROUP_LATCH \
+                   | XCB_XKB_STATE_PART_GROUP_LOCK
+        .affectState = STATE_PARTS,
+        .stateDetails = STATE_PARTS,
+    };
+
+    int32_t core = xkb_x11_get_core_keyboard_device_id(conn);
+    if (core == -1)
+    {
+        xkb_context_unref(sys->xkb.ctx);
+        sys->xkb.ctx = NULL;
+        return -1;
+    }
+
+    xcb_xkb_select_events_aux(conn, core, affect, 0, 0, map_parts, map_parts,
+                              &details);
+
+    InitKeyboard(wnd);
+    return 0;
+}
+
+static void DeinitKeyboardExtension(vout_window_t *wnd)
+{
+    vout_window_sys_t *sys = wnd->sys;
+
+    if (sys->xkb.ctx == NULL)
+        return;
+
+    DeinitKeyboard(wnd);
+    xkb_context_unref(sys->xkb.ctx);
+}
+#else
+# define InitKeyboardExtension(w) (w, -1)
+# define DeinitKeyboardExtension(w) ((void)(w))
+#endif
+
 static xcb_cursor_t CursorCreate(xcb_connection_t *conn, xcb_window_t root)
 {
     xcb_cursor_t cur = xcb_generate_id(conn);
@@ -80,29 +231,14 @@ static int ProcessEvent(vout_window_t *wnd, xcb_generic_event_t *ev)
     {
         case XCB_KEY_PRESS:
         {
-#ifdef HAVE_XCB_KEYSYMS
+#ifdef HAVE_XCB_XKB
             xcb_key_press_event_t *e = (xcb_key_press_event_t *)ev;
-            xcb_keysym_t sym = xcb_key_press_lookup_keysym(sys->keys, e, 0);
-            uint_fast32_t vk = vlc_xkb_convert_keysym(sym);
+            uint_fast32_t vk = vlc_xkb_get_one(sys->xkb.state, e->detail);
 
             msg_Dbg(wnd, "key: 0x%08"PRIxFAST32" (X11: 0x%04"PRIx32")",
-                    vk, sym);
+                    vk, e->detail);
             if (vk == KEY_UNSET)
                 break;
-            if (e->state & XCB_MOD_MASK_SHIFT) /* Shift */
-                vk |= KEY_MODIFIER_SHIFT;
-            /* XCB_MOD_MASK_LOCK */ /* Caps Lock */
-            if (e->state & XCB_MOD_MASK_CONTROL) /* Control */
-                vk |= KEY_MODIFIER_CTRL;
-            if (e->state & XCB_MOD_MASK_1) /* Alternate */
-                vk |= KEY_MODIFIER_ALT;
-            /* XCB_MOD_MASK_2 */ /* Numeric Pad Lock */
-            if (e->state & XCB_MOD_MASK_3) /* Super */
-                vk |= KEY_MODIFIER_META;
-            if (e->state & XCB_MOD_MASK_4) /* Meta */
-                vk |= KEY_MODIFIER_META;
-            if (e->state & XCB_MOD_MASK_5) /* Alternate Graphic */
-                vk |= KEY_MODIFIER_ALT;
 
             vout_window_ReportKeyPress(wnd, vk);
 #endif
@@ -151,17 +287,16 @@ static int ProcessEvent(vout_window_t *wnd, xcb_generic_event_t *ev)
             break;
 
         case XCB_MAPPING_NOTIFY:
-        {
-#ifdef HAVE_XCB_KEYSYMS
-            xcb_mapping_notify_event_t *e = (xcb_mapping_notify_event_t *)ev;
-
-            msg_Dbg(wnd, "refreshing keyboard mapping");
-            xcb_refresh_keyboard_mapping(sys->keys, e);
-#endif
             break;
-        }
 
         default:
+#ifdef HAVE_XCB_XKB
+            if (sys->xkb.ctx != NULL && ev->response_type == sys->xkb.base)
+            {
+                ProcessKeyboardEvent(wnd, ev);
+                break;
+            }
+#endif
             msg_Dbg (wnd, "unhandled event %"PRIu8, ev->response_type);
     }
 
@@ -488,12 +623,8 @@ static int Open (vout_window_t *wnd, const vout_window_cfg_t *cfg)
     wnd->sys = p_sys;
 
     p_sys->conn = conn;
-#ifdef HAVE_XCB_KEYSYMS
-    if (var_InheritBool (wnd, "keyboard-events"))
-        p_sys->keys = xcb_key_symbols_alloc(conn);
-    else
-        p_sys->keys = NULL;
-#endif
+    if (var_InheritBool(wnd, "keyboard-events"))
+        InitKeyboardExtension(wnd);
     p_sys->root = scr->root;
 
     /* ICCCM
@@ -565,10 +696,7 @@ static int Open (vout_window_t *wnd, const vout_window_cfg_t *cfg)
      * request from this thread must be completed at this point. */
     if (vlc_clone (&p_sys->thread, Thread, wnd, VLC_THREAD_PRIORITY_LOW))
     {
-#ifdef HAVE_XCB_KEYSYMS
-        if (p_sys->keys != NULL)
-            xcb_key_symbols_free(p_sys->keys);
-#endif
+        DeinitKeyboardExtension(wnd);
         goto error;
     }
 
@@ -593,11 +721,8 @@ static void Close (vout_window_t *wnd)
 
     vlc_cancel (p_sys->thread);
     vlc_join (p_sys->thread, NULL);
-#ifdef HAVE_XCB_KEYSYMS
-    if (p_sys->keys != NULL)
-        xcb_key_symbols_free(p_sys->keys);
-#endif
 
+    DeinitKeyboardExtension(wnd);
     xcb_disconnect (conn);
     free (wnd->display.x11);
     free (p_sys);
@@ -724,18 +849,11 @@ static int EmOpen (vout_window_t *wnd, const vout_window_cfg_t *cfg)
     vout_window_ReportSize(wnd, geo->width, geo->height);
     free (geo);
 
-#ifdef HAVE_XCB_KEYSYMS
     /* Try to subscribe to keyboard and mouse events (only one X11 client can
      * subscribe to input events, so this can fail). */
-    if (var_InheritBool (wnd, "keyboard-events"))
-    {
-        p_sys->keys = xcb_key_symbols_alloc(conn);
-        if (p_sys->keys != NULL)
-            value |= XCB_EVENT_MASK_KEY_PRESS;
-    }
-    else
-        p_sys->keys = NULL;
-#endif
+    if (var_InheritBool(wnd, "keyboard-events")
+     && InitKeyboardExtension(wnd) == 0)
+        value |= XCB_EVENT_MASK_KEY_PRESS;
 
     if (var_InheritBool(wnd, "mouse-events"))
         value |= XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE;
@@ -746,10 +864,7 @@ static int EmOpen (vout_window_t *wnd, const vout_window_cfg_t *cfg)
     CacheAtoms (p_sys);
     if (vlc_clone (&p_sys->thread, Thread, wnd, VLC_THREAD_PRIORITY_LOW))
     {
-#ifdef HAVE_XCB_KEYSYMS
-        if (p_sys->keys != NULL)
-            xcb_key_symbols_free(p_sys->keys);
-#endif
+        DeinitKeyboardExtension(wnd);
         goto error;
     }
 



More information about the vlc-commits mailing list