]> git.sesse.net Git - vlc/blob - src/win32/specific.c
libvlc os-specific path discovery
[vlc] / src / win32 / specific.c
1 /*****************************************************************************
2  * specific.c: Win32 specific initilization
3  *****************************************************************************
4  * Copyright (C) 2001-2004 the VideoLAN team
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
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 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 General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, 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_charset.h>
33
34 #include "../extras/getopt.h"
35
36 #if !defined( UNDER_CE )
37 #   include <io.h>
38 #   include <fcntl.h>
39 #   include  <mmsystem.h>
40 #endif
41
42 #include <winsock.h>
43
44 /*****************************************************************************
45  * system_Init: initialize winsock and misc other things.
46  *****************************************************************************/
47 void system_Init( libvlc_int_t *p_this, int *pi_argc, const char *ppsz_argv[] )
48 {
49     VLC_UNUSED( p_this ); VLC_UNUSED( pi_argc ); VLC_UNUSED( ppsz_argv );
50     WSADATA Data;
51     MEMORY_BASIC_INFORMATION mbi;
52
53     /* Get our full path */
54     char psz_path[MAX_PATH];
55     char *psz_vlc;
56
57     wchar_t psz_wpath[MAX_PATH];
58     if( VirtualQuery(system_Init, &mbi, sizeof(mbi) ) )
59     {
60         HMODULE hMod = (HMODULE) mbi.AllocationBase;
61         if( GetModuleFileName( hMod, psz_wpath, MAX_PATH ) )
62         {
63             WideCharToMultiByte( CP_UTF8, 0, psz_wpath, -1,
64                                 psz_path, MAX_PATH, NULL, NULL );
65         }
66         else psz_path[0] = '\0';
67     }
68
69     if( (psz_vlc = strrchr( psz_path, '\\' )) ) *psz_vlc = '\0';
70
71 #ifndef HAVE_RELEASE
72     {
73         /* remove trailing \.libs from executable dir path if seen,
74            we assume we are running vlc through libtool wrapper in build dir */
75         int offset  = strlen(psz_path)-sizeof("\\.libs")+1;
76         if( offset > 0 )
77         {
78             psz_vlc = psz_path+offset;
79             if( ! strcmp(psz_vlc, "\\.libs") ) *psz_vlc = '\0';
80         }
81     }
82 #endif
83
84     psz_vlcpath = strdup( psz_path );
85
86     /* Set the default file-translation mode */
87 #if !defined( UNDER_CE )
88     _fmode = _O_BINARY;
89     _setmode( _fileno( stdin ), _O_BINARY ); /* Needed for pipes */
90
91     timeBeginPeriod(5);
92 #endif
93
94     /* Call mdate() once to make sure it is initialized properly */
95     mdate();
96
97     /* WinSock Library Init. */
98     if( !WSAStartup( MAKEWORD( 2, 2 ), &Data ) )
99     {
100         /* Aah, pretty useless check, we should always have Winsock 2.2
101          * since it appeared in Win98. */
102         if( LOBYTE( Data.wVersion ) != 2 || HIBYTE( Data.wVersion ) != 2 )
103             /* We could not find a suitable WinSock DLL. */
104             WSACleanup( );
105         else
106             /* Everything went ok. */
107             return;
108     }
109
110     /* Let's try with WinSock 1.1 */
111     if( !WSAStartup( MAKEWORD( 1, 1 ), &Data ) )
112     {
113         /* Confirm that the WinSock DLL supports 1.1.*/
114         if( LOBYTE( Data.wVersion ) != 1 || HIBYTE( Data.wVersion ) != 1 )
115             /* We could not find a suitable WinSock DLL. */
116             WSACleanup( );
117         else
118             /* Everything went ok. */
119             return;
120     }
121
122     fprintf( stderr, "error: can't initialize WinSocks\n" );
123 }
124
125 /*****************************************************************************
126  * system_Configure: check for system specific configuration options.
127  *****************************************************************************/
128 static unsigned __stdcall IPCHelperThread( void * );
129 LRESULT CALLBACK WMCOPYWNDPROC( HWND, UINT, WPARAM, LPARAM );
130 static vlc_object_t *p_helper = NULL;
131 static unsigned long hIPCHelper;
132 static HANDLE hIPCHelperReady;
133
134 typedef struct
135 {
136   int argc;
137   int enqueue;
138   char data[];
139 } vlc_ipc_data_t;
140
141 void system_Configure( libvlc_int_t *p_this, int *pi_argc, const char *ppsz_argv[] )
142 {
143 #if !defined( UNDER_CE )
144     /* Raise default priority of the current process */
145 #ifndef ABOVE_NORMAL_PRIORITY_CLASS
146 #   define ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
147 #endif
148     if( var_InheritBool( p_this, "high-priority" ) )
149     {
150         if( SetPriorityClass( GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS )
151              || SetPriorityClass( GetCurrentProcess(), HIGH_PRIORITY_CLASS ) )
152         {
153             msg_Dbg( p_this, "raised process priority" );
154         }
155         else
156         {
157             msg_Dbg( p_this, "could not raise process priority" );
158         }
159     }
160
161     if( var_InheritBool( p_this, "one-instance" )
162      || ( var_InheritBool( p_this, "one-instance-when-started-from-file" )
163        && var_InheritBool( p_this, "started-from-file" ) ) )
164     {
165         HANDLE hmutex;
166
167         msg_Info( p_this, "one instance mode ENABLED");
168
169         /* Use a named mutex to check if another instance is already running */
170         if( !( hmutex = CreateMutex( 0, TRUE, L"VLC ipc "VERSION ) ) )
171         {
172             /* Failed for some reason. Just ignore the option and go on as
173              * normal. */
174             msg_Err( p_this, "one instance mode DISABLED "
175                      "(mutex couldn't be created)" );
176             return;
177         }
178
179         if( GetLastError() != ERROR_ALREADY_EXISTS )
180         {
181             /* We are the 1st instance. */
182             static const char typename[] = "ipc helper";
183             p_helper =
184                 vlc_custom_create( p_this, sizeof(vlc_object_t),
185                                    VLC_OBJECT_GENERIC, typename );
186
187             /* Run the helper thread */
188             hIPCHelperReady = CreateEvent( NULL, FALSE, FALSE, NULL );
189             hIPCHelper = _beginthreadex( NULL, 0, IPCHelperThread, p_helper,
190                                          0, NULL );
191             if( hIPCHelper )
192                 WaitForSingleObject( hIPCHelperReady, INFINITE );
193             else
194             {
195                 msg_Err( p_this, "one instance mode DISABLED "
196                          "(IPC helper thread couldn't be created)" );
197                 vlc_object_release (p_helper);
198                 p_helper = NULL;
199             }
200             vlc_object_attach (p_helper, p_this);
201             CloseHandle( hIPCHelperReady );
202
203             /* Initialization done.
204              * Release the mutex to unblock other instances */
205             ReleaseMutex( hmutex );
206         }
207         else
208         {
209             /* Another instance is running */
210
211             HWND ipcwindow;
212
213             /* Wait until the 1st instance is initialized */
214             WaitForSingleObject( hmutex, INFINITE );
215
216             /* Locate the window created by the IPC helper thread of the
217              * 1st instance */
218             if( !( ipcwindow = FindWindow( 0, L"VLC ipc "VERSION ) ) )
219             {
220                 msg_Err( p_this, "one instance mode DISABLED "
221                          "(couldn't find 1st instance of program)" );
222                 ReleaseMutex( hmutex );
223                 return;
224             }
225
226             /* We assume that the remaining parameters are filenames
227              * and their input options */
228             if( *pi_argc - 1 >= optind )
229             {
230                 COPYDATASTRUCT wm_data;
231                 int i_opt;
232                 vlc_ipc_data_t *p_data;
233                 size_t i_data = sizeof (*p_data);
234
235                 for( i_opt = optind; i_opt < *pi_argc; i_opt++ )
236                 {
237                     i_data += sizeof (size_t);
238                     i_data += strlen( ppsz_argv[ i_opt ] ) + 1;
239                 }
240
241                 p_data = malloc( i_data );
242                 p_data->argc = *pi_argc - optind;
243                 p_data->enqueue = var_InheritBool( p_this, "playlist-enqueue" );
244                 i_data = 0;
245                 for( i_opt = optind; i_opt < *pi_argc; i_opt++ )
246                 {
247                     size_t i_len = strlen( ppsz_argv[ i_opt ] ) + 1;
248                     /* Windows will never switch to an architecture
249                      * with stronger alignment requirements, right. */
250                     *((size_t *)(p_data->data + i_data)) = i_len;
251                     i_data += sizeof (size_t);
252                     memcpy( &p_data->data[i_data], ppsz_argv[ i_opt ], i_len );
253                     i_data += i_len;
254                 }
255                 i_data += sizeof (*p_data);
256
257                 /* Send our playlist items to the 1st instance */
258                 wm_data.dwData = 0;
259                 wm_data.cbData = i_data;
260                 wm_data.lpData = p_data;
261                 SendMessage( ipcwindow, WM_COPYDATA, 0, (LPARAM)&wm_data );
262             }
263
264             /* Initialization done.
265              * Release the mutex to unblock other instances */
266             ReleaseMutex( hmutex );
267
268             /* Bye bye */
269             system_End( p_this );
270             exit( 0 );
271         }
272     }
273
274 #endif
275 }
276
277 static unsigned __stdcall IPCHelperThread( void *data )
278 {
279     vlc_object_t *p_this = data;
280     HWND ipcwindow;
281     MSG message;
282
283     ipcwindow =
284         CreateWindow( L"STATIC",                     /* name of window class */
285                   L"VLC ipc "VERSION,               /* window title bar text */
286                   0,                                         /* window style */
287                   0,                                 /* default X coordinate */
288                   0,                                 /* default Y coordinate */
289                   0,                                         /* window width */
290                   0,                                        /* window height */
291                   NULL,                                  /* no parent window */
292                   NULL,                            /* no menu in this window */
293                   GetModuleHandle(NULL),  /* handle of this program instance */
294                   NULL );                               /* sent to WM_CREATE */
295
296     SetWindowLongPtr( ipcwindow, GWLP_WNDPROC, (LRESULT)WMCOPYWNDPROC );
297     SetWindowLongPtr( ipcwindow, GWLP_USERDATA, (LONG_PTR)p_this );
298
299     /* Signal the creation of the thread and events queue */
300     SetEvent( hIPCHelperReady );
301
302     while( GetMessage( &message, NULL, 0, 0 ) )
303     {
304         TranslateMessage( &message );
305         DispatchMessage( &message );
306     }
307     return 0;
308 }
309
310 LRESULT CALLBACK WMCOPYWNDPROC( HWND hwnd, UINT uMsg, WPARAM wParam,
311                                 LPARAM lParam )
312 {
313     if( uMsg == WM_COPYDATA )
314     {
315         COPYDATASTRUCT *pwm_data = (COPYDATASTRUCT*)lParam;
316         vlc_object_t *p_this;
317         playlist_t *p_playlist;
318
319         p_this = (vlc_object_t *)
320             (uintptr_t)GetWindowLongPtr( hwnd, GWLP_USERDATA );
321
322         if( !p_this ) return 0;
323
324         /* Add files to the playlist */
325         p_playlist = pl_Hold( p_this );
326         if( !p_playlist ) return 0;
327
328         if( pwm_data->lpData )
329         {
330             char **ppsz_argv;
331             vlc_ipc_data_t *p_data = (vlc_ipc_data_t *)pwm_data->lpData;
332             size_t i_data = 0;
333             int i_argc = p_data->argc, i_opt, i_options;
334
335             ppsz_argv = (char **)malloc( i_argc * sizeof(char *) );
336             for( i_opt = 0; i_opt < i_argc; i_opt++ )
337             {
338                 ppsz_argv[i_opt] = p_data->data + i_data + sizeof(int);
339                 i_data += sizeof(int) + *((int *)(p_data->data + i_data));
340             }
341
342             for( i_opt = 0; i_opt < i_argc; i_opt++ )
343             {
344                 i_options = 0;
345
346                 /* Count the input options */
347                 while( i_opt + i_options + 1 < i_argc &&
348                        *ppsz_argv[ i_opt + i_options + 1 ] == ':' )
349                 {
350                     i_options++;
351                 }
352                 playlist_AddExt( p_playlist, ppsz_argv[i_opt],
353                   NULL, PLAYLIST_APPEND |
354                         ( ( i_opt || p_data->enqueue ) ? 0 : PLAYLIST_GO ),
355                   PLAYLIST_END, -1,
356                   i_options,
357                   (char const **)( i_options ? &ppsz_argv[i_opt+1] : NULL ),
358                   VLC_INPUT_OPTION_TRUSTED,
359                   true, pl_Unlocked );
360
361                 i_opt += i_options;
362             }
363
364             free( ppsz_argv );
365         }
366
367         vlc_object_release( p_playlist );
368     }
369
370     return DefWindowProc( hwnd, uMsg, wParam, lParam );
371 }
372
373 /*****************************************************************************
374  * system_End: terminate winsock.
375  *****************************************************************************/
376 void system_End( libvlc_int_t *p_this )
377 {
378     HWND ipcwindow;
379     if( p_this )
380     {
381         free( psz_vlcpath );
382         psz_vlcpath = NULL;
383     }
384
385     if( ( ipcwindow = FindWindow( 0, L"VLC ipc "VERSION ) ) != 0 )
386     {
387         SendMessage( ipcwindow, WM_QUIT, 0, 0 );
388     }
389
390     if (p_helper && p_helper->p_parent == VLC_OBJECT(p_this) )
391     {
392         /* FIXME: thread-safety... */
393         vlc_object_detach (p_helper);
394         vlc_object_release (p_helper);
395         p_helper = NULL;
396     }
397
398 #if !defined( UNDER_CE )
399     timeEndPeriod(5);
400 #endif
401
402     WSACleanup();
403 }