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>
33 #include <xcb/xcb_keysyms.h>
34 #include <X11/keysym.h>
35 #include <X11/XF86keysym.h>
39 /*****************************************************************************
41 *****************************************************************************/
42 static int Open( vlc_object_t *p_this );
43 static void Close( vlc_object_t *p_this );
45 /*****************************************************************************
47 *****************************************************************************/
49 set_shortname( N_("Global Hotkeys") )
50 set_category( CAT_INTERFACE )
51 set_subcategory( SUBCAT_INTERFACE_HOTKEYS )
52 set_description( N_("Global Hotkeys interface") )
53 set_capability( "interface", 0 )
54 set_callbacks( Open, Close )
55 add_shortcut( "globalhotkeys" )
60 xcb_keycode_t *p_keys;
69 xcb_connection_t *p_connection;
71 xcb_key_symbols_t *p_symbols;
74 hotkey_mapping_t *p_map;
77 static bool Mapping( intf_thread_t *p_intf );
78 static void Register( intf_thread_t *p_intf );
79 static void *Thread( void *p_data );
81 /*****************************************************************************
83 *****************************************************************************/
84 static int Open( vlc_object_t *p_this )
86 intf_thread_t *p_intf = (intf_thread_t *)p_this;
88 int ret = VLC_EGENERIC;
90 p_intf->p_sys = p_sys = calloc( 1, sizeof(*p_sys) );
95 p_sys->p_connection = xcb_connect( NULL, &i_screen_default );
97 if( xcb_connection_has_error( p_sys->p_connection ) )
100 /* Get the root windows of the default screen */
101 const xcb_setup_t* xcbsetup = xcb_get_setup( p_sys->p_connection );
104 xcb_screen_iterator_t iter = xcb_setup_roots_iterator( xcbsetup );
105 for( int i = 0; i < i_screen_default; i++ )
109 xcb_screen_next( &iter );
113 p_sys->root = iter.data->root;
116 p_sys->p_symbols = xcb_key_symbols_alloc( p_sys->p_connection ); // FIXME
117 if( !p_sys->p_symbols )
120 if( !Mapping( p_intf ) )
123 p_intf->p_sys = NULL; /* for Close() */
128 if( vlc_clone( &p_sys->thread, Thread, p_intf, VLC_THREAD_PRIORITY_LOW ) )
132 free( p_sys->p_map->p_keys );
133 free( p_sys->p_map );
140 if( p_sys->p_symbols )
141 xcb_key_symbols_free( p_sys->p_symbols );
142 xcb_disconnect( p_sys->p_connection );
147 /*****************************************************************************
149 *****************************************************************************/
150 static void Close( vlc_object_t *p_this )
152 intf_thread_t *p_intf = (intf_thread_t *)p_this;
153 intf_sys_t *p_sys = p_intf->p_sys;
156 return; /* if we were running disabled */
158 vlc_cancel( p_sys->thread );
159 vlc_join( p_sys->thread, NULL );
163 free( p_sys->p_map->p_keys );
164 free( p_sys->p_map );
166 xcb_key_symbols_free( p_sys->p_symbols );
167 xcb_disconnect( p_sys->p_connection );
171 /*****************************************************************************
173 *****************************************************************************/
174 static unsigned GetModifier( xcb_connection_t *p_connection, xcb_key_symbols_t *p_symbols, xcb_keysym_t sym )
176 static const unsigned pi_mask[8] = {
177 XCB_MOD_MASK_SHIFT, XCB_MOD_MASK_LOCK, XCB_MOD_MASK_CONTROL,
178 XCB_MOD_MASK_1, XCB_MOD_MASK_2, XCB_MOD_MASK_3,
179 XCB_MOD_MASK_4, XCB_MOD_MASK_5
183 return 0; /* no modifier */
185 xcb_get_modifier_mapping_cookie_t r =
186 xcb_get_modifier_mapping( p_connection );
187 xcb_get_modifier_mapping_reply_t *p_map =
188 xcb_get_modifier_mapping_reply( p_connection, r, NULL );
192 xcb_keycode_t *p_keys = xcb_key_symbols_get_keycode( p_symbols, sym );
197 bool no_modifier = true;
198 while( p_keys[i] != XCB_NO_SYMBOL )
211 xcb_keycode_t *p_keycode = xcb_get_modifier_mapping_keycodes( p_map );
215 for( int i = 0; i < 8; i++ )
216 for( int j = 0; j < p_map->keycodes_per_modifier; j++ )
217 for( int k = 0; p_keys[k] != XCB_NO_SYMBOL; k++ )
218 if( p_keycode[i*p_map->keycodes_per_modifier + j] == p_keys[k])
227 free( p_map ); // FIXME to check
232 static unsigned GetX11Modifier( xcb_connection_t *p_connection,
233 xcb_key_symbols_t *p_symbols, unsigned i_vlc )
237 if( i_vlc & KEY_MODIFIER_ALT )
238 i_mask |= GetModifier( p_connection, p_symbols, XK_Alt_L ) |
239 GetModifier( p_connection, p_symbols, XK_Alt_R );
240 if( i_vlc & KEY_MODIFIER_CTRL )
241 i_mask |= GetModifier( p_connection, p_symbols, XK_Control_L ) |
242 GetModifier( p_connection, p_symbols, XK_Control_R );
243 if( i_vlc & KEY_MODIFIER_SHIFT )
244 i_mask |= GetModifier( p_connection, p_symbols, XK_Shift_L ) |
245 GetModifier( p_connection, p_symbols, XK_Shift_R );
249 /* FIXME this table is also used by the vout */
255 } x11keys_to_vlckeys[] =
257 #include "../../video_output/xcb/xcb_keysym.h"
260 static xcb_keysym_t GetX11Key( unsigned i_vlc )
262 /* X11 and VLC use ASCII for printable ASCII characters */
263 if( i_vlc >= 32 && i_vlc <= 127 )
266 for( int i = 0; x11keys_to_vlckeys[i].i_vlc != 0; i++ )
268 if( x11keys_to_vlckeys[i].i_vlc == i_vlc )
269 return x11keys_to_vlckeys[i].i_x11;
272 return XK_VoidSymbol;
275 static bool Mapping( intf_thread_t *p_intf )
277 static const xcb_keysym_t p_x11_modifier_ignored[] = {
284 intf_sys_t *p_sys = p_intf->p_sys;
290 /* Registering of Hotkeys */
291 for( const struct hotkey *p_hotkey = p_intf->p_libvlc->p_hotkeys;
292 p_hotkey->psz_action != NULL;
295 char varname[12 + strlen( p_hotkey->psz_action )];
296 sprintf( varname, "global-key-%s", p_hotkey->psz_action );
298 char *key = var_InheritString( p_intf, varname );
302 uint_fast32_t i_vlc_key = vlc_str2keycode( key );
304 if( i_vlc_key == KEY_UNSET )
307 xcb_keycode_t *p_keys = xcb_key_symbols_get_keycode(
308 p_sys->p_symbols, GetX11Key( i_vlc_key & ~KEY_MODIFIER ) );
312 const unsigned i_modifier = GetX11Modifier( p_sys->p_connection,
313 p_sys->p_symbols, i_vlc_key & KEY_MODIFIER );
315 const size_t max = sizeof(p_x11_modifier_ignored) /
316 sizeof(*p_x11_modifier_ignored);
317 for( unsigned int i = 0; i < max; i++ )
319 const unsigned i_ignored = GetModifier( p_sys->p_connection,
320 p_sys->p_symbols, p_x11_modifier_ignored[i] );
321 if( i != 0 && i_ignored == 0)
324 hotkey_mapping_t *p_map_old = p_sys->p_map;
325 p_sys->p_map = realloc( p_sys->p_map,
326 sizeof(*p_sys->p_map) * (p_sys->i_map+1) );
329 p_sys->p_map = p_map_old;
332 hotkey_mapping_t *p_map = &p_sys->p_map[p_sys->i_map++];
334 p_map->p_keys = p_keys;
335 p_map->i_modifier = i_modifier|i_ignored;
336 p_map->i_vlc = i_vlc_key;
343 static void Register( intf_thread_t *p_intf )
345 intf_sys_t *p_sys = p_intf->p_sys;
347 for( int i = 0; i < p_sys->i_map; i++ )
349 const hotkey_mapping_t *p_map = &p_sys->p_map[i];
350 for( int j = 0; p_map->p_keys[j] != XCB_NO_SYMBOL; j++ )
352 xcb_grab_key( p_sys->p_connection, true, p_sys->root,
353 p_map->i_modifier, p_map->p_keys[j],
354 XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC );
359 static void *Thread( void *p_data )
361 intf_thread_t *p_intf = p_data;
362 intf_sys_t *p_sys = p_intf->p_sys;
363 xcb_connection_t *p_connection = p_sys->p_connection;
365 int canc = vlc_savecancel();
368 xcb_flush( p_connection );
371 int fd = xcb_get_file_descriptor( p_connection );
374 /* Wait for x11 event */
375 vlc_restorecancel( canc );
376 struct pollfd fds = { .fd = fd, .events = POLLIN, };
377 if( poll( &fds, 1, -1 ) < 0 )
381 canc = vlc_savecancel();
384 canc = vlc_savecancel();
386 xcb_generic_event_t *p_event;
387 while( ( p_event = xcb_poll_for_event( p_connection ) ) )
389 if( ( p_event->response_type & 0x7f ) != XCB_KEY_PRESS )
395 xcb_key_press_event_t *e = (xcb_key_press_event_t *)p_event;
397 for( int i = 0; i < p_sys->i_map; i++ )
399 hotkey_mapping_t *p_map = &p_sys->p_map[i];
401 for( int j = 0; p_map->p_keys[j] != XCB_NO_SYMBOL; j++ )
402 if( p_map->p_keys[j] == e->detail &&
403 p_map->i_modifier == e->state )
405 var_SetInteger( p_intf->p_libvlc, "global-key-pressed",