XCB window: add missing numpad keys
[vlc.git] / modules / video_output / xcb / keys.c
1 /**
2  * @file keys.c
3  * @brief X C Bindings VLC keyboard event handling
4  */
5 /*****************************************************************************
6  * Copyright © 2009 Rémi Denis-Courmont
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public License
10  * as published by the Free Software Foundation; either version 2.1
11  * of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21  ****************************************************************************/
22
23 #ifdef HAVE_CONFIG_H
24 # include <config.h>
25 #endif
26
27 #include <stdlib.h>
28 #include <inttypes.h>
29 #include <ctype.h>
30 #include <assert.h>
31
32 #include <xcb/xcb.h>
33 #include <xcb/xcb_keysyms.h>
34 #include <X11/keysym.h>
35 #include <X11/XF86keysym.h>
36
37 #include <vlc_common.h>
38 #include <vlc_keys.h>
39
40 #include "xcb_vlc.h"
41
42 struct key_handler_t
43 {
44     vlc_object_t      *obj;
45     xcb_key_symbols_t *syms;
46 };
47
48 /**
49  * Create an X11 key event handler for a VLC window.
50  * The caller shall arrange receiving applicable X11 events, and pass them to
51  * ProcessKeyEvent() later.
52  *
53  * @param obj VLC object owning an X window
54  * @param conn XCB connection to the X server (to fetch key mappings)
55  * @return NULL on error, or a key handling context.
56  */
57 key_handler_t *CreateKeyHandler (vlc_object_t *obj, xcb_connection_t *conn)
58 {
59     key_handler_t *ctx = malloc (sizeof (*ctx));
60     if (!ctx)
61         return NULL;
62
63     ctx->obj = obj;
64     ctx->syms = xcb_key_symbols_alloc (conn);
65     return ctx;
66 }
67
68 void DestroyKeyHandler (key_handler_t *ctx)
69 {
70     xcb_key_symbols_free (ctx->syms);
71     free (ctx);
72 }
73
74 static int keysymcmp (const void *pa, const void *pb)
75 {
76     int a = *(const xcb_keysym_t *)pa;
77     int b = *(const xcb_keysym_t *)pb;
78
79     return a - b;
80 }
81
82 static uint_fast32_t ConvertKeySym (xcb_keysym_t sym)
83 {
84     static const struct
85     {
86         xcb_keysym_t x11;
87         uint32_t vlc;
88     } *res, tab[] = {
89     /* This list MUST be in XK_* incremental order (see keysymdef.h),
90      * so that binary search works.
91      * Multiple X keys can match the same VLC key.
92      * X key symbols must be in the first column of the struct. */
93         { XK_BackSpace,     KEY_BACKSPACE, },
94         { XK_Tab,           KEY_TAB, },
95         { XK_Return,        KEY_ENTER, },
96         { XK_Escape,        KEY_ESC, },
97         { XK_Home,          KEY_HOME, },
98         { XK_Left,          KEY_LEFT, },
99         { XK_Up,            KEY_UP, },
100         { XK_Right,         KEY_RIGHT, },
101         { XK_Down,          KEY_DOWN, },
102         { XK_Page_Up,       KEY_PAGEUP, },
103         { XK_Page_Down,     KEY_PAGEDOWN, },
104         { XK_End,           KEY_END, },
105         { XK_Begin,         KEY_HOME, },
106         { XK_Insert,        KEY_INSERT, },
107         { XK_Menu,          KEY_MENU },
108
109         /* Numeric pad keys */
110         { XK_KP_Space,      ' ', },
111         { XK_KP_Tab,        KEY_TAB, },
112         { XK_KP_Enter,      KEY_ENTER, },
113         { XK_KP_F1,         KEY_F1, },
114         { XK_KP_F2,         KEY_F2, },
115         { XK_KP_F3,         KEY_F3, },
116         { XK_KP_F4,         KEY_F4, },
117         { XK_KP_Home,       KEY_HOME, },
118         { XK_KP_Left,       KEY_LEFT, },
119         { XK_KP_Up,         KEY_UP, },
120         { XK_KP_Right,      KEY_RIGHT, },
121         { XK_KP_Down,       KEY_DOWN, },
122         { XK_KP_Page_Up,    KEY_PAGEUP, },
123         { XK_KP_Page_Down,  KEY_PAGEDOWN, },
124         { XK_KP_End,        KEY_END, },
125         { XK_KP_Begin,      KEY_HOME, }, /* KP middle (5 without numlock) */
126         { XK_KP_Insert,     KEY_INSERT, },
127         { XK_KP_Delete,     KEY_DELETE, },
128         { XK_KP_Equal,      '=', },
129         { XK_KP_Multiply,   '*', },
130         { XK_KP_Add,        '+', },
131         { XK_KP_Separator,  ',', },
132         { XK_KP_Subtract,   '-', },
133         { XK_KP_Decimal,    ',', }, /* FIXME: I don't know that key */
134         { XK_KP_Divide,     '/', },
135         { XK_KP_0,          '0', },
136         { XK_KP_1,          '1', },
137         { XK_KP_2,          '2', },
138         { XK_KP_3,          '3', },
139         { XK_KP_4,          '4', },
140         { XK_KP_5,          '5', },
141         { XK_KP_6,          '6', },
142         { XK_KP_7,          '7', },
143         { XK_KP_8,          '8', },
144         { XK_KP_9,          '9', },
145
146         { XK_F1,            KEY_F1, },
147         { XK_F2,            KEY_F2, },
148         { XK_F3,            KEY_F3, },
149         { XK_F4,            KEY_F4, },
150         { XK_F5,            KEY_F5, },
151         { XK_F6,            KEY_F6, },
152         { XK_F7,            KEY_F7, },
153         { XK_F8,            KEY_F8, },
154         { XK_F9,            KEY_F9, },
155         { XK_F10,           KEY_F10, },
156         { XK_F11,           KEY_F11, },
157         { XK_F12,           KEY_F12, },
158         { XK_Delete,        KEY_DELETE, },
159
160         /* XFree86 extensions */
161         { XF86XK_AudioLowerVolume, KEY_VOLUME_DOWN, },
162         { XF86XK_AudioMute,        KEY_VOLUME_MUTE, },
163         { XF86XK_AudioRaiseVolume, KEY_VOLUME_UP, },
164         { XF86XK_AudioPlay,        KEY_MEDIA_PLAY_PAUSE, },
165         { XF86XK_AudioStop,        KEY_MEDIA_STOP, },
166         { XF86XK_AudioPrev,        KEY_MEDIA_PREV_TRACK, },
167         { XF86XK_AudioNext,        KEY_MEDIA_NEXT_TRACK, },
168         { XF86XK_HomePage,         KEY_BROWSER_HOME, },
169         { XF86XK_Search,           KEY_BROWSER_SEARCH, },
170         { XF86XK_Back,             KEY_BROWSER_BACK, },
171         { XF86XK_Forward,          KEY_BROWSER_FORWARD, },
172         { XF86XK_Stop,             KEY_BROWSER_STOP, },
173         { XF86XK_Refresh,          KEY_BROWSER_REFRESH, },
174         { XF86XK_Favorites,        KEY_BROWSER_FAVORITES, },
175         { XF86XK_AudioPause,       KEY_MEDIA_PLAY_PAUSE, },
176         { XF86XK_Reload,           KEY_BROWSER_REFRESH, },
177     };
178
179     /* X11 and VLC both use the ASCII code for printable ASCII characters. */
180     if (isascii(sym))
181         return sym;
182
183     /* Special keys */
184     res = bsearch (&sym, tab, sizeof (tab) / sizeof (tab[0]), sizeof (tab[0]),
185                    keysymcmp);
186     if (res != NULL)
187         return res->vlc;
188
189     return KEY_UNSET;
190 }
191
192
193 /**
194  * Process an X11 event, convert into VLC hotkey event if applicable.
195  *
196  * @param ctx key handler created with CreateKeyHandler()
197  * @param ev XCB event to process
198  * @return 0 if the event was handled and free()'d, non-zero otherwise
199  */
200 int ProcessKeyEvent (key_handler_t *ctx, xcb_generic_event_t *ev)
201 {
202     assert (ctx);
203
204     switch (ev->response_type & 0x7f)
205     {
206         case XCB_KEY_PRESS:
207         {
208             xcb_key_press_event_t *e = (xcb_key_press_event_t *)ev;
209             xcb_keysym_t sym = xcb_key_press_lookup_keysym (ctx->syms, e, 0);
210             uint_fast32_t vk = ConvertKeySym (sym);
211
212             msg_Dbg (ctx->obj, "key: 0x%08"PRIxFAST32, vk);
213             if (vk == KEY_UNSET)
214                 break;
215             if (e->state & XCB_MOD_MASK_SHIFT)
216                 vk |= KEY_MODIFIER_SHIFT;
217             if (e->state & XCB_MOD_MASK_CONTROL)
218                 vk |= KEY_MODIFIER_CTRL;
219             if (e->state & XCB_MOD_MASK_1)
220                 vk |= KEY_MODIFIER_ALT;
221             if (e->state & XCB_MOD_MASK_4)
222                 vk |= KEY_MODIFIER_COMMAND;
223             var_SetInteger (ctx->obj->p_libvlc, "key-pressed", vk);
224             break;
225         }
226
227         case XCB_KEY_RELEASE:
228             break;
229
230         case XCB_MAPPING_NOTIFY:
231         {
232             xcb_mapping_notify_event_t *e = (xcb_mapping_notify_event_t *)ev;
233             msg_Dbg (ctx->obj, "refreshing keyboard mapping");
234             xcb_refresh_keyboard_mapping (ctx->syms, e);
235             break;
236         }
237
238         default:
239             return -1;
240     }
241
242     free (ev);
243     return 0;
244 }