From b732089382143974d2aff65904e0ec6cd91a2f50 Mon Sep 17 00:00:00 2001 From: Laurent Aimar Date: Sun, 25 Jan 2009 18:41:36 +0100 Subject: [PATCH] Added x11 global hotkeys support (linux). --- configure.ac | 3 + include/vlc_plugin.h | 2 +- modules/control/Modules.am | 1 - modules/control/globalhotkeys/Modules.am | 2 + modules/control/globalhotkeys/win32.c | 2 +- modules/control/globalhotkeys/x11.c | 363 +++++++++++++++++++++++ 6 files changed, 370 insertions(+), 3 deletions(-) create mode 100644 modules/control/globalhotkeys/x11.c diff --git a/configure.ac b/configure.ac index 8b619bdf1a..bbfa7142e3 100644 --- a/configure.ac +++ b/configure.ac @@ -3886,6 +3886,9 @@ if test "${enable_x11}" != "no" && VLC_ADD_PLUGIN([panoramix]) VLC_ADD_LIBS([panoramix],[${X_LIBS} ${X_PRE_LIBS} -lX11]) VLC_ADD_CPPFLAGS([panoramix],[${X_CFLAGS}]) + VLC_ADD_PLUGIN([globalhotkeys]) + VLC_ADD_LIBS([globalhotkeys],[${X_LIBS} ${X_PRE_LIBS} -lX11]) + VLC_ADD_CPPFLAGS([globalhotkeys],[${X_CFLAGS}]) AC_CHECK_LIB(Xext, XShmAttach, [ VLC_ADD_PLUGIN([x11]) VLC_ADD_LIBS([x11],[${X_LIBS} ${X_PRE_LIBS} -lX11 -lXext]) diff --git a/include/vlc_plugin.h b/include/vlc_plugin.h index a1e550ddb2..197fa1ec0c 100644 --- a/include/vlc_plugin.h +++ b/include/vlc_plugin.h @@ -353,7 +353,7 @@ enum vlc_config_properties add_int_inner( CONFIG_ITEM_INTEGER, name, text, longtext, advc, \ p_callback, value ) -#ifndef WIN32 +#if !defined(WIN32) && !defined(SYS_LINUX) #define add_key( name, value, p_callback, text, longtext, advc ) \ add_int_inner( CONFIG_ITEM_KEY, name, text, longtext, advc, p_callback, \ value ) diff --git a/modules/control/Modules.am b/modules/control/Modules.am index a34a26ee45..61d29881a7 100644 --- a/modules/control/Modules.am +++ b/modules/control/Modules.am @@ -5,7 +5,6 @@ SOURCES_telnet = telnet.c SOURCES_netsync = netsync.c SOURCES_ntservice = ntservice.c SOURCES_hotkeys = hotkeys.c -SOURCES_globalhotkeys = globalhotkeys.c SOURCES_lirc = lirc.c SOURCES_rc = rc.c SOURCES_dbus = dbus.c dbus.h diff --git a/modules/control/globalhotkeys/Modules.am b/modules/control/globalhotkeys/Modules.am index f62edc0098..4254cba2ed 100644 --- a/modules/control/globalhotkeys/Modules.am +++ b/modules/control/globalhotkeys/Modules.am @@ -1,5 +1,7 @@ if HAVE_WIN32 globalhotkeys_extra = win32.c +else +globalhotkeys_extra = x11.c endif SOURCES_globalhotkeys = \ diff --git a/modules/control/globalhotkeys/win32.c b/modules/control/globalhotkeys/win32.c index af88dd2f6e..e326762d4f 100644 --- a/modules/control/globalhotkeys/win32.c +++ b/modules/control/globalhotkeys/win32.c @@ -1,5 +1,5 @@ /***************************************************************************** - * globalhotkeys.c: Global-Hotkey handling for vlc + * win32.c: Global-Hotkey WIN32 handling for vlc ***************************************************************************** * Copyright (C) 2008-2009 the VideoLAN team * diff --git a/modules/control/globalhotkeys/x11.c b/modules/control/globalhotkeys/x11.c new file mode 100644 index 0000000000..74f9bcb525 --- /dev/null +++ b/modules/control/globalhotkeys/x11.c @@ -0,0 +1,363 @@ +/***************************************************************************** + * x11.c: Global-Hotkey X11 handling for vlc + ***************************************************************************** + * Copyright (C) 2009 the VideoLAN team + * + * Authors: Laurent Aimar + * + * This program 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 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/***************************************************************************** + * Local prototypes + *****************************************************************************/ +static int Open( vlc_object_t *p_this ); +static void Close( vlc_object_t *p_this ); + +/***************************************************************************** + * Module descriptor + *****************************************************************************/ +vlc_module_begin(); + set_shortname( _("Global Hotkeys") ); + set_category( CAT_INTERFACE ); + set_subcategory( SUBCAT_INTERFACE_HOTKEYS ); + set_description( _("Global Hotkeys interface") ); + set_capability( "interface", 0 ); + set_callbacks( Open, Close ); +vlc_module_end(); + +typedef struct +{ + KeyCode i_x11; + unsigned i_modifier; + int i_action; +} hotkey_mapping_t; + +struct intf_sys_t +{ + vlc_thread_t thread; + Display *p_display; + + int i_map; + hotkey_mapping_t *p_map; +}; + +static void Mapping( intf_thread_t *p_intf ); +static void Register( intf_thread_t *p_intf ); +static void Unregister( intf_thread_t *p_intf ); +static void *Thread( void *p_data ); + +/***************************************************************************** + * Open: + *****************************************************************************/ +static int Open( vlc_object_t *p_this ) +{ + intf_thread_t *p_intf = (intf_thread_t *)p_this; + intf_sys_t *p_sys; + + Display *p_display = XOpenDisplay( NULL ); + if( !p_display ) + return VLC_EGENERIC; + + p_intf->p_sys = p_sys = malloc( sizeof(*p_sys) ); + if( !p_sys ) + { + XCloseDisplay( p_display ); + return VLC_ENOMEM; + } + p_sys->p_display = p_display; + Mapping( p_intf ); + Register( p_intf ); + + if( vlc_clone( &p_sys->thread, Thread, p_intf, VLC_THREAD_PRIORITY_LOW ) ) + { + Unregister( p_intf ); + XCloseDisplay( p_display ); + free( p_sys->p_map ); + free( p_sys ); + return VLC_ENOMEM; + } + return VLC_SUCCESS; +} + +/***************************************************************************** + * Close: + *****************************************************************************/ +static void Close( vlc_object_t *p_this ) +{ + intf_thread_t *p_intf = (intf_thread_t *)p_this; + intf_sys_t *p_sys = p_intf->p_sys; + + vlc_cancel( p_sys->thread ); + vlc_join( p_sys->thread, NULL ); + + Unregister( p_intf ); + XCloseDisplay( p_sys->p_display ); + free( p_sys->p_map ); + + free( p_sys ); +} + +/***************************************************************************** + * Thread: + *****************************************************************************/ +static unsigned GetModifier( Display *p_display, KeySym sym ) +{ + static const unsigned pi_mask[8] = { + ShiftMask, LockMask, ControlMask, Mod1Mask, + Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask + }; + + const KeyCode key = XKeysymToKeycode( p_display, sym ); + if( key == 0 ) + return 0; + + XModifierKeymap *p_map = XGetModifierMapping( p_display ); + if( !p_map ) + return 0; + + unsigned i_mask = 0; + for( int i = 0; i < 8 * p_map->max_keypermod; i++ ) + { + if( p_map->modifiermap[i] == key ) + { + i_mask = pi_mask[i / p_map->max_keypermod]; + break; + } + } + + XFreeModifiermap( p_map ); + return i_mask; +} +static unsigned GetX11Modifier( Display *p_display, unsigned i_vlc ) +{ + unsigned i_mask = 0; + + if( i_vlc & KEY_MODIFIER_ALT ) + i_mask |= GetModifier( p_display, XK_Alt_L ) | + GetModifier( p_display, XK_Alt_R ); + if( i_vlc & KEY_MODIFIER_CTRL ) + i_mask |= GetModifier( p_display, XK_Control_L ) | + GetModifier( p_display, XK_Control_R ); + if( i_vlc & KEY_MODIFIER_SHIFT ) + i_mask |= GetModifier( p_display, XK_Shift_L ) | + GetModifier( p_display, XK_Shift_R ); + return i_mask; +} + +/* FIXME this table is also used by the vout */ +static const struct +{ + KeySym i_x11; + unsigned i_vlc; + +} x11keys_to_vlckeys[] = +{ + { 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_Return, KEY_ENTER }, + { XK_KP_Enter, KEY_ENTER }, + { XK_space, KEY_SPACE }, + { XK_Escape, KEY_ESC }, + + { XK_Menu, KEY_MENU }, + { XK_Left, KEY_LEFT }, + { XK_Right, KEY_RIGHT }, + { XK_Up, KEY_UP }, + { XK_Down, KEY_DOWN }, + + { XK_Home, KEY_HOME }, + { XK_End, KEY_END }, + { XK_Page_Up, KEY_PAGEUP }, + { XK_Page_Down, KEY_PAGEDOWN }, + + { XK_Insert, KEY_INSERT }, + { XK_Delete, KEY_DELETE }, + { XF86XK_AudioNext, KEY_MEDIA_NEXT_TRACK}, + { XF86XK_AudioPrev, KEY_MEDIA_PREV_TRACK}, + { XF86XK_AudioMute, KEY_VOLUME_MUTE }, + { XF86XK_AudioLowerVolume, KEY_VOLUME_DOWN }, + { XF86XK_AudioRaiseVolume, KEY_VOLUME_UP }, + { XF86XK_AudioPlay, KEY_MEDIA_PLAY_PAUSE }, + { XF86XK_AudioPause, KEY_MEDIA_PLAY_PAUSE }, + + { 0, 0 } +}; +static KeySym GetX11Key( unsigned i_vlc ) +{ + for( int i = 0; x11keys_to_vlckeys[i].i_vlc != 0; i++ ) + { + if( x11keys_to_vlckeys[i].i_vlc == i_vlc ) + return x11keys_to_vlckeys[i].i_x11; + } + + char psz_key[2]; + psz_key[0] = i_vlc; + psz_key[1] = '\0'; + return XStringToKeysym( psz_key ); +} + +static void Mapping( intf_thread_t *p_intf ) +{ + static const KeySym p_x11_modifier_ignored[] = { + 0, + XK_Num_Lock, + XK_Scroll_Lock, + XK_Caps_Lock, + }; + + intf_sys_t *p_sys = p_intf->p_sys; + + p_sys->i_map = 0; + p_sys->p_map = NULL; + + /* Registering of Hotkeys */ + for( struct hotkey *p_hotkey = p_intf->p_libvlc->p_hotkeys; + p_hotkey->psz_action != NULL; + p_hotkey++ ) + { + char *psz_hotkey; + if( asprintf( &psz_hotkey, "global-%s", p_hotkey->psz_action ) < 0 ) + break; + + const int i_vlc_action = p_hotkey->i_action; + const int i_vlc_key = config_GetInt( p_intf, psz_hotkey ); + + free( psz_hotkey ); + + if( !i_vlc_key ) + continue; + + const KeyCode key = XKeysymToKeycode( p_sys->p_display, + GetX11Key( i_vlc_key & ~KEY_MODIFIER ) ); + const unsigned i_modifier = GetX11Modifier( p_sys->p_display, i_vlc_key & KEY_MODIFIER ); + + for( int j = 0; j < sizeof(p_x11_modifier_ignored)/sizeof(*p_x11_modifier_ignored); j++ ) + { + const unsigned i_ignored = GetModifier( p_sys->p_display, p_x11_modifier_ignored[j] ); + if( j != 0 && i_ignored == 0x00) + continue; + + hotkey_mapping_t *p_map_old = p_sys->p_map; + p_sys->p_map = realloc( p_sys->p_map, sizeof(*p_sys->p_map) * (p_sys->i_map+1) ); + if( !p_sys->p_map ) + { + p_sys->p_map = p_map_old; + break; + } + hotkey_mapping_t *p_map = &p_sys->p_map[p_sys->i_map++]; + + p_map->i_x11 = key; + p_map->i_modifier = i_modifier|i_ignored; + p_map->i_action = i_vlc_action; + } + } +} + +static void Register( intf_thread_t *p_intf ) +{ + intf_sys_t *p_sys = p_intf->p_sys; + + for( int i = 0; i < p_sys->i_map; i++ ) + { + hotkey_mapping_t *p_map = &p_sys->p_map[i]; + XGrabKey( p_sys->p_display, p_map->i_x11, p_map->i_modifier, + DefaultRootWindow( p_sys->p_display ), True, GrabModeAsync, GrabModeAsync ); + } +} +static void Unregister( intf_thread_t *p_intf ) +{ + intf_sys_t *p_sys = p_intf->p_sys; + + for( int i = 0; i < p_sys->i_map; i++ ) + { + hotkey_mapping_t *p_map = &p_sys->p_map[i]; + XUngrabKey( p_sys->p_display, p_map->i_x11, p_map->i_modifier, + DefaultRootWindow( p_sys->p_display ) ); + } +} + +static void *Thread( void *p_data ) +{ + intf_thread_t *p_intf = p_data; + intf_sys_t *p_sys = p_intf->p_sys; + Display *p_display = p_sys->p_display; + + int canc = vlc_savecancel(); + + if( !p_display ) + return NULL; + + /* */ + XFlush( p_display ); + + /* */ + int fd = ConnectionNumber( p_display ); + for( ;; ) + { + /* Wait for x11 event */ + vlc_restorecancel( canc ); + struct pollfd fds = { .fd = fd, .events = POLLIN, }; + if( poll( &fds, 1, -1 ) < 0 ) + { + if( errno != EINTR ) + break; + continue; + } + canc = vlc_savecancel(); + + while( XPending( p_display ) > 0 ) + { + XEvent e; + + XNextEvent( p_display, &e ); + if( e.type != KeyPress ) + continue; + + for( int i = 0; i < p_sys->i_map; i++ ) + { + hotkey_mapping_t *p_map = &p_sys->p_map[i]; + + if( p_map->i_x11 == e.xkey.keycode && + p_map->i_modifier == e.xkey.state ) + { + var_SetInteger( p_intf->p_libvlc, "key-action", p_map->i_action ); + break; + } + } + } + } + + vlc_restorecancel( canc ); + return NULL; +} + -- 2.39.2