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