1 /*****************************************************************************
2 * x11.c: Global-Hotkey X11 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 *****************************************************************************/
27 #include <X11/keysym.h>
28 #include <X11/Xutil.h>
29 #include <X11/XF86keysym.h>
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
34 #include <vlc_interface.h>
37 /*****************************************************************************
39 *****************************************************************************/
40 static int Open( vlc_object_t *p_this );
41 static void Close( vlc_object_t *p_this );
43 /*****************************************************************************
45 *****************************************************************************/
47 set_shortname( N_("Global Hotkeys") )
48 set_category( CAT_INTERFACE )
49 set_subcategory( SUBCAT_INTERFACE_HOTKEYS )
50 set_description( N_("Global Hotkeys interface") )
51 set_capability( "interface", 0 )
52 set_callbacks( Open, Close )
68 hotkey_mapping_t *p_map;
71 static void Mapping( intf_thread_t *p_intf );
72 static void Register( intf_thread_t *p_intf );
73 static void Unregister( intf_thread_t *p_intf );
74 static void *Thread( void *p_data );
75 static int X11ErrorHandler( Display *, XErrorEvent * );
77 /*****************************************************************************
79 *****************************************************************************/
80 static int Open( vlc_object_t *p_this )
82 intf_thread_t *p_intf = (intf_thread_t *)p_this;
84 char *psz_display = var_CreateGetNonEmptyString( p_intf, "x11-display" );
86 Display *p_display = XOpenDisplay( psz_display );
90 XSetErrorHandler( X11ErrorHandler );
92 p_intf->p_sys = p_sys = malloc( sizeof(*p_sys) );
95 XCloseDisplay( p_display );
98 p_sys->p_display = p_display;
102 if( vlc_clone( &p_sys->thread, Thread, p_intf, VLC_THREAD_PRIORITY_LOW ) )
104 Unregister( p_intf );
105 XCloseDisplay( p_display );
106 free( p_sys->p_map );
113 /*****************************************************************************
115 *****************************************************************************/
116 static void Close( vlc_object_t *p_this )
118 intf_thread_t *p_intf = (intf_thread_t *)p_this;
119 intf_sys_t *p_sys = p_intf->p_sys;
121 vlc_cancel( p_sys->thread );
122 vlc_join( p_sys->thread, NULL );
124 Unregister( p_intf );
125 XCloseDisplay( p_sys->p_display );
126 free( p_sys->p_map );
131 /*****************************************************************************
133 *****************************************************************************/
134 static int X11ErrorHandler( Display *p_display, XErrorEvent *p_event )
139 XGetErrorText( p_display, p_event->error_code, psz_txt, sizeof(psz_txt) );
141 "[????????] globalhotkeys interface error: X11 request %u.%u failed "
142 "with error code %u:\n %s\n",
143 p_event->request_code, p_event->minor_code, p_event->error_code, psz_txt );
145 VLC_UNUSED(p_display); VLC_UNUSED(p_event);
150 static unsigned GetModifier( Display *p_display, KeySym sym )
152 static const unsigned pi_mask[8] = {
153 ShiftMask, LockMask, ControlMask, Mod1Mask,
154 Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask
157 const KeyCode key = XKeysymToKeycode( p_display, sym );
161 XModifierKeymap *p_map = XGetModifierMapping( p_display );
166 for( int i = 0; i < 8 * p_map->max_keypermod; i++ )
168 if( p_map->modifiermap[i] == key )
170 i_mask = pi_mask[i / p_map->max_keypermod];
175 XFreeModifiermap( p_map );
178 static unsigned GetX11Modifier( Display *p_display, unsigned i_vlc )
182 if( i_vlc & KEY_MODIFIER_ALT )
183 i_mask |= GetModifier( p_display, XK_Alt_L ) |
184 GetModifier( p_display, XK_Alt_R );
185 if( i_vlc & KEY_MODIFIER_CTRL )
186 i_mask |= GetModifier( p_display, XK_Control_L ) |
187 GetModifier( p_display, XK_Control_R );
188 if( i_vlc & KEY_MODIFIER_SHIFT )
189 i_mask |= GetModifier( p_display, XK_Shift_L ) |
190 GetModifier( p_display, XK_Shift_R );
194 /* FIXME this table is also used by the vout */
200 } x11keys_to_vlckeys[] =
202 { XK_F1, KEY_F1 }, { XK_F2, KEY_F2 }, { XK_F3, KEY_F3 }, { XK_F4, KEY_F4 },
203 { XK_F5, KEY_F5 }, { XK_F6, KEY_F6 }, { XK_F7, KEY_F7 }, { XK_F8, KEY_F8 },
204 { XK_F9, KEY_F9 }, { XK_F10, KEY_F10 }, { XK_F11, KEY_F11 },
207 { XK_Return, KEY_ENTER },
208 { XK_KP_Enter, KEY_ENTER },
209 { XK_space, KEY_SPACE },
210 { XK_Escape, KEY_ESC },
212 { XK_Menu, KEY_MENU },
213 { XK_Left, KEY_LEFT },
214 { XK_Right, KEY_RIGHT },
216 { XK_Down, KEY_DOWN },
218 { XK_Home, KEY_HOME },
220 { XK_Page_Up, KEY_PAGEUP },
221 { XK_Page_Down, KEY_PAGEDOWN },
223 { XK_Insert, KEY_INSERT },
224 { XK_Delete, KEY_DELETE },
225 { XF86XK_AudioNext, KEY_MEDIA_NEXT_TRACK},
226 { XF86XK_AudioPrev, KEY_MEDIA_PREV_TRACK},
227 { XF86XK_AudioMute, KEY_VOLUME_MUTE },
228 { XF86XK_AudioLowerVolume, KEY_VOLUME_DOWN },
229 { XF86XK_AudioRaiseVolume, KEY_VOLUME_UP },
230 { XF86XK_AudioPlay, KEY_MEDIA_PLAY_PAUSE },
231 { XF86XK_AudioPause, KEY_MEDIA_PLAY_PAUSE },
235 static KeySym GetX11Key( unsigned i_vlc )
237 for( int i = 0; x11keys_to_vlckeys[i].i_vlc != 0; i++ )
239 if( x11keys_to_vlckeys[i].i_vlc == i_vlc )
240 return x11keys_to_vlckeys[i].i_x11;
246 return XStringToKeysym( psz_key );
249 static void Mapping( intf_thread_t *p_intf )
251 static const KeySym p_x11_modifier_ignored[] = {
258 intf_sys_t *p_sys = p_intf->p_sys;
263 /* Registering of Hotkeys */
264 for( struct hotkey *p_hotkey = p_intf->p_libvlc->p_hotkeys;
265 p_hotkey->psz_action != NULL;
269 if( asprintf( &psz_hotkey, "global-%s", p_hotkey->psz_action ) < 0 )
272 const int i_vlc_action = p_hotkey->i_action;
273 const int i_vlc_key = config_GetInt( p_intf, psz_hotkey );
280 const KeyCode key = XKeysymToKeycode( p_sys->p_display,
281 GetX11Key( i_vlc_key & ~KEY_MODIFIER ) );
282 const unsigned i_modifier = GetX11Modifier( p_sys->p_display, i_vlc_key & KEY_MODIFIER );
284 for( int j = 0; j < sizeof(p_x11_modifier_ignored)/sizeof(*p_x11_modifier_ignored); j++ )
286 const unsigned i_ignored = GetModifier( p_sys->p_display, p_x11_modifier_ignored[j] );
287 if( j != 0 && i_ignored == 0x00)
290 hotkey_mapping_t *p_map_old = p_sys->p_map;
291 p_sys->p_map = realloc( p_sys->p_map, sizeof(*p_sys->p_map) * (p_sys->i_map+1) );
294 p_sys->p_map = p_map_old;
297 hotkey_mapping_t *p_map = &p_sys->p_map[p_sys->i_map++];
300 p_map->i_modifier = i_modifier|i_ignored;
301 p_map->i_action = i_vlc_action;
306 static void Register( intf_thread_t *p_intf )
308 intf_sys_t *p_sys = p_intf->p_sys;
310 for( int i = 0; i < p_sys->i_map; i++ )
312 hotkey_mapping_t *p_map = &p_sys->p_map[i];
313 XGrabKey( p_sys->p_display, p_map->i_x11, p_map->i_modifier,
314 DefaultRootWindow( p_sys->p_display ), True, GrabModeAsync, GrabModeAsync );
317 static void Unregister( intf_thread_t *p_intf )
319 intf_sys_t *p_sys = p_intf->p_sys;
321 for( int i = 0; i < p_sys->i_map; i++ )
323 hotkey_mapping_t *p_map = &p_sys->p_map[i];
324 XUngrabKey( p_sys->p_display, p_map->i_x11, p_map->i_modifier,
325 DefaultRootWindow( p_sys->p_display ) );
329 static void *Thread( void *p_data )
331 intf_thread_t *p_intf = p_data;
332 intf_sys_t *p_sys = p_intf->p_sys;
333 Display *p_display = p_sys->p_display;
335 int canc = vlc_savecancel();
344 int fd = ConnectionNumber( p_display );
347 /* Wait for x11 event */
348 vlc_restorecancel( canc );
349 struct pollfd fds = { .fd = fd, .events = POLLIN, };
350 if( poll( &fds, 1, -1 ) < 0 )
354 canc = vlc_savecancel();
357 canc = vlc_savecancel();
359 while( XPending( p_display ) > 0 )
363 XNextEvent( p_display, &e );
364 if( e.type != KeyPress )
367 for( int i = 0; i < p_sys->i_map; i++ )
369 hotkey_mapping_t *p_map = &p_sys->p_map[i];
371 if( p_map->i_x11 == e.xkey.keycode &&
372 p_map->i_modifier == e.xkey.state )
374 var_SetInteger( p_intf->p_libvlc, "key-action", p_map->i_action );