XCB key press handling
authorRémi Denis-Courmont <rdenis@simphalempin.com>
Tue, 3 Feb 2009 21:49:15 +0000 (23:49 +0200)
committerRémi Denis-Courmont <rdenis@simphalempin.com>
Wed, 4 Feb 2009 20:14:24 +0000 (22:14 +0200)
Windows-specific keys are missing (found in XFree86?).
Mouse wheel is implemented with mouse events (like X11); afterall it is
not on the KEYboard.

modules/video_output/Modules.am
modules/video_output/xcb/keys.c [new file with mode: 0644]
modules/video_output/xcb/xcb.c
modules/video_output/xcb/xcb_vlc.h

index 49a1755..5f68727 100644 (file)
@@ -25,17 +25,21 @@ XCB_LIBS = -lxcb
 XCB_SHM_LIBS = -lxcb-shm
 XCB_AUX_LIBS = -lxcb-aux
 XCB_IMAGE_LIBS = -lxcb-image
+XCB_KEYSYMS_LIBS = -lxcb-keysyms
 
 libxcb_plugin_la_SOURCES = \
        xcb/xcb_vlc.h \
        xcb/xcb.c \
-       xcb/events.c
+       xcb/events.c \
+       xcb/keys.c
 libxcb_plugin_la_CFLAGS = $(AM_CFLAGS) \
        $(XCB_CFLAGS) $(XCB_SHM) \
-       $(XCB_AUX_CFLAGS) $(XCB_IMAGE_CFLAGS)
+       $(XPROTO_CFLAGS) \
+       $(XCB_AUX_CFLAGS) $(XCB_IMAGE_CFLAGS) $(XCB_KEYSYMS_CFLAGS)
 libxcb_plugin_la_LIBADD = $(AM_LIBADD) \
        $(XCB_LIBS) $(XCB_SHM) \
-       $(XCB_AUX_LIBS) $(XCB_IMAGE_LIBS)
+       $(XPROTO_LIBS) \
+       $(XCB_AUX_LIBS) $(XCB_IMAGE_LIBS) $(XCB_KEYSYMS_LIBS)
 libxcb_plugin_la_DEPENDENCIES =
 
 libxcb_window_plugin_la_SOURCES = xcb/window.c
diff --git a/modules/video_output/xcb/keys.c b/modules/video_output/xcb/keys.c
new file mode 100644 (file)
index 0000000..d4a9a1f
--- /dev/null
@@ -0,0 +1,219 @@
+/**
+ * @file keys.c
+ * @brief X C Bindings VLC keyboard event handling
+ */
+/*****************************************************************************
+ * Copyright © 2009 Rémi Denis-Courmont
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2.0
+ * of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ ****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <inttypes.h>
+#include <ctype.h>
+#include <assert.h>
+
+#include <xcb/xcb.h>
+#include <xcb/xcb_keysyms.h>
+#include <X11/keysym.h>
+
+#include <vlc_common.h>
+#include <vlc_keys.h>
+
+#include "xcb_vlc.h"
+
+struct key_handler_t
+{
+    vlc_object_t      *obj;
+    xcb_key_symbols_t *syms;
+};
+
+/**
+ * Create an X11 key event handler for a VLC window.
+ * The caller shall arrange receiving applicable X11 events, and pass them to
+ * ProcessKeyEvent() later.
+ *
+ * @param obj VLC object owning an X window
+ * @param conn XCB connection to the X server (to fetch key mappings)
+ * @return NULL on error, or a key handling context.
+ */
+key_handler_t *CreateKeyHandler (vlc_object_t *obj, xcb_connection_t *conn)
+{
+    key_handler_t *ctx = malloc (sizeof (*ctx));
+    if (!ctx)
+        return NULL;
+
+    ctx->obj = obj;
+    ctx->syms = xcb_key_symbols_alloc (conn);
+    return ctx;
+}
+
+void DestroyKeyHandler (key_handler_t *ctx)
+{
+    xcb_key_symbols_free (ctx->syms);
+    free (ctx);
+}
+
+static int keysymcmp (const void *pa, const void *pb)
+{
+    int a = *(const xcb_keysym_t *)pa;
+    int b = *(const xcb_keysym_t *)pb;
+
+    return a - b;
+}
+
+static int ConvertKeySym (xcb_keysym_t sym)
+{
+    static const struct
+    {
+        xcb_keysym_t x11;
+        uint32_t vlc;
+    } *res, tab[] = {
+    /* This list MUST be in XK_* incremental order (see keysymdef.h),
+     * so that binary search works.
+     * Multiple X keys can match the same VLC key.
+     * X key symbols must be in the first column of the struct. */
+        { XK_BackSpace,     KEY_BACKSPACE, },
+        { XK_Tab,           KEY_TAB, },
+        { XK_Return,        KEY_ENTER, },
+        { XK_Escape,        KEY_ESC, },
+        { XK_Home,          KEY_HOME, },
+        { XK_Left,          KEY_LEFT, },
+        { XK_Up,            KEY_UP, },
+        { XK_Right,         KEY_RIGHT, },
+        { XK_Down,          KEY_DOWN, },
+        { XK_Page_Up,       KEY_PAGEUP, },
+        { XK_Page_Down,     KEY_PAGEDOWN, },
+        { XK_End,           KEY_END, },
+        { XK_Begin,         KEY_HOME, },
+        { XK_Insert,        KEY_INSERT, },
+        { XK_Menu,          KEY_MENU },
+        { XK_KP_Space,      KEY_SPACE, },
+        { XK_KP_Tab,        KEY_TAB, },
+        { XK_KP_Enter,      KEY_ENTER, },
+        { XK_KP_F1,         KEY_F1, },
+        { XK_KP_F2,         KEY_F2, },
+        { XK_KP_F3,         KEY_F3, },
+        { XK_KP_F4,         KEY_F4, },
+        { XK_KP_Home,       KEY_HOME, },
+        { XK_KP_Left,       KEY_LEFT, },
+        { XK_KP_Up,         KEY_UP, },
+        { XK_KP_Right,      KEY_RIGHT, },
+        { XK_KP_Down,       KEY_DOWN, },
+        { XK_KP_Page_Up,    KEY_PAGEUP, },
+        { XK_KP_Page_Down,  KEY_PAGEDOWN, },
+        { XK_KP_End,        KEY_END, },
+        { XK_KP_Begin,      KEY_HOME, },
+        { XK_KP_Insert,     KEY_INSERT, },
+        { XK_KP_Delete,     KEY_DELETE, },
+        { XK_F1,            KEY_F1, },
+        { XK_F2,            KEY_F2, },
+        { XK_F3,            KEY_F3, },
+        { XK_F4,            KEY_F4, },
+        { XK_F5,            KEY_F5, },
+        { XK_F6,            KEY_F6, },
+        { XK_F7,            KEY_F7, },
+        { XK_F8,            KEY_F8, },
+        { XK_F9,            KEY_F9, },
+        { XK_F10,           KEY_F10, },
+        { XK_F11,           KEY_F11, },
+        { XK_F12,           KEY_F12, },
+        { XK_Delete,        KEY_DELETE, },
+    };
+    /* TODO: add XF86 extensions */
+#if 0
+    KEY_BROWSER_BACK
+    KEY_BROWSER_FORWARD
+    KEY_BROWSER_REFRESH
+    KEY_BROWSER_STOP
+    KEY_BROWSER_SEARCH
+    KEY_BROWSER_FAVORITES
+    KEY_BROWSER_HOME
+    KEY_VOLUME_MUTE
+    KEY_VOLUME_DOWN
+    KEY_VOLUME_UP
+    KEY_MEDIA_NEXT_TRACK
+    KEY_MEDIA_PREV_TRACK
+    KEY_MEDIA_STOP
+    KEY_MEDIA_PLAY_PAUSE
+#endif
+
+    /* X11 and VLC both use the ASCII code for printable ASCII characters,
+     * except for space (only X11). */
+    if (sym == XK_space)
+        return KEY_SPACE;
+    if (isascii(sym))
+        return sym;
+    if (sym > XK_Delete)
+        return KEY_UNSET;
+
+    /* Special keys */
+    res = bsearch (&sym, tab, sizeof (tab) / sizeof (tab[0]), sizeof (tab[0]),
+                   keysymcmp);
+    if (res != NULL)
+        return res->vlc;
+
+    return KEY_UNSET;
+}
+
+
+/**
+ * Process an X11 event, convert into VLC hotkey event if applicable.
+ *
+ * @param ctx key handler created with CreateKeyHandler()
+ * @param ev XCB event to process
+ * @return 0 if the event was handled and free()'d, non-zero otherwise
+ */
+int ProcessKeyEvent (key_handler_t *ctx, xcb_generic_event_t *ev)
+{
+    assert (ctx);
+
+    switch (ev->response_type & 0x7f)
+    {
+        case XCB_KEY_PRESS:
+        {
+            xcb_key_press_event_t *e = (xcb_key_press_event_t *)ev;
+            xcb_keysym_t sym = xcb_key_press_lookup_keysym (ctx->syms, e, 0);
+            int vk = ConvertKeySym (sym);
+
+            if (vk == KEY_UNSET)
+                break;
+            if (e->state & XCB_MOD_MASK_SHIFT)
+                vk |= KEY_MODIFIER_SHIFT;
+            if (e->state & XCB_MOD_MASK_CONTROL)
+                vk |= KEY_MODIFIER_CTRL;
+            if (e->state & XCB_MOD_MASK_1)
+                vk |= KEY_MODIFIER_ALT;
+            if (e->state & XCB_MOD_MASK_4)
+                vk |= KEY_MODIFIER_COMMAND;
+            var_SetInteger (ctx->obj->p_libvlc, "key-pressed", vk);
+            break;
+        }
+
+        case XCB_KEY_RELEASE:
+            break;
+
+        /*TODO: key mappings update*/
+        default:
+            return -1;
+    }
+
+    free (ev);
+    return 0;
+}
index e39257c..52952fb 100644 (file)
@@ -76,6 +76,7 @@ struct vout_sys_t
     xcb_connection_t *conn;
     xcb_screen_t *screen;
     vout_window_t *embed; /* VLC window (when windowed) */
+    key_handler_t *keys;
 
     xcb_visualid_t vid;
     xcb_window_t parent; /* parent X window */
@@ -192,6 +193,9 @@ static int Open (vlc_object_t *obj)
         }
     }
 
+    /* Prefetch keyboard mappings */
+    p_sys->keys = CreateKeyHandler (obj, p_sys->conn);
+
     vout->pf_init = Init;
     vout->pf_end = Deinit;
     vout->pf_display = Display;
@@ -213,6 +217,8 @@ static void Close (vlc_object_t *obj)
     vout_sys_t *p_sys = vout->p_sys;
 
     assert (p_sys->embed == NULL);
+    if (p_sys->keys)
+        DestroyKeyHandler (p_sys->keys);
     if (p_sys->conn)
         xcb_disconnect (p_sys->conn);
     free (p_sys);
@@ -435,6 +441,7 @@ static int Init (vout_thread_t *vout)
         /* XCB_CW_BACK_PIXEL */
         screen->black_pixel,
         /* XCB_CW_EVENT_MASK */
+        XCB_EVENT_MASK_KEY_PRESS |
         XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE |
         XCB_EVENT_MASK_POINTER_MOTION,
     };
@@ -534,7 +541,11 @@ static int Manage (vout_thread_t *vout)
     xcb_generic_event_t *ev;
 
     while ((ev = xcb_poll_for_event (p_sys->conn)) != NULL)
+    {
+        if (p_sys->keys && (ProcessKeyEvent (p_sys->keys, ev) == 0))
+            continue;
         ProcessEvent (vout, ev);
+    }
 
     if (xcb_connection_has_error (p_sys->conn))
     {
index e93f2ee..51f92c7 100644 (file)
@@ -22,3 +22,8 @@
 
 int CheckError (vout_thread_t *, const char *str, xcb_void_cookie_t);
 int ProcessEvent (vout_thread_t *, xcb_generic_event_t *);
+
+typedef struct key_handler_t key_handler_t;
+key_handler_t *CreateKeyHandler (vlc_object_t *, xcb_connection_t *);
+void DestroyKeyHandler (key_handler_t *);
+int ProcessKeyEvent (key_handler_t *, xcb_generic_event_t *);