xcb.c
上传用户:kjfoods
上传日期:2020-07-06
资源大小:29949k
文件大小:15k
源码类别:

midi

开发平台:

Unix_Linux

  1. /*****************************************************************************
  2.  * xcb.c: Global-Hotkey X11 using xcb handling for vlc
  3.  *****************************************************************************
  4.  * Copyright (C) 2009 the VideoLAN team
  5.  *
  6.  * Authors: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
  7.  *
  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.
  12.  *
  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.
  17.  *
  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.  *****************************************************************************/
  22. #ifdef HAVE_CONFIG_H
  23. # include "config.h"
  24. #endif
  25. #include <vlc_common.h>
  26. #include <vlc_plugin.h>
  27. #include <vlc_interface.h>
  28. #include <vlc_keys.h>
  29. #include <ctype.h>
  30. #include <errno.h>
  31. #include <xcb/xcb.h>
  32. #include <xcb/xcb_keysyms.h>
  33. #include <X11/keysym.h>
  34. #include <X11/XF86keysym.h>
  35. #include <poll.h>
  36. /*****************************************************************************
  37.  * Local prototypes
  38.  *****************************************************************************/
  39. static int Open( vlc_object_t *p_this );
  40. static void Close( vlc_object_t *p_this );
  41. /*****************************************************************************
  42.  * Module descriptor
  43.  *****************************************************************************/
  44. vlc_module_begin()
  45.     set_shortname( N_("Global Hotkeys") )
  46.     set_category( CAT_INTERFACE )
  47.     set_subcategory( SUBCAT_INTERFACE_HOTKEYS )
  48.     set_description( N_("Global Hotkeys interface") )
  49.     set_capability( "interface", 0 )
  50.     set_callbacks( Open, Close )
  51. vlc_module_end()
  52. typedef struct
  53. {
  54. #ifdef XCB_KEYSYM_OLD_API /* as seen in Debian Lenny */
  55.     xcb_keycode_t i_x11;
  56. #else
  57.     xcb_keycode_t *p_keys;
  58. #endif
  59.     unsigned      i_modifier;
  60.     int           i_action;
  61. } hotkey_mapping_t;
  62. struct intf_sys_t
  63. {
  64.     vlc_thread_t thread;
  65.     xcb_connection_t  *p_connection;
  66.     xcb_window_t      root;
  67.     xcb_key_symbols_t *p_symbols;
  68.     int              i_map;
  69.     hotkey_mapping_t *p_map;
  70. };
  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. /*****************************************************************************
  76.  * Open:
  77.  *****************************************************************************/
  78. static int Open( vlc_object_t *p_this )
  79. {
  80.     intf_thread_t *p_intf = (intf_thread_t *)p_this;
  81.     intf_sys_t *p_sys;
  82.     p_intf->p_sys = p_sys = calloc( 1, sizeof(*p_sys) );
  83.     if( !p_sys )
  84.         return VLC_ENOMEM;
  85.     char *psz_display = var_CreateGetNonEmptyString( p_intf, "x11-display" );
  86.     int i_screen_default;
  87.     p_sys->p_connection = xcb_connect( psz_display, &i_screen_default );
  88.     free( psz_display );
  89.     if( xcb_connection_has_error( p_sys->p_connection ) )
  90.         goto error;
  91.     /* Get the root windows of the default screen */
  92.     const xcb_setup_t* xcbsetup = xcb_get_setup( p_sys->p_connection );
  93.     if( !xcbsetup )
  94.         goto error;
  95.     xcb_screen_iterator_t iter = xcb_setup_roots_iterator( xcbsetup );
  96.     for( int i = 0; i < i_screen_default; i++ )
  97.     {
  98.         if( !iter.rem )
  99.             break;
  100.         xcb_screen_next( &iter );
  101.     }
  102.     if( !iter.rem )
  103.         goto error;
  104.     p_sys->root = iter.data->root;
  105.     /* */
  106.     p_sys->p_symbols = xcb_key_symbols_alloc( p_sys->p_connection ); // FIXME
  107.     if( !p_sys->p_symbols )
  108.         goto error;
  109.     Mapping( p_intf );
  110.     Register( p_intf );
  111.     if( vlc_clone( &p_sys->thread, Thread, p_intf, VLC_THREAD_PRIORITY_LOW ) )
  112.     {
  113.         Unregister( p_intf );
  114. #ifndef XCB_KEYSYM_OLD_API /* as seen in Debian Lenny */
  115.         if( p_sys->p_map )
  116.             free( p_sys->p_map->p_keys );
  117. #endif
  118.         free( p_sys->p_map );
  119.         goto error;
  120.     }
  121.     return VLC_SUCCESS;
  122. error:
  123.     if( p_sys->p_symbols )
  124.         xcb_key_symbols_free( p_sys->p_symbols );
  125.     xcb_disconnect( p_sys->p_connection );
  126.     free( p_sys );
  127.     return VLC_EGENERIC;
  128. }
  129. /*****************************************************************************
  130.  * Close:
  131.  *****************************************************************************/
  132. static void Close( vlc_object_t *p_this )
  133. {
  134.     intf_thread_t *p_intf = (intf_thread_t *)p_this;
  135.     intf_sys_t *p_sys = p_intf->p_sys;
  136.     vlc_cancel( p_sys->thread );
  137.     vlc_join( p_sys->thread, NULL );
  138.     Unregister( p_intf );
  139. #ifndef XCB_KEYSYM_OLD_API /* as seen in Debian Lenny */
  140.     if( p_sys->p_map )
  141.         free( p_sys->p_map->p_keys );
  142. #endif
  143.     free( p_sys->p_map );
  144.     xcb_key_symbols_free( p_sys->p_symbols );
  145.     xcb_disconnect( p_sys->p_connection );
  146.     free( p_sys );
  147. }
  148. /*****************************************************************************
  149.  *
  150.  *****************************************************************************/
  151. static unsigned GetModifier( xcb_connection_t *p_connection, xcb_key_symbols_t *p_symbols, xcb_keysym_t sym )
  152. {
  153.     static const unsigned pi_mask[8] = {
  154.         XCB_MOD_MASK_SHIFT, XCB_MOD_MASK_LOCK, XCB_MOD_MASK_CONTROL,
  155.         XCB_MOD_MASK_1, XCB_MOD_MASK_2, XCB_MOD_MASK_3,
  156.         XCB_MOD_MASK_4, XCB_MOD_MASK_5
  157.     };
  158.     if( sym == 0 )
  159.         return 0; /* no modifier */
  160. #ifdef XCB_KEYSYM_OLD_API /* as seen in Debian Lenny */
  161.     const xcb_keycode_t key = xcb_key_symbols_get_keycode( p_symbols, sym );
  162.     if( key == 0 )
  163.         return 0;
  164. #else
  165.     const xcb_keycode_t *p_keys = xcb_key_symbols_get_keycode( p_symbols, sym );
  166.     if( !p_keys )
  167.         return 0;
  168.     int i = 0;
  169.     bool no_modifier = true;
  170.     while( p_keys[i] != XCB_NO_SYMBOL )
  171.     {
  172.         if( p_keys[i] != 0 )
  173.         {
  174.             no_modifier = false;
  175.             break;
  176.         }
  177.         i++;
  178.     }
  179.     if( no_modifier )
  180.         return 0;
  181. #endif
  182.     xcb_get_modifier_mapping_cookie_t r =
  183.             xcb_get_modifier_mapping( p_connection );
  184.     xcb_get_modifier_mapping_reply_t *p_map =
  185.             xcb_get_modifier_mapping_reply( p_connection, r, NULL );
  186.     if( !p_map )
  187.         return 0;
  188.     xcb_keycode_t *p_keycode = xcb_get_modifier_mapping_keycodes( p_map );
  189.     if( !p_keycode )
  190.         return 0;
  191.     for( int i = 0; i < 8; i++ )
  192.         for( int j = 0; j < p_map->keycodes_per_modifier; j++ )
  193. #ifdef XCB_KEYSYM_OLD_API /* as seen in Debian Lenny */
  194.             if( p_keycode[i * p_map->keycodes_per_modifier + j] == key )
  195.             {
  196.                 free( p_map );
  197.                 return pi_mask[i];
  198.             }
  199. #else
  200.             for( int k = 0; p_keys[k] != XCB_NO_SYMBOL; k++ )
  201.                 if( p_keycode[i*p_map->keycodes_per_modifier + j] == p_keys[k])
  202.                 {
  203.                     free( p_map );
  204.                     return pi_mask[i];
  205.                 }
  206. #endif
  207.     free( p_map ); // FIXME to check
  208.     return 0;
  209. }
  210. static unsigned GetX11Modifier( xcb_connection_t *p_connection,
  211.         xcb_key_symbols_t *p_symbols, unsigned i_vlc )
  212. {
  213.     unsigned i_mask = 0;
  214.     if( i_vlc & KEY_MODIFIER_ALT )
  215.         i_mask |= GetModifier( p_connection, p_symbols, XK_Alt_L ) |
  216.                   GetModifier( p_connection, p_symbols, XK_Alt_R );
  217.     if( i_vlc & KEY_MODIFIER_CTRL )
  218.         i_mask |= GetModifier( p_connection, p_symbols, XK_Control_L ) |
  219.                   GetModifier( p_connection, p_symbols, XK_Control_R );
  220.     if( i_vlc & KEY_MODIFIER_SHIFT )
  221.         i_mask |= GetModifier( p_connection, p_symbols, XK_Shift_L ) |
  222.                   GetModifier( p_connection, p_symbols, XK_Shift_R );
  223.     return i_mask;
  224. }
  225. /* FIXME this table is also used by the vout */
  226. static const struct
  227. {
  228.     xcb_keysym_t i_x11;
  229.     unsigned     i_vlc;
  230. } x11keys_to_vlckeys[] =
  231. {
  232.     { XK_F1, KEY_F1 }, { XK_F2, KEY_F2 }, { XK_F3, KEY_F3 }, { XK_F4, KEY_F4 },
  233.     { XK_F5, KEY_F5 }, { XK_F6, KEY_F6 }, { XK_F7, KEY_F7 }, { XK_F8, KEY_F8 },
  234.     { XK_F9, KEY_F9 }, { XK_F10, KEY_F10 }, { XK_F11, KEY_F11 },
  235.     { XK_F12, KEY_F12 },
  236.     { XK_Return, KEY_ENTER },
  237.     { XK_KP_Enter, KEY_ENTER },
  238.     { XK_space, KEY_SPACE },
  239.     { XK_Escape, KEY_ESC },
  240.     { XK_Menu, KEY_MENU },
  241.     { XK_Left, KEY_LEFT },
  242.     { XK_Right, KEY_RIGHT },
  243.     { XK_Up, KEY_UP },
  244.     { XK_Down, KEY_DOWN },
  245.     { XK_Home, KEY_HOME },
  246.     { XK_End, KEY_END },
  247.     { XK_Page_Up, KEY_PAGEUP },
  248.     { XK_Page_Down, KEY_PAGEDOWN },
  249.     { XK_Insert, KEY_INSERT },
  250.     { XK_Delete, KEY_DELETE },
  251.     { XF86XK_AudioNext, KEY_MEDIA_NEXT_TRACK},
  252.     { XF86XK_AudioPrev, KEY_MEDIA_PREV_TRACK},
  253.     { XF86XK_AudioMute, KEY_VOLUME_MUTE },
  254.     { XF86XK_AudioLowerVolume, KEY_VOLUME_DOWN },
  255.     { XF86XK_AudioRaiseVolume, KEY_VOLUME_UP },
  256.     { XF86XK_AudioPlay, KEY_MEDIA_PLAY_PAUSE },
  257.     { XF86XK_AudioPause, KEY_MEDIA_PLAY_PAUSE },
  258.     { 0, 0 }
  259. };
  260. static xcb_keysym_t GetX11Key( unsigned i_vlc )
  261. {
  262.     for( int i = 0; x11keys_to_vlckeys[i].i_vlc != 0; i++ )
  263.     {
  264.         if( x11keys_to_vlckeys[i].i_vlc == i_vlc )
  265.             return x11keys_to_vlckeys[i].i_x11;
  266.     }
  267.     /* Copied from xcb, it seems that xcb use ascii code for ascii characters */
  268.     if( isascii( i_vlc ) )
  269.         return i_vlc;
  270.     return XK_VoidSymbol;
  271. }
  272. static void Mapping( intf_thread_t *p_intf )
  273. {
  274.     static const xcb_keysym_t p_x11_modifier_ignored[] = {
  275.         0,
  276.         XK_Num_Lock,
  277.         XK_Scroll_Lock,
  278.         XK_Caps_Lock,
  279.     };
  280.     intf_sys_t *p_sys = p_intf->p_sys;
  281.     p_sys->i_map = 0;
  282.     p_sys->p_map = NULL;
  283.     /* Registering of Hotkeys */
  284.     for( const struct hotkey *p_hotkey = p_intf->p_libvlc->p_hotkeys;
  285.             p_hotkey->psz_action != NULL;
  286.             p_hotkey++ )
  287.     {
  288.         char *psz_hotkey;
  289.         if( asprintf( &psz_hotkey, "global-%s", p_hotkey->psz_action ) < 0 )
  290.             break;
  291.         const int i_vlc_action = p_hotkey->i_action;
  292.         const int i_vlc_key = config_GetInt( p_intf, psz_hotkey );
  293.         free( psz_hotkey );
  294.         if( !i_vlc_key )
  295.             continue;
  296. #ifdef XCB_KEYSYM_OLD_API /* as seen in Debian Lenny */
  297.         const xcb_keycode_t key = xcb_key_symbols_get_keycode(
  298.                 p_sys->p_symbols, GetX11Key( i_vlc_key & ~KEY_MODIFIER ) );
  299. #else
  300.         xcb_keycode_t *p_keys = xcb_key_symbols_get_keycode(
  301.                 p_sys->p_symbols, GetX11Key( i_vlc_key & ~KEY_MODIFIER ) );
  302.         if( !p_keys )
  303.             continue;
  304. #endif
  305.         const unsigned i_modifier = GetX11Modifier( p_sys->p_connection,
  306.                 p_sys->p_symbols, i_vlc_key & KEY_MODIFIER );
  307.         const size_t max = sizeof(p_x11_modifier_ignored) /
  308.                 sizeof(*p_x11_modifier_ignored);
  309.         for( unsigned int i = 0; i < max; i++ )
  310.         {
  311.             const unsigned i_ignored = GetModifier( p_sys->p_connection,
  312.                     p_sys->p_symbols, p_x11_modifier_ignored[i] );
  313.             if( i != 0 && i_ignored == 0)
  314.                 continue;
  315.             hotkey_mapping_t *p_map_old = p_sys->p_map;
  316.             p_sys->p_map = realloc( p_sys->p_map,
  317.                     sizeof(*p_sys->p_map) * (p_sys->i_map+1) );
  318.             if( !p_sys->p_map )
  319.             {
  320.                 p_sys->p_map = p_map_old;
  321.                 break;
  322.             }
  323.             hotkey_mapping_t *p_map = &p_sys->p_map[p_sys->i_map++];
  324. #ifdef XCB_KEYSYM_OLD_API /* as seen in Debian Lenny */
  325.             p_map->i_x11 = key;
  326. #else
  327.             p_map->p_keys = p_keys;
  328. #endif
  329.             p_map->i_modifier = i_modifier|i_ignored;
  330.             p_map->i_action = i_vlc_action;
  331.         }
  332.     }
  333. }
  334. static void Register( intf_thread_t *p_intf )
  335. {
  336.     intf_sys_t *p_sys = p_intf->p_sys;
  337.     for( int i = 0; i < p_sys->i_map; i++ )
  338.     {
  339.         const hotkey_mapping_t *p_map = &p_sys->p_map[i];
  340. #ifdef XCB_KEYSYM_OLD_API /* as seen in Debian Lenny */
  341.         xcb_grab_key( p_sys->p_connection, true, p_sys->root,
  342.                       p_map->i_modifier, p_map->i_x11,
  343.                       XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC );
  344. #else
  345.         for( int j = 0; p_map->p_keys[j] != XCB_NO_SYMBOL; j++ )
  346.         {
  347.             xcb_grab_key( p_sys->p_connection, true, p_sys->root,
  348.                           p_map->i_modifier, p_map->p_keys[j],
  349.                           XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC );
  350.         }
  351. #endif
  352.     }
  353. }
  354. static void Unregister( intf_thread_t *p_intf )
  355. {
  356.     intf_sys_t *p_sys = p_intf->p_sys;
  357.     for( int i = 0; i < p_sys->i_map; i++ )
  358.     {
  359.         const hotkey_mapping_t *p_map = &p_sys->p_map[i];
  360. #ifdef XCB_KEYSYM_OLD_API /* as seen in Debian Lenny */
  361.         xcb_ungrab_key( p_sys->p_connection, p_map->i_x11, p_sys->root,
  362.                 p_map->i_modifier );
  363. #else
  364.         for( int j = 0; p_map->p_keys[j] != XCB_NO_SYMBOL; j++ )
  365.             xcb_ungrab_key( p_sys->p_connection, p_map->p_keys[j], p_sys->root,
  366.                     p_map->i_modifier );
  367. #endif
  368.     }
  369. }
  370. static void *Thread( void *p_data )
  371. {
  372.     intf_thread_t *p_intf = p_data;
  373.     intf_sys_t *p_sys = p_intf->p_sys;
  374.     xcb_connection_t *p_connection = p_sys->p_connection;
  375.     int canc = vlc_savecancel();
  376.     /* */
  377.     xcb_flush( p_connection );
  378.     /* */
  379.     int fd = xcb_get_file_descriptor( p_connection );
  380.     for( ;; )
  381.     {
  382.         /* Wait for x11 event */
  383.         vlc_restorecancel( canc );
  384.         struct pollfd fds = { .fd = fd, .events = POLLIN, };
  385.         if( poll( &fds, 1, -1 ) < 0 )
  386.         {
  387.             if( errno != EINTR )
  388.                 break;
  389.             canc = vlc_savecancel();
  390.             continue;
  391.         }
  392.         canc = vlc_savecancel();
  393.         xcb_generic_event_t *p_event;
  394.         while( ( p_event = xcb_poll_for_event( p_connection ) ) )
  395.         {
  396.             if( ( p_event->response_type & 0x7f ) != XCB_KEY_PRESS )
  397.             {
  398.                 free( p_event );
  399.                 continue;
  400.             }
  401.             xcb_key_press_event_t *e = (xcb_key_press_event_t *)p_event;
  402.             for( int i = 0; i < p_sys->i_map; i++ )
  403.             {
  404.                 hotkey_mapping_t *p_map = &p_sys->p_map[i];
  405. #ifdef XCB_KEYSYM_OLD_API /* as seen in Debian Lenny */
  406.                 if( p_map->i_x11 == e->detail &&
  407.                     p_map->i_modifier == e->state )
  408.                 {
  409.                     var_SetInteger( p_intf->p_libvlc, "key-action",
  410.                             p_map->i_action );
  411.                     break;
  412.                 }
  413. #else
  414.             bool loop_break = false;
  415.             for( int j = 0; p_map->p_keys[j] != XCB_NO_SYMBOL; j++ )
  416.                 if( p_map->p_keys[j] == e->detail &&
  417.                     p_map->i_modifier == e->state )
  418.                 {
  419.                     var_SetInteger( p_intf->p_libvlc, "key-action",
  420.                             p_map->i_action );
  421.                     loop_break = true;
  422.                     break;
  423.                 }
  424.             if( loop_break )
  425.                 break;
  426. #endif
  427.             }
  428.             free( p_event );
  429.         }
  430.     }
  431.     return NULL;
  432. }