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