]> git.sesse.net Git - vlc/blob - src/win32/specific.c
44529df0a153001481e79dbf7351d0686fb86115
[vlc] / src / win32 / specific.c
1 /*****************************************************************************
2  * specific.c: Win32 specific initilization
3  *****************************************************************************
4  * Copyright (C) 2001-2004, 2010 VLC authors and VideoLAN
5  *
6  * Authors: Samuel Hocevar <sam@zoy.org>
7  *          Gildas Bazin <gbazin@videolan.org>
8  *
9  * This program is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2.1 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27
28 #define UNICODE
29 #include <vlc_common.h>
30 #include "../libvlc.h"
31 #include <vlc_playlist.h>
32 #include <vlc_url.h>
33
34 #include "../config/vlc_getopt.h"
35
36 #include <mmsystem.h>
37 #include <winsock.h>
38
39
40 static int system_InitWSA(int hi, int lo)
41 {
42     WSADATA data;
43
44     if (WSAStartup(MAKEWORD(hi, lo), &data) == 0)
45     {
46         if (LOBYTE(data.wVersion) == 2 && HIBYTE(data.wVersion) == 2)
47             return 0;
48         /* Winsock DLL is not usable */
49         WSACleanup( );
50     }
51     return -1;
52 }
53
54 /**
55  * Initializes MME timer, Winsock.
56  */
57 void system_Init(void)
58 {
59 #if !VLC_WINSTORE_APP
60     timeBeginPeriod(5);
61 #endif
62
63     if (system_InitWSA(2, 2) && system_InitWSA(1, 1))
64         fputs("Error: cannot initialize Winsocks\n", stderr);
65 }
66
67 /*****************************************************************************
68  * system_Configure: check for system specific configuration options.
69  *****************************************************************************/
70 static unsigned __stdcall IPCHelperThread( void * );
71 LRESULT CALLBACK WMCOPYWNDPROC( HWND, UINT, WPARAM, LPARAM );
72 static vlc_object_t *p_helper = NULL;
73 static unsigned long hIPCHelper;
74 static HANDLE hIPCHelperReady;
75
76 typedef struct
77 {
78     int argc;
79     int enqueue;
80     char data[];
81 } vlc_ipc_data_t;
82
83 void system_Configure( libvlc_int_t *p_this, int i_argc, const char *const ppsz_argv[] )
84 {
85 #if !VLC_WINSTORE_APP
86     /* Raise default priority of the current process */
87 #ifndef ABOVE_NORMAL_PRIORITY_CLASS
88 #   define ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
89 #endif
90     if( var_InheritBool( p_this, "high-priority" ) )
91     {
92         if( SetPriorityClass( GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS )
93              || SetPriorityClass( GetCurrentProcess(), HIGH_PRIORITY_CLASS ) )
94         {
95             msg_Dbg( p_this, "raised process priority" );
96         }
97         else
98         {
99             msg_Dbg( p_this, "could not raise process priority" );
100         }
101     }
102
103     if( var_InheritBool( p_this, "one-instance" )
104      || ( var_InheritBool( p_this, "one-instance-when-started-from-file" )
105        && var_InheritBool( p_this, "started-from-file" ) ) )
106     {
107         HANDLE hmutex;
108
109         msg_Info( p_this, "one instance mode ENABLED");
110
111         /* Use a named mutex to check if another instance is already running */
112         if( !( hmutex = CreateMutex( 0, TRUE, L"VLC ipc " TEXT(VERSION) ) ) )
113         {
114             /* Failed for some reason. Just ignore the option and go on as
115              * normal. */
116             msg_Err( p_this, "one instance mode DISABLED "
117                      "(mutex couldn't be created)" );
118             return;
119         }
120
121         if( GetLastError() != ERROR_ALREADY_EXISTS )
122         {
123             /* We are the 1st instance. */
124             p_helper =
125                 vlc_custom_create( p_this, sizeof(*p_helper), "ipc helper" );
126
127             /* Run the helper thread */
128             hIPCHelperReady = CreateEvent( NULL, FALSE, FALSE, NULL );
129             hIPCHelper = _beginthreadex( NULL, 0, IPCHelperThread, p_helper,
130                                          0, NULL );
131             if( hIPCHelper )
132                 WaitForSingleObject( hIPCHelperReady, INFINITE );
133             else
134             {
135                 msg_Err( p_this, "one instance mode DISABLED "
136                          "(IPC helper thread couldn't be created)" );
137                 vlc_object_release (p_helper);
138                 p_helper = NULL;
139             }
140             CloseHandle( hIPCHelperReady );
141
142             /* Initialization done.
143              * Release the mutex to unblock other instances */
144             ReleaseMutex( hmutex );
145         }
146         else
147         {
148             /* Another instance is running */
149
150             HWND ipcwindow;
151
152             /* Wait until the 1st instance is initialized */
153             WaitForSingleObject( hmutex, INFINITE );
154
155             /* Locate the window created by the IPC helper thread of the
156              * 1st instance */
157             if( !( ipcwindow = FindWindow( 0, L"VLC ipc " TEXT(VERSION) ) ) )
158             {
159                 msg_Err( p_this, "one instance mode DISABLED "
160                          "(couldn't find 1st instance of program)" );
161                 ReleaseMutex( hmutex );
162                 return;
163             }
164
165             /* We assume that the remaining parameters are filenames
166              * and their input options */
167             if( i_argc > 0 )
168             {
169                 COPYDATASTRUCT wm_data;
170                 int i_opt;
171                 vlc_ipc_data_t *p_data;
172                 size_t i_data = sizeof (*p_data);
173
174                 for( i_opt = 0; i_opt < i_argc; i_opt++ )
175                 {
176                     i_data += sizeof (size_t);
177                     i_data += strlen( ppsz_argv[ i_opt ] ) + 1;
178                 }
179
180                 p_data = malloc( i_data );
181                 p_data->argc = i_argc;
182                 p_data->enqueue = var_InheritBool( p_this, "playlist-enqueue" );
183                 i_data = 0;
184                 for( i_opt = 0; i_opt < i_argc; i_opt++ )
185                 {
186                     size_t i_len = strlen( ppsz_argv[ i_opt ] ) + 1;
187                     /* Windows will never switch to an architecture
188                      * with stronger alignment requirements, right. */
189                     *((size_t *)(p_data->data + i_data)) = i_len;
190                     i_data += sizeof (size_t);
191                     memcpy( &p_data->data[i_data], ppsz_argv[ i_opt ], i_len );
192                     i_data += i_len;
193                 }
194                 i_data += sizeof (*p_data);
195
196                 /* Send our playlist items to the 1st instance */
197                 wm_data.dwData = 0;
198                 wm_data.cbData = i_data;
199                 wm_data.lpData = p_data;
200                 SendMessage( ipcwindow, WM_COPYDATA, 0, (LPARAM)&wm_data );
201             }
202
203             /* Initialization done.
204              * Release the mutex to unblock other instances */
205             ReleaseMutex( hmutex );
206
207             /* Bye bye */
208             system_End( );
209             exit( 0 );
210         }
211     }
212 #endif
213 }
214
215 #if !VLC_WINSTORE_APP
216 static unsigned __stdcall IPCHelperThread( void *data )
217 {
218     vlc_object_t *p_this = data;
219     HWND ipcwindow;
220     MSG message;
221
222     ipcwindow =
223         CreateWindow( L"STATIC",                     /* name of window class */
224                   L"VLC ipc " TEXT(VERSION),               /* window title bar text */
225                   0,                                         /* window style */
226                   0,                                 /* default X coordinate */
227                   0,                                 /* default Y coordinate */
228                   0,                                         /* window width */
229                   0,                                        /* window height */
230                   NULL,                                  /* no parent window */
231                   NULL,                            /* no menu in this window */
232                   GetModuleHandle(NULL),  /* handle of this program instance */
233                   NULL );                               /* sent to WM_CREATE */
234
235     SetWindowLongPtr( ipcwindow, GWLP_WNDPROC, (LRESULT)WMCOPYWNDPROC );
236     SetWindowLongPtr( ipcwindow, GWLP_USERDATA, (LONG_PTR)p_this );
237
238     /* Signal the creation of the thread and events queue */
239     SetEvent( hIPCHelperReady );
240
241     while( GetMessage( &message, NULL, 0, 0 ) )
242     {
243         TranslateMessage( &message );
244         DispatchMessage( &message );
245     }
246     return 0;
247 }
248
249 LRESULT CALLBACK WMCOPYWNDPROC( HWND hwnd, UINT uMsg, WPARAM wParam,
250                                 LPARAM lParam )
251 {
252     if( uMsg == WM_QUIT  )
253     {
254         PostQuitMessage( 0 );
255     }
256     else if( uMsg == WM_COPYDATA )
257     {
258         COPYDATASTRUCT *pwm_data = (COPYDATASTRUCT*)lParam;
259         vlc_object_t *p_this;
260         playlist_t *p_playlist;
261
262         p_this = (vlc_object_t *)
263             (uintptr_t)GetWindowLongPtr( hwnd, GWLP_USERDATA );
264
265         if( !p_this ) return 0;
266
267         /* Add files to the playlist */
268         p_playlist = pl_Get( p_this );
269
270         if( pwm_data->lpData )
271         {
272             char **ppsz_argv;
273             vlc_ipc_data_t *p_data = (vlc_ipc_data_t *)pwm_data->lpData;
274             size_t i_data = 0;
275             int i_argc = p_data->argc, i_opt, i_options;
276
277             ppsz_argv = (char **)malloc( i_argc * sizeof(char *) );
278             for( i_opt = 0; i_opt < i_argc; i_opt++ )
279             {
280                 ppsz_argv[i_opt] = p_data->data + i_data + sizeof(size_t);
281                 i_data += sizeof(size_t) + *((size_t *)(p_data->data + i_data));
282             }
283
284             for( i_opt = 0; i_opt < i_argc; i_opt++ )
285             {
286                 i_options = 0;
287
288                 /* Count the input options */
289                 while( i_opt + i_options + 1 < i_argc &&
290                         *ppsz_argv[ i_opt + i_options + 1 ] == ':' )
291                 {
292                     i_options++;
293                 }
294
295 #warning URI conversion must be done in calling process instead!
296                 /* FIXME: This breaks relative paths if calling vlc.exe is
297                  * started from a different working directory. */
298                 char *psz_URI = NULL;
299                 if( strstr( ppsz_argv[i_opt], "://" ) == NULL )
300                     psz_URI = vlc_path2uri( ppsz_argv[i_opt], NULL );
301                 playlist_AddExt( p_playlist,
302                         (psz_URI != NULL) ? psz_URI : ppsz_argv[i_opt],
303                         NULL, PLAYLIST_APPEND |
304                         ( ( i_opt || p_data->enqueue ) ? 0 : PLAYLIST_GO ),
305                         PLAYLIST_END, -1,
306                         i_options,
307                         (char const **)( i_options ? &ppsz_argv[i_opt+1] : NULL ),
308                         VLC_INPUT_OPTION_TRUSTED,
309                         true, pl_Unlocked );
310
311                 i_opt += i_options;
312                 free( psz_URI );
313             }
314
315             free( ppsz_argv );
316         }
317     }
318
319     return DefWindowProc( hwnd, uMsg, wParam, lParam );
320 }
321 #endif
322
323 /**
324  * Cleans up after system_Init() and system_Configure().
325  */
326 void system_End(void)
327 {
328 #if !VLC_WINSTORE_APP
329     HWND ipcwindow;
330
331     /* FIXME: thread-safety... */
332     if (p_helper)
333     {
334         if( ( ipcwindow = FindWindow( 0, L"VLC ipc " TEXT(VERSION) ) ) != 0 )
335         {
336             SendMessage( ipcwindow, WM_QUIT, 0, 0 );
337         }
338         vlc_object_release (p_helper);
339         p_helper = NULL;
340     }
341
342     timeEndPeriod(5);
343 #endif
344
345     /* XXX: In theory, we should not call this if WSAStartup() failed. */
346     WSACleanup();
347 }