]> git.sesse.net Git - vlc/blob - modules/control/globalhotkeys/win32.c
f80510ed82305183afea465d7ce16f73f68e8066
[vlc] / modules / control / globalhotkeys / win32.c
1 /*****************************************************************************
2  * win32.c: Global-Hotkey _WIN32 handling for vlc
3  *****************************************************************************
4  * Copyright (C) 2008-2009 the VideoLAN team
5  *
6  * Authors: Domani Hannes <ssbssa at yahoo dot de>
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
23 #ifdef HAVE_CONFIG_H
24 # include "config.h"
25 #endif
26
27 #include <ctype.h>
28
29 #include <vlc_common.h>
30 #include <vlc_plugin.h>
31 #include <vlc_interface.h>
32 #include <vlc_keys.h>
33
34 /*****************************************************************************
35  * Local prototypes
36  *****************************************************************************/
37 static int Open( vlc_object_t *p_this );
38 static void Close( vlc_object_t *p_this );
39 static void *Thread( void *p_data );
40 LRESULT CALLBACK WMHOTKEYPROC( HWND, UINT, WPARAM, LPARAM );
41
42 /*****************************************************************************
43  * Module descriptor
44  *****************************************************************************/
45 vlc_module_begin()
46     set_shortname( N_("Global Hotkeys") )
47     set_category( CAT_INTERFACE )
48     set_subcategory( SUBCAT_INTERFACE_HOTKEYS )
49     set_description( N_("Global Hotkeys interface") )
50     set_capability( "interface", 0 )
51     set_callbacks( Open, Close )
52     add_shortcut( "globalhotkeys" )
53 vlc_module_end()
54
55 struct intf_sys_t
56 {
57     vlc_thread_t thread;
58     HWND hotkeyWindow;
59     vlc_mutex_t lock;
60     vlc_cond_t wait;
61 };
62
63 /*****************************************************************************
64  * Open: initialize interface
65  *****************************************************************************/
66 static int Open( vlc_object_t *p_this )
67 {
68     intf_thread_t *p_intf = (intf_thread_t *)p_this;
69     intf_sys_t *p_sys = malloc( sizeof (intf_sys_t) );
70
71     if( p_sys == NULL )
72         return VLC_ENOMEM;
73
74     p_intf->p_sys = p_sys;
75     p_sys->hotkeyWindow = NULL;
76     vlc_mutex_init( &p_sys->lock );
77     vlc_cond_init( &p_sys->wait );
78
79     if( vlc_clone( &p_sys->thread, Thread, p_intf, VLC_THREAD_PRIORITY_LOW ) )
80     {
81         vlc_mutex_destroy( &p_sys->lock );
82         vlc_cond_destroy( &p_sys->wait );
83         free( p_sys );
84         p_intf->p_sys = NULL;
85
86         return VLC_ENOMEM;
87     }
88
89     vlc_mutex_lock( &p_sys->lock );
90     while( p_sys->hotkeyWindow == NULL )
91         vlc_cond_wait( &p_sys->wait, &p_sys->lock );
92     if( p_sys->hotkeyWindow == INVALID_HANDLE_VALUE )
93     {
94         vlc_mutex_unlock( &p_sys->lock );
95         vlc_join( p_sys->thread, NULL );
96         vlc_mutex_destroy( &p_sys->lock );
97         vlc_cond_destroy( &p_sys->wait );
98         free( p_sys );
99         p_intf->p_sys = NULL;
100
101         return VLC_ENOMEM;
102     }
103     vlc_mutex_unlock( &p_sys->lock );
104
105     return VLC_SUCCESS;
106 }
107
108 /*****************************************************************************
109  * Close: destroy interface
110  *****************************************************************************/
111 static void Close( vlc_object_t *p_this )
112 {
113     intf_thread_t *p_intf = (intf_thread_t *)p_this;
114     intf_sys_t *p_sys = p_intf->p_sys;
115
116     /* stop hotkey window */
117     vlc_mutex_lock( &p_sys->lock );
118     if( p_sys->hotkeyWindow != NULL )
119         PostMessage( p_sys->hotkeyWindow, WM_CLOSE, 0, 0 );
120     vlc_mutex_unlock( &p_sys->lock );
121
122     vlc_join( p_sys->thread, NULL );
123     vlc_mutex_destroy( &p_sys->lock );
124     vlc_cond_destroy( &p_sys->wait );
125     free( p_sys );
126 }
127
128 /*****************************************************************************
129  * Thread: main loop
130  *****************************************************************************/
131 static void *Thread( void *p_data )
132 {
133     MSG message;
134
135     intf_thread_t *p_intf = p_data;
136     intf_sys_t *p_sys = p_intf->p_sys;
137
138     /* Window which receives Hotkeys */
139     vlc_mutex_lock( &p_sys->lock );
140     p_sys->hotkeyWindow =
141         (void*)CreateWindow( _T("STATIC"),           /* name of window class */
142                 _T("VLC ghk ") _T(VERSION),         /* window title bar text */
143                 0,                                           /* window style */
144                 0,                                   /* default X coordinate */
145                 0,                                   /* default Y coordinate */
146                 0,                                           /* window width */
147                 0,                                          /* window height */
148                 NULL,                                    /* no parent window */
149                 NULL,                              /* no menu in this window */
150                 GetModuleHandle(NULL),    /* handle of this program instance */
151                 NULL );                                 /* sent to WM_CREATE */
152
153     if( p_sys->hotkeyWindow == NULL )
154     {
155         p_sys->hotkeyWindow = INVALID_HANDLE_VALUE;
156         vlc_cond_signal( &p_sys->wait );
157         vlc_mutex_unlock( &p_sys->lock );
158         return NULL;
159     }
160     vlc_cond_signal( &p_sys->wait );
161     vlc_mutex_unlock( &p_sys->lock );
162
163     SetWindowLongPtr( p_sys->hotkeyWindow, GWLP_WNDPROC,
164             (LONG_PTR)WMHOTKEYPROC );
165     SetWindowLongPtr( p_sys->hotkeyWindow, GWLP_USERDATA,
166             (LONG_PTR)p_intf );
167
168     /* Registering of Hotkeys */
169     for( const struct hotkey *p_hotkey = p_intf->p_libvlc->p_hotkeys;
170             p_hotkey->psz_action != NULL;
171             p_hotkey++ )
172     {
173         char varname[12 + strlen( p_hotkey->psz_action )];
174         sprintf( varname, "global-key-%s", p_hotkey->psz_action );
175
176         char *key = var_InheritString( p_intf, varname );
177         if( key == NULL )
178             continue;
179
180         UINT i_key = vlc_str2keycode( key );
181         free( key );
182         if( i_key == KEY_UNSET )
183             continue;
184
185         UINT i_keyMod = 0;
186         if( i_key & KEY_MODIFIER_SHIFT ) i_keyMod |= MOD_SHIFT;
187         if( i_key & KEY_MODIFIER_ALT ) i_keyMod |= MOD_ALT;
188         if( i_key & KEY_MODIFIER_CTRL ) i_keyMod |= MOD_CONTROL;
189
190 #define HANDLE( key ) case KEY_##key: i_vk = VK_##key; break
191 #define HANDLE2( key, key2 ) case KEY_##key: i_vk = VK_##key2; break
192
193 #define KEY_SPACE ' '
194
195 #ifndef VK_VOLUME_DOWN
196 #define VK_VOLUME_DOWN          0xAE
197 #define VK_VOLUME_UP            0xAF
198 #endif
199
200 #ifndef VK_MEDIA_NEXT_TRACK
201 #define VK_MEDIA_NEXT_TRACK     0xB0
202 #define VK_MEDIA_PREV_TRACK     0xB1
203 #define VK_MEDIA_STOP           0xB2
204 #define VK_MEDIA_PLAY_PAUSE     0xB3
205 #endif
206
207 #ifndef VK_PAGEUP
208 #define VK_PAGEUP               0x21
209 #define VK_PAGEDOWN             0x22
210 #endif
211
212         UINT i_vk = 0;
213         switch( i_key & ~KEY_MODIFIER )
214         {
215             HANDLE( LEFT );
216             HANDLE( RIGHT );
217             HANDLE( UP );
218             HANDLE( DOWN );
219             HANDLE( SPACE );
220             HANDLE2( ESC, ESCAPE );
221             HANDLE2( ENTER, RETURN );
222             HANDLE( F1 );
223             HANDLE( F2 );
224             HANDLE( F3 );
225             HANDLE( F4 );
226             HANDLE( F5 );
227             HANDLE( F6 );
228             HANDLE( F7 );
229             HANDLE( F8 );
230             HANDLE( F9 );
231             HANDLE( F10 );
232             HANDLE( F11 );
233             HANDLE( F12 );
234             HANDLE( PAGEUP );
235             HANDLE( PAGEDOWN );
236             HANDLE( HOME );
237             HANDLE( END );
238             HANDLE( INSERT );
239             HANDLE( DELETE );
240             HANDLE( VOLUME_DOWN );
241             HANDLE( VOLUME_UP );
242             HANDLE( MEDIA_PLAY_PAUSE );
243             HANDLE( MEDIA_STOP );
244             HANDLE( MEDIA_PREV_TRACK );
245             HANDLE( MEDIA_NEXT_TRACK );
246
247             default:
248                 i_vk = toupper( (uint8_t)(i_key & ~KEY_MODIFIER) );
249                 break;
250         }
251         if( !i_vk ) continue;
252
253 #undef HANDLE
254 #undef HANDLE2
255
256         ATOM atom = GlobalAddAtomA( p_hotkey->psz_action );
257         if( !atom ) continue;
258
259         if( !RegisterHotKey( p_sys->hotkeyWindow, atom, i_keyMod, i_vk ) )
260             GlobalDeleteAtom( atom );
261     }
262
263     /* Main message loop */
264     while( GetMessage( &message, NULL, 0, 0 ) )
265         DispatchMessage( &message );
266
267     /* Unregistering of Hotkeys */
268     for( const struct hotkey *p_hotkey = p_intf->p_libvlc->p_hotkeys;
269             p_hotkey->psz_action != NULL;
270             p_hotkey++ )
271     {
272         ATOM atom = GlobalFindAtomA( p_hotkey->psz_action );
273         if( !atom ) continue;
274
275         if( UnregisterHotKey( p_sys->hotkeyWindow, atom ) )
276             GlobalDeleteAtom( atom );
277     }
278
279     /* close window */
280     vlc_mutex_lock( &p_sys->lock );
281     DestroyWindow( p_sys->hotkeyWindow );
282     p_sys->hotkeyWindow = NULL;
283     vlc_mutex_unlock( &p_sys->lock );
284
285     return NULL;
286 }
287
288 /*****************************************************************************
289  * WMHOTKEYPROC: event callback
290  *****************************************************************************/
291 LRESULT CALLBACK WMHOTKEYPROC( HWND hwnd, UINT uMsg, WPARAM wParam,
292         LPARAM lParam )
293 {
294     switch( uMsg )
295     {
296         case WM_HOTKEY:
297             {
298                 char psz_atomName[44];
299
300                 LONG_PTR ret = GetWindowLongPtr( hwnd, GWLP_USERDATA );
301                 intf_thread_t *p_intf = (intf_thread_t*)ret;
302                 strcpy( psz_atomName, "key-" );
303
304                 if( !GlobalGetAtomNameA(
305                         wParam, psz_atomName + 4,
306                         sizeof( psz_atomName ) - 4 ) )
307                     return 0;
308
309                 /* search for key associated with VLC */
310                 vlc_action_t action = vlc_GetActionId( psz_atomName );
311                 if( action != ACTIONID_NONE )
312                 {
313                     var_SetInteger( p_intf->p_libvlc,
314                             "key-action", action );
315                     return 1;
316                 }
317             }
318             break;
319
320         case WM_DESTROY:
321             PostQuitMessage( 0 );
322             break;
323
324         default:
325             return DefWindowProc( hwnd, uMsg, wParam, lParam );
326     }
327
328     return 0;
329 }