1 /*****************************************************************************
2 * xcb.c: Global-Hotkey X11 using xcb handling for vlc
3 *****************************************************************************
4 * Copyright (C) 2009 the VideoLAN team
6 * Authors: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program 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.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21 *****************************************************************************/
26 #include <vlc_common.h>
27 #include <vlc_plugin.h>
28 #include <vlc_interface.h>
34 #include <xcb/xcb_keysyms.h>
35 #include <X11/keysym.h>
36 #include <X11/XF86keysym.h>
40 /*****************************************************************************
42 *****************************************************************************/
43 static int Open( vlc_object_t *p_this );
44 static void Close( vlc_object_t *p_this );
46 /*****************************************************************************
48 *****************************************************************************/
50 set_shortname( N_("Global Hotkeys") )
51 set_category( CAT_INTERFACE )
52 set_subcategory( SUBCAT_INTERFACE_HOTKEYS )
53 set_description( N_("Global Hotkeys interface") )
54 set_capability( "interface", 0 )
55 set_callbacks( Open, Close )
69 xcb_connection_t *p_connection;
71 xcb_key_symbols_t *p_symbols;
74 hotkey_mapping_t *p_map;
77 static void Mapping( intf_thread_t *p_intf );
78 static void Register( intf_thread_t *p_intf );
79 static void Unregister( intf_thread_t *p_intf );
80 static void *Thread( void *p_data );
82 /*****************************************************************************
84 *****************************************************************************/
85 static int Open( vlc_object_t *p_this )
87 intf_thread_t *p_intf = (intf_thread_t *)p_this;
90 p_intf->p_sys = p_sys = calloc( 1, sizeof(*p_sys) );
94 char *psz_display = var_CreateGetNonEmptyString( p_intf, "x11-display" );
97 p_sys->p_connection = xcb_connect( psz_display, &i_screen_default );
100 if( !p_sys->p_connection )
103 /* Get the root windows of the default screen */
104 const xcb_setup_t* xcbsetup = xcb_get_setup( p_sys->p_connection );
107 xcb_screen_iterator_t iter = xcb_setup_roots_iterator( xcbsetup );
108 for( int i = 0; i < i_screen_default; i++ )
112 xcb_screen_next( &iter );
116 p_sys->root = iter.data->root;
119 p_sys->p_symbols = xcb_key_symbols_alloc( p_sys->p_connection ); // FIXME
120 if( !p_sys->p_symbols )
126 if( vlc_clone( &p_sys->thread, Thread, p_intf, VLC_THREAD_PRIORITY_LOW ) )
128 Unregister( p_intf );
129 free( p_sys->p_map );
135 if( p_sys->p_symbols )
136 xcb_key_symbols_free( p_sys->p_symbols );
137 if( p_sys->p_connection )
138 xcb_disconnect( p_sys->p_connection );
143 /*****************************************************************************
145 *****************************************************************************/
146 static void Close( vlc_object_t *p_this )
148 intf_thread_t *p_intf = (intf_thread_t *)p_this;
149 intf_sys_t *p_sys = p_intf->p_sys;
151 vlc_cancel( p_sys->thread );
152 vlc_join( p_sys->thread, NULL );
154 Unregister( p_intf );
155 free( p_sys->p_map );
157 xcb_key_symbols_free( p_sys->p_symbols );
158 xcb_disconnect( p_sys->p_connection );
162 /*****************************************************************************
164 *****************************************************************************/
165 static unsigned GetModifier( xcb_connection_t *p_connection, xcb_key_symbols_t *p_symbols, xcb_keysym_t sym )
167 static const unsigned pi_mask[8] = {
168 XCB_MOD_MASK_SHIFT, XCB_MOD_MASK_LOCK, XCB_MOD_MASK_CONTROL,
169 XCB_MOD_MASK_1, XCB_MOD_MASK_2, XCB_MOD_MASK_3,
170 XCB_MOD_MASK_4, XCB_MOD_MASK_5
173 const xcb_keycode_t key = xcb_key_symbols_get_keycode( p_symbols, sym );
177 xcb_get_modifier_mapping_cookie_t r =
178 xcb_get_modifier_mapping( p_connection );
179 xcb_get_modifier_mapping_reply_t *p_map =
180 xcb_get_modifier_mapping_reply( p_connection, r, NULL );
184 xcb_keycode_t *p_keycode = xcb_get_modifier_mapping_keycodes( p_map );
189 for( int i = 0; i < 8; i++ )
191 for( int j = 0; j < p_map->keycodes_per_modifier; j++ )
193 if( p_keycode[i * p_map->keycodes_per_modifier + j] == key )
198 free( p_map ); // FIXME to check
201 static unsigned GetX11Modifier( xcb_connection_t *p_connection,
202 xcb_key_symbols_t *p_symbols, unsigned i_vlc )
206 if( i_vlc & KEY_MODIFIER_ALT )
207 i_mask |= GetModifier( p_connection, p_symbols, XK_Alt_L ) |
208 GetModifier( p_connection, p_symbols, XK_Alt_R );
209 if( i_vlc & KEY_MODIFIER_CTRL )
210 i_mask |= GetModifier( p_connection, p_symbols, XK_Control_L ) |
211 GetModifier( p_connection, p_symbols, XK_Control_R );
212 if( i_vlc & KEY_MODIFIER_SHIFT )
213 i_mask |= GetModifier( p_connection, p_symbols, XK_Shift_L ) |
214 GetModifier( p_connection, p_symbols, XK_Shift_R );
218 /* FIXME this table is also used by the vout */
224 } x11keys_to_vlckeys[] =
226 { XK_F1, KEY_F1 }, { XK_F2, KEY_F2 }, { XK_F3, KEY_F3 }, { XK_F4, KEY_F4 },
227 { XK_F5, KEY_F5 }, { XK_F6, KEY_F6 }, { XK_F7, KEY_F7 }, { XK_F8, KEY_F8 },
228 { XK_F9, KEY_F9 }, { XK_F10, KEY_F10 }, { XK_F11, KEY_F11 },
231 { XK_Return, KEY_ENTER },
232 { XK_KP_Enter, KEY_ENTER },
233 { XK_space, KEY_SPACE },
234 { XK_Escape, KEY_ESC },
236 { XK_Menu, KEY_MENU },
237 { XK_Left, KEY_LEFT },
238 { XK_Right, KEY_RIGHT },
240 { XK_Down, KEY_DOWN },
242 { XK_Home, KEY_HOME },
244 { XK_Page_Up, KEY_PAGEUP },
245 { XK_Page_Down, KEY_PAGEDOWN },
247 { XK_Insert, KEY_INSERT },
248 { XK_Delete, KEY_DELETE },
249 { XF86XK_AudioNext, KEY_MEDIA_NEXT_TRACK},
250 { XF86XK_AudioPrev, KEY_MEDIA_PREV_TRACK},
251 { XF86XK_AudioMute, KEY_VOLUME_MUTE },
252 { XF86XK_AudioLowerVolume, KEY_VOLUME_DOWN },
253 { XF86XK_AudioRaiseVolume, KEY_VOLUME_UP },
254 { XF86XK_AudioPlay, KEY_MEDIA_PLAY_PAUSE },
255 { XF86XK_AudioPause, KEY_MEDIA_PLAY_PAUSE },
259 static xcb_keysym_t GetX11Key( unsigned i_vlc )
261 for( int i = 0; x11keys_to_vlckeys[i].i_vlc != 0; i++ )
263 if( x11keys_to_vlckeys[i].i_vlc == i_vlc )
264 return x11keys_to_vlckeys[i].i_x11;
267 /* Copied from xcb, it seems that xcb use ascii code for ascii characters */
268 if( isascii( i_vlc ) )
271 return XK_VoidSymbol;
274 static void Mapping( intf_thread_t *p_intf )
276 static const xcb_keysym_t p_x11_modifier_ignored[] = {
283 intf_sys_t *p_sys = p_intf->p_sys;
288 /* Registering of Hotkeys */
289 for( const struct hotkey *p_hotkey = p_intf->p_libvlc->p_hotkeys;
290 p_hotkey->psz_action != NULL;
294 if( asprintf( &psz_hotkey, "global-%s", p_hotkey->psz_action ) < 0 )
297 const int i_vlc_action = p_hotkey->i_action;
298 const int i_vlc_key = config_GetInt( p_intf, psz_hotkey );
305 const xcb_keycode_t key = xcb_key_symbols_get_keycode(
306 p_sys->p_symbols, GetX11Key( i_vlc_key & ~KEY_MODIFIER ) );
307 const unsigned i_modifier = GetX11Modifier( p_sys->p_connection,
308 p_sys->p_symbols, i_vlc_key & KEY_MODIFIER );
310 const size_t max = sizeof(p_x11_modifier_ignored) /
311 sizeof(*p_x11_modifier_ignored);
312 for( unsigned int i = 0; i < max; i++ )
314 const unsigned i_ignored = GetModifier( p_sys->p_connection,
315 p_sys->p_symbols, p_x11_modifier_ignored[i] );
316 if( i != 0 && i_ignored == 0x00)
319 hotkey_mapping_t *p_map_old = p_sys->p_map;
320 p_sys->p_map = realloc( p_sys->p_map,
321 sizeof(*p_sys->p_map) * (p_sys->i_map+1) );
324 p_sys->p_map = p_map_old;
327 hotkey_mapping_t *p_map = &p_sys->p_map[p_sys->i_map++];
330 p_map->i_modifier = i_modifier|i_ignored;
331 p_map->i_action = i_vlc_action;
336 static void Register( intf_thread_t *p_intf )
338 intf_sys_t *p_sys = p_intf->p_sys;
340 for( int i = 0; i < p_sys->i_map; i++ )
342 const hotkey_mapping_t *p_map = &p_sys->p_map[i];
343 xcb_grab_key( p_sys->p_connection, true, p_sys->root,
344 p_map->i_modifier, p_map->i_x11,
345 XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC );
348 static void Unregister( intf_thread_t *p_intf )
350 intf_sys_t *p_sys = p_intf->p_sys;
352 for( int i = 0; i < p_sys->i_map; i++ )
354 const hotkey_mapping_t *p_map = &p_sys->p_map[i];
355 xcb_ungrab_key( p_sys->p_connection, p_map->i_x11, p_sys->root,
360 static void *Thread( void *p_data )
362 intf_thread_t *p_intf = p_data;
363 intf_sys_t *p_sys = p_intf->p_sys;
364 xcb_connection_t *p_connection = p_sys->p_connection;
366 int canc = vlc_savecancel();
369 xcb_flush( p_connection );
372 int fd = xcb_get_file_descriptor( p_connection );
375 /* Wait for x11 event */
376 vlc_restorecancel( canc );
377 struct pollfd fds = { .fd = fd, .events = POLLIN, };
378 if( poll( &fds, 1, -1 ) < 0 )
382 canc = vlc_savecancel();
385 canc = vlc_savecancel();
387 xcb_generic_event_t *p_event;
388 while( ( p_event = xcb_poll_for_event( p_connection ) ) )
390 if( ( p_event->response_type & 0x7f ) != XCB_KEY_PRESS )
396 xcb_key_press_event_t *e = (xcb_key_press_event_t *)p_event;
398 for( int i = 0; i < p_sys->i_map; i++ )
400 hotkey_mapping_t *p_map = &p_sys->p_map[i];
402 if( p_map->i_x11 == e->detail &&
403 p_map->i_modifier == e->state )
405 var_SetInteger( p_intf->p_libvlc, "key-action",