>From 9f6f072ec19fafe7579d4c90f0483c91ec135503 Mon Sep 17 00:00:00 2001
From: KO Myung-Hun <komh@chollian.net>
Date: Wed, 14 Nov 2012 22:41:41 +0900
Subject: [PATCH] OS/2: implement one-instance feature

Signed-off-by: Jean-Baptiste Kempf <jb@videolan.org>
(cherry picked from commit 00c5b9f871961c2600ce62d31938995b657a6a20)

Conflicts:

	src/libvlc-module.c
	src/libvlc.c
	src/libvlc.h
	src/os2/specific.c
---
 src/libvlc-module.c |    6 +-
 src/os2/specific.c  |  232 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 234 insertions(+), 4 deletions(-)

diff --git a/src/libvlc-module.c b/src/libvlc-module.c
index 13bc29a..33d795c 100644
--- a/src/libvlc-module.c
+++ b/src/libvlc-module.c
@@ -1227,12 +1227,12 @@ static const char *const ppsz_prefres[] = {
     "Log all VLC messages to syslog (UNIX systems)." )
 
 #define ONEINSTANCE_TEXT N_("Allow only one running instance")
-#if defined( WIN32 )
+#if defined( WIN32 ) || defined( __OS2__ )
 #define ONEINSTANCE_LONGTEXT N_( \
     "Allowing only one running instance of VLC can sometimes be useful, " \
     "for example if you associated VLC with some media types and you " \
     "don't want a new instance of VLC to be opened each time you " \
-    "double-click on a file in the explorer. This option will allow you " \
+    "open a file in your file manager. This option will allow you " \
     "to play the file with the already running instance or enqueue it.")
 #elif defined( HAVE_DBUS )
 #define ONEINSTANCE_LONGTEXT N_( \
@@ -2151,7 +2151,7 @@ vlc_module_begin ()
               INHIBIT_LONGTEXT, true )
 #endif
 
-#if defined(WIN32) || defined(HAVE_DBUS)
+#if defined(WIN32) || defined(HAVE_DBUS) || defined(__OS2__)
     add_bool( "one-instance", 0, ONEINSTANCE_TEXT,
               ONEINSTANCE_LONGTEXT, true )
     add_bool( "started-from-file", 0, STARTEDFROMFILE_TEXT,
diff --git a/src/os2/specific.c b/src/os2/specific.c
index 6d2e61c..81a1aea 100644
--- a/src/os2/specific.c
+++ b/src/os2/specific.c
@@ -24,12 +24,99 @@
 
 #include <vlc_common.h>
 #include "../libvlc.h"
+#include <vlc_playlist.h>
+#include <vlc_url.h>
 
 #include <fcntl.h>
 #include <io.h>
 
+#define VLC_IPC_PIPE "\\PIPE\\VLC\\IPC\\"VERSION
+
+#define IPC_CMD_GO      0x00
+#define IPC_CMD_ENQUEUE 0x01
+#define IPC_CMD_QUIT    0xFF
+
 extern int _fmode_bin;
 
+static HPIPE hpipeIPC     = NULLHANDLE;
+static int   tidIPCFirst  = -1;
+static int   tidIPCHelper = -1;
+
+static void IPCHelperThread( void *arg )
+{
+    vlc_object_t *p_this = arg;
+
+    ULONG  ulCmd;
+    int    i_argc;
+    char **ppsz_argv;
+    size_t i_len;
+    ULONG  cbActual;
+    int    i_options;
+
+    /* Add files to the playlist */
+    playlist_t *p_playlist = pl_Get( p_this );
+
+    do
+    {
+        DosConnectNPipe( hpipeIPC );
+
+        /* Read command */
+        DosRead( hpipeIPC, &ulCmd, sizeof( ulCmd ), &cbActual );
+        if( ulCmd == IPC_CMD_QUIT )
+            continue;
+
+        /* Read a count of arguments */
+        DosRead( hpipeIPC, &i_argc, sizeof( i_argc ), &cbActual );
+
+        ppsz_argv = malloc( i_argc * sizeof( *ppsz_argv ));
+
+        for( int i_opt = 0; i_opt < i_argc; i_opt++ )
+        {
+            /* Read a length of argv */
+            DosRead( hpipeIPC, &i_len, sizeof( i_len ), &cbActual );
+
+            ppsz_argv[ i_opt ] = malloc( i_len );
+
+            /* Read argv */
+            DosRead( hpipeIPC, ppsz_argv[ i_opt ], i_len, &cbActual );
+        }
+
+        for( int i_opt = 0; i_opt < i_argc;)
+        {
+            i_options = 0;
+
+            /* Count the input options */
+            while( i_opt + i_options + 1 < i_argc &&
+                   *ppsz_argv[ i_opt + i_options + 1 ] == ':' )
+                i_options++;
+
+            playlist_AddExt( p_playlist, ppsz_argv[ i_opt ], NULL,
+                             PLAYLIST_APPEND |
+                             (( i_opt || ulCmd == IPC_CMD_ENQUEUE ) ?
+                                    0 : PLAYLIST_GO ),
+                             PLAYLIST_END, -1, i_options,
+                             ( char const ** )
+                                 ( i_options ? &ppsz_argv[ i_opt + 1 ] :
+                                               NULL ),
+                             VLC_INPUT_OPTION_TRUSTED,
+                             true, pl_Unlocked );
+
+            for( ; i_options >= 0; i_options-- )
+                free( ppsz_argv[ i_opt++ ]);
+        }
+
+        free( ppsz_argv );
+    } while( !DosDisConnectNPipe( hpipeIPC ) && ulCmd != IPC_CMD_QUIT );
+
+    vlc_object_release( p_this );
+
+    DosClose( hpipeIPC );
+    hpipeIPC = NULLHANDLE;
+
+    tidIPCFirst  = -1;
+    tidIPCHelper = -1;
+}
+
 void system_Init( void )
 {
     HMODULE hmod;
@@ -58,7 +145,6 @@ void system_Init( void )
 
 void system_Configure( libvlc_int_t *p_this, int i_argc, const char *const ppsz_argv[] )
 {
-    VLC_UNUSED( i_argc ); VLC_UNUSED( ppsz_argv );
     if( var_InheritBool( p_this, "high-priority" ) )
     {
         if( !DosSetPriority( PRTYS_PROCESS, PRTYC_REGULAR, PRTYD_MAXIMUM, 0 ) )
@@ -70,10 +156,154 @@ void system_Configure( libvlc_int_t *p_this, int i_argc, const char *const ppsz_
             msg_Dbg( p_this, "could not raise process priority" );
         }
     }
+
+    if( var_InheritBool( p_this, "one-instance" )
+        || ( var_InheritBool( p_this, "one-instance-when-started-from-file" )
+             && var_InheritBool( p_this, "started-from-file" ) ) )
+    {
+        HPIPE hpipe;
+        ULONG ulAction;
+        ULONG rc;
+
+        msg_Info( p_this, "one instance mode ENABLED");
+
+        /* Use a named pipe to check if another instance is already running */
+        for(;;)
+        {
+            rc = DosOpen( VLC_IPC_PIPE, &hpipe, &ulAction, 0, 0,
+                          OPEN_ACTION_OPEN_IF_EXISTS,
+                          OPEN_ACCESS_READWRITE | OPEN_SHARE_DENYREADWRITE |
+                          OPEN_FLAGS_FAIL_ON_ERROR,
+                          NULL );
+
+            if( rc == ERROR_PIPE_BUSY )
+                DosWaitNPipe( VLC_IPC_PIPE, -1 );
+            else
+                break;
+        }
+
+        if( rc )
+        {
+            rc = DosCreateNPipe( VLC_IPC_PIPE,  &hpipeIPC,
+                                 NP_ACCESS_DUPLEX,
+                                 NP_WAIT | NP_TYPE_MESSAGE |
+                                 NP_READMODE_MESSAGE | 0x01,
+                                 32768, 32768, 0 );
+            if( rc )
+            {
+                /* Failed to create a named pipe. Just ignore the option and
+                 * go on as normal. */
+                msg_Err( p_this, "one instance mode DISABLED "
+                         "(a named pipe couldn't be created)" );
+                return;
+            }
+
+            /* We are the 1st instance. */
+            vlc_object_t* p_helper = vlc_custom_create( p_this,
+                                                        sizeof( *p_helper ),
+                                                        "ipc helper" );
+
+            /* Save the tid of the first instance */
+            tidIPCFirst = _gettid();
+
+            /* Run the helper thread */
+            tidIPCHelper = _beginthread( IPCHelperThread, NULL, 1024 * 1024,
+                                         p_helper );
+            if( tidIPCHelper == -1 )
+            {
+                msg_Err( p_this, "one instance mode DISABLED "
+                         "(IPC helper thread couldn't be created)");
+                vlc_object_release( p_helper );
+
+                tidIPCFirst = -1;
+            }
+        }
+        else
+        {
+            /* Another instance is running */
+            ULONG ulCmd = var_InheritBool( p_this, "playlist-enqueue") ?
+                              IPC_CMD_ENQUEUE : IPC_CMD_GO;
+            ULONG cbActual;
+
+            /* Write a command */
+            DosWrite( hpipe, &ulCmd, sizeof( ulCmd ), &cbActual );
+
+            /* We assume that the remaining parameters are filenames
+             * and their input options */
+
+            /* Write a count of arguments */
+            DosWrite( hpipe, &i_argc, sizeof( i_argc ), &cbActual );
+
+            for( int i_opt = 0; i_opt < i_argc; i_opt++ )
+            {
+                /* We need to resolve relative paths in this instance */
+                char *mrl;
+                if( strstr( ppsz_argv[ i_opt ], "://" ))
+                    mrl = strdup( ppsz_argv[ i_opt ] );
+                else
+                    mrl = make_URI( ppsz_argv[ i_opt ], NULL );
+
+                if( !mrl )
+                    mrl = ( char * )ppsz_argv[ i_opt ];
+
+                size_t i_len = strlen( mrl ) + 1;
+
+                /* Write a length of an argument */
+                DosWrite( hpipe, &i_len, sizeof( i_len ), &cbActual );
+
+                /* Write an argument */
+                DosWrite( hpipe, mrl, i_len, &cbActual );
+
+                if( mrl != ppsz_argv[ i_opt ])
+                    free( mrl );
+            }
+
+            /* Close a named pipe of a client side */
+            DosClose( hpipe );
+
+            /* Bye bye */
+            system_End();
+            exit( 0 );
+        }
+    }
 }
 
+/**
+ * Cleans up after system_Init() and system_Configure().
+ */
 void system_End( void )
 {
     free( psz_vlcpath );
     psz_vlcpath = NULL;
+
+    if( tidIPCFirst == _gettid())
+    {
+        HPIPE hpipe;
+        ULONG ulAction;
+        ULONG cbActual;
+        ULONG rc;
+
+        do
+        {
+            rc = DosOpen( VLC_IPC_PIPE, &hpipe, &ulAction, 0, 0,
+                          OPEN_ACTION_OPEN_IF_EXISTS,
+                          OPEN_ACCESS_READWRITE | OPEN_SHARE_DENYREADWRITE |
+                          OPEN_FLAGS_FAIL_ON_ERROR,
+                          NULL );
+
+            if( rc == ERROR_PIPE_BUSY )
+                DosWaitNPipe( VLC_IPC_PIPE, -1 );
+            else if( rc )
+                DosSleep( 1 );
+        } while( rc );
+
+        /* Ask for IPCHelper to quit */
+        ULONG ulCmd = IPC_CMD_QUIT;
+        DosWrite( hpipe, &ulCmd, sizeof( ulCmd ), &cbActual );
+
+        DosClose( hpipe );
+
+        TID tid = tidIPCHelper;
+        DosWaitThread( &tid, DCWW_WAIT );
+    }
 }
-- 
1.7.3.2

