]> git.sesse.net Git - vlc/blob - src/os2/specific.c
OS/2: implement one-instance feature
[vlc] / src / os2 / specific.c
1 /*****************************************************************************
2  * specific.c: OS/2 specific features
3  *****************************************************************************
4  * Copyright (C) 2010 KO Myung-Hun
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation; either version 2.1 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19  *****************************************************************************/
20
21 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 #endif
24
25 #include <vlc_common.h>
26 #include "../libvlc.h"
27 #include <vlc_playlist.h>
28 #include <vlc_url.h>
29
30 #include <fcntl.h>
31 #include <io.h>
32
33 #define VLC_IPC_PIPE "\\PIPE\\VLC\\IPC\\"VERSION
34
35 #define IPC_CMD_GO      0x00
36 #define IPC_CMD_ENQUEUE 0x01
37 #define IPC_CMD_QUIT    0xFF
38
39 extern int _fmode_bin;
40
41 static HPIPE hpipeIPC     = NULLHANDLE;
42 static int   tidIPCFirst  = -1;
43 static int   tidIPCHelper = -1;
44
45 static void IPCHelperThread( void *arg )
46 {
47     vlc_object_t *p_this = arg;
48
49     ULONG  ulCmd;
50     int    i_argc;
51     char **ppsz_argv;
52     size_t i_len;
53     ULONG  cbActual;
54     int    i_options;
55
56     /* Add files to the playlist */
57     playlist_t *p_playlist = pl_Get( p_this );
58
59     do
60     {
61         DosConnectNPipe( hpipeIPC );
62
63         /* Read command */
64         DosRead( hpipeIPC, &ulCmd, sizeof( ulCmd ), &cbActual );
65         if( ulCmd == IPC_CMD_QUIT )
66             continue;
67
68         /* Read a count of arguments */
69         DosRead( hpipeIPC, &i_argc, sizeof( i_argc ), &cbActual );
70
71         ppsz_argv = malloc( i_argc * sizeof( *ppsz_argv ));
72
73         for( int i_opt = 0; i_opt < i_argc; i_opt++ )
74         {
75             /* Read a length of argv */
76             DosRead( hpipeIPC, &i_len, sizeof( i_len ), &cbActual );
77
78             ppsz_argv[ i_opt ] = malloc( i_len );
79
80             /* Read argv */
81             DosRead( hpipeIPC, ppsz_argv[ i_opt ], i_len, &cbActual );
82         }
83
84         for( int i_opt = 0; i_opt < i_argc;)
85         {
86             i_options = 0;
87
88             /* Count the input options */
89             while( i_opt + i_options + 1 < i_argc &&
90                    *ppsz_argv[ i_opt + i_options + 1 ] == ':' )
91                 i_options++;
92
93             playlist_AddExt( p_playlist, ppsz_argv[ i_opt ], NULL,
94                              PLAYLIST_APPEND |
95                              (( i_opt || ulCmd == IPC_CMD_ENQUEUE ) ?
96                                     0 : PLAYLIST_GO ),
97                              PLAYLIST_END, -1, i_options,
98                              ( char const ** )
99                                  ( i_options ? &ppsz_argv[ i_opt + 1 ] :
100                                                NULL ),
101                              VLC_INPUT_OPTION_TRUSTED,
102                              true, pl_Unlocked );
103
104             for( ; i_options >= 0; i_options-- )
105                 free( ppsz_argv[ i_opt++ ]);
106         }
107
108         free( ppsz_argv );
109     } while( !DosDisConnectNPipe( hpipeIPC ) && ulCmd != IPC_CMD_QUIT );
110
111     vlc_object_release( p_this );
112
113     DosClose( hpipeIPC );
114     hpipeIPC = NULLHANDLE;
115
116     tidIPCFirst  = -1;
117     tidIPCHelper = -1;
118 }
119
120 void system_Init( void )
121 {
122     /* Set the default file-translation mode */
123     _fmode_bin = 1;
124     setmode( fileno( stdin ), O_BINARY ); /* Needed for pipes */
125 }
126
127 void system_Configure( libvlc_int_t *p_this, int i_argc, const char *const ppsz_argv[] )
128 {
129     if( var_InheritBool( p_this, "high-priority" ) )
130     {
131         if( !DosSetPriority( PRTYS_PROCESS, PRTYC_REGULAR, PRTYD_MAXIMUM, 0 ) )
132         {
133             msg_Dbg( p_this, "raised process priority" );
134         }
135         else
136         {
137             msg_Dbg( p_this, "could not raise process priority" );
138         }
139     }
140
141     if( var_InheritBool( p_this, "one-instance" )
142         || ( var_InheritBool( p_this, "one-instance-when-started-from-file" )
143              && var_InheritBool( p_this, "started-from-file" ) ) )
144     {
145         HPIPE hpipe;
146         ULONG ulAction;
147         ULONG rc;
148
149         msg_Info( p_this, "one instance mode ENABLED");
150
151         /* Use a named pipe to check if another instance is already running */
152         for(;;)
153         {
154             rc = DosOpen( VLC_IPC_PIPE, &hpipe, &ulAction, 0, 0,
155                           OPEN_ACTION_OPEN_IF_EXISTS,
156                           OPEN_ACCESS_READWRITE | OPEN_SHARE_DENYREADWRITE |
157                           OPEN_FLAGS_FAIL_ON_ERROR,
158                           NULL );
159
160             if( rc == ERROR_PIPE_BUSY )
161                 DosWaitNPipe( VLC_IPC_PIPE, -1 );
162             else
163                 break;
164         }
165
166         if( rc )
167         {
168             rc = DosCreateNPipe( VLC_IPC_PIPE,  &hpipeIPC,
169                                  NP_ACCESS_DUPLEX,
170                                  NP_WAIT | NP_TYPE_MESSAGE |
171                                  NP_READMODE_MESSAGE | 0x01,
172                                  32768, 32768, 0 );
173             if( rc )
174             {
175                 /* Failed to create a named pipe. Just ignore the option and
176                  * go on as normal. */
177                 msg_Err( p_this, "one instance mode DISABLED "
178                          "(a named pipe couldn't be created)" );
179                 return;
180             }
181
182             /* We are the 1st instance. */
183             vlc_object_t* p_helper = vlc_custom_create( p_this,
184                                                         sizeof( *p_helper ),
185                                                         "ipc helper" );
186
187             /* Save the tid of the first instance */
188             tidIPCFirst = _gettid();
189
190             /* Run the helper thread */
191             tidIPCHelper = _beginthread( IPCHelperThread, NULL, 1024 * 1024,
192                                          p_helper );
193             if( tidIPCHelper == -1 )
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
199                 tidIPCFirst = -1;
200             }
201         }
202         else
203         {
204             /* Another instance is running */
205             ULONG ulCmd = var_InheritBool( p_this, "playlist-enqueue") ?
206                               IPC_CMD_ENQUEUE : IPC_CMD_GO;
207             ULONG cbActual;
208
209             /* Write a command */
210             DosWrite( hpipe, &ulCmd, sizeof( ulCmd ), &cbActual );
211
212             /* We assume that the remaining parameters are filenames
213              * and their input options */
214
215             /* Write a count of arguments */
216             DosWrite( hpipe, &i_argc, sizeof( i_argc ), &cbActual );
217
218             for( int i_opt = 0; i_opt < i_argc; i_opt++ )
219             {
220                 /* We need to resolve relative paths in this instance */
221                 char *mrl;
222                 if( strstr( ppsz_argv[ i_opt ], "://" ))
223                     mrl = strdup( ppsz_argv[ i_opt ] );
224                 else
225                     mrl = vlc_path2uri( ppsz_argv[ i_opt ], NULL );
226
227                 if( !mrl )
228                     mrl = ( char * )ppsz_argv[ i_opt ];
229
230                 size_t i_len = strlen( mrl ) + 1;
231
232                 /* Write a length of an argument */
233                 DosWrite( hpipe, &i_len, sizeof( i_len ), &cbActual );
234
235                 /* Write an argument */
236                 DosWrite( hpipe, mrl, i_len, &cbActual );
237
238                 if( mrl != ppsz_argv[ i_opt ])
239                     free( mrl );
240             }
241
242             /* Close a named pipe of a client side */
243             DosClose( hpipe );
244
245             /* Bye bye */
246             system_End();
247             exit( 0 );
248         }
249     }
250 }
251
252 /**
253  * Cleans up after system_Init() and system_Configure().
254  */
255 void system_End(void)
256 {
257     if( tidIPCFirst == _gettid())
258     {
259         HPIPE hpipe;
260         ULONG ulAction;
261         ULONG cbActual;
262         ULONG rc;
263
264         do
265         {
266             rc = DosOpen( VLC_IPC_PIPE, &hpipe, &ulAction, 0, 0,
267                           OPEN_ACTION_OPEN_IF_EXISTS,
268                           OPEN_ACCESS_READWRITE | OPEN_SHARE_DENYREADWRITE |
269                           OPEN_FLAGS_FAIL_ON_ERROR,
270                           NULL );
271
272             if( rc == ERROR_PIPE_BUSY )
273                 DosWaitNPipe( VLC_IPC_PIPE, -1 );
274             else if( rc )
275                 DosSleep( 1 );
276         } while( rc );
277
278         /* Ask for IPCHelper to quit */
279         ULONG ulCmd = IPC_CMD_QUIT;
280         DosWrite( hpipe, &ulCmd, sizeof( ulCmd ), &cbActual );
281
282         DosClose( hpipe );
283
284         TID tid = tidIPCHelper;
285         DosWaitThread( &tid, DCWW_WAIT );
286     }
287 }
288