diff -Naur pcmanfm-20101030_10ae31a.orig/src/Makefile.am pcmanfm-20101030_10ae31a/src/Makefile.am --- pcmanfm-20101030_10ae31a.orig/src/Makefile.am 2010-10-30 20:36:02.000000000 +0200 +++ pcmanfm-20101030_10ae31a/src/Makefile.am 2010-10-31 09:13:48.000000000 +0100 @@ -16,7 +16,6 @@ pref.c pref.h \ utils.c utils.h \ gseal-gtk-compat.h \ - single-inst.c single-inst.h \ $(NULL) EXTRA_DIST= \ diff -Naur pcmanfm-20101030_10ae31a.orig/src/pcmanfm.c pcmanfm-20101030_10ae31a/src/pcmanfm.c --- pcmanfm-20101030_10ae31a.orig/src/pcmanfm.c 2010-10-30 20:36:02.000000000 +0200 +++ pcmanfm-20101030_10ae31a/src/pcmanfm.c 2010-10-31 09:25:45.000000000 +0100 @@ -31,6 +31,8 @@ #include /* socket is used to keep single instance */ #include +#include +#include #include #include /* for getcwd */ @@ -41,13 +43,14 @@ #include "volume-manager.h" #include "pref.h" #include "pcmanfm.h" -#include "single-inst.h" + +static int sock; +GIOChannel* io_channel = NULL; static int signal_pipe[2] = {-1, -1}; gboolean daemon_mode = FALSE; static char** files_to_open = NULL; -static int n_files_to_open = 0; static char* profile = NULL; static gboolean no_desktop = FALSE; static gboolean show_desktop = FALSE; @@ -80,25 +83,13 @@ { NULL } }; -/* single instance command id */ -enum { - CMD_INVALID, - CMD_CWD, - CMD_PROFILE, - CMD_DESKTOP, - CMD_DESKTOP_OFF, - CMD_DAEMON_MODE, - CMD_DESKTOP_PREF, - CMD_SET_WALLPAPER, - CMD_WALLPAPER_MODE, - CMD_SHOW_PREF, - CMD_FILES_TO_OPEN, - CMD_EOF -}; - static const char* valid_wallpaper_modes[] = {"color", "stretch", "fit", "center", "tile"}; +static gboolean single_instance_check(); +static void single_instance_finalize(); +static void get_socket_name(char* buf, int len); static gboolean pcmanfm_run(); +static gboolean on_socket_event(GIOChannel* ioc, GIOCondition cond, gpointer data); /* it's not safe to call gtk+ functions in unix signal handler * since the process is interrupted here and the state of gtk+ is unpredictable. */ @@ -121,97 +112,6 @@ return TRUE; } -static gboolean on_single_inst_command(int cmd, SingleInstCmdData* data) -{ - switch(cmd) - { - case CMD_CWD: - g_free(ipc_cwd); - ipc_cwd = single_inst_get_str(data, NULL); - break; - case CMD_PROFILE: - /* Not supported */ - break; - case CMD_DESKTOP: - single_inst_get_bool(data, &show_desktop); - break; - case CMD_DESKTOP_OFF: - single_inst_get_bool(data, &desktop_off); - break; - case CMD_DAEMON_MODE: - /* Not supported */ - break; - case CMD_DESKTOP_PREF: - single_inst_get_bool(data, &desktop_pref); - break; - case CMD_SET_WALLPAPER: - g_free(set_wallpaper); - set_wallpaper = single_inst_get_str(data, NULL); - break; - case CMD_WALLPAPER_MODE: - g_free(wallpaper_mode); - wallpaper_mode = single_inst_get_str(data, NULL); - break; - case CMD_SHOW_PREF: - single_inst_get_int(data, &show_pref); - break; - case CMD_FILES_TO_OPEN: - { - g_strfreev(files_to_open); - n_files_to_open = 0; - files_to_open = single_inst_get_strv(data, &n_files_to_open); - } - break; - case CMD_EOF: - { - int i; - /* canonicalize filename if needed. */ - for(i = 0; i < n_files_to_open; ++i) - { - char* file = files_to_open[i]; - char* scheme = g_uri_parse_scheme(file); - if(scheme) /* a valid URI */ - { - /* FIXME: should we canonicalize URIs? and how about file:///? */ - g_free(scheme); - } - else /* a file path */ - { - files_to_open[i] = fm_canonicalize_filename(file, ipc_cwd); - g_free(file); - } - } - - /* handle the parsed result and run the main program */ - pcmanfm_run(); - } - break; - } - return TRUE; -} - -/* we're not the first instance. pass the argv to the existing one. */ -static void pass_args_to_existing_instance() -{ - /* send our current working dir to existing instance via IPC. */ - ipc_cwd = g_get_current_dir(); - single_inst_send_str(CMD_CWD, ipc_cwd); - g_free(ipc_cwd); - - single_inst_send_bool(CMD_DESKTOP, show_desktop); - single_inst_send_bool(CMD_DESKTOP_OFF, desktop_off); - single_inst_send_bool(CMD_DESKTOP_PREF, desktop_pref); - single_inst_send_str(CMD_SET_WALLPAPER, set_wallpaper); - single_inst_send_str(CMD_WALLPAPER_MODE, wallpaper_mode); - single_inst_send_int(CMD_SHOW_PREF, show_pref); - /* single_inst_send_bool(CMD_FIND_FILES, find_files); */ - - single_inst_send_strv(CMD_FILES_TO_OPEN, files_to_open); - single_inst_send_bool(CMD_EOF, TRUE); /* all args have been sent. */ - - single_inst_finalize(); -} - int main(int argc, char** argv) { FmConfig* config; @@ -230,17 +130,10 @@ return 1; } - /* ensure that there is only one instance of pcmanfm. */ - switch(single_inst_init("pcmanfm", on_single_inst_command)) - { - case SINGLE_INST_CLIENT: /* we're not the first instance. */ - pass_args_to_existing_instance(); - gdk_notify_startup_complete(); - return 0; - case SINGLE_INST_ERROR: /* error happened. */ - single_inst_finalize(); - return 1; - } + /* ensure that there is only one instance of pcmanfm. + if there is an existing instance, command line arguments + will be passed to the existing instance, and exit() will be called here. */ + single_instance_check(); if(pipe(signal_pipe) == 0) { @@ -274,13 +167,240 @@ fm_volume_manager_finalize(); } - single_inst_finalize(); + single_instance_finalize(); + fm_gtk_finalize(); g_object_unref(config); return 0; } +inline static void buf_append_str(GByteArray* buf, const char* str) +{ + int len; + if(G_LIKELY(str)) + { + len = strlen(str) + 1; + g_byte_array_append(buf, (guint8*)&len, sizeof(len)); + g_byte_array_append(buf, (guint8*)str, len); + } + else + { + len = 0; + g_byte_array_append(buf, (guint8*)&len, sizeof(len)); + } +} + +inline static GByteArray* args_to_ipc_buf() +{ + int i, len; + GByteArray* buf = g_byte_array_sized_new(4096); + /* send our current working dir to existing instance via IPC. */ + ipc_cwd = g_get_current_dir(); + buf_append_str(buf, ipc_cwd); + g_free(ipc_cwd); + + /* g_byte_array_append(buf, (guint8*)&new_tab, sizeof(new_tab)); */ + g_byte_array_append(buf, (guint8*)&show_desktop, sizeof(show_desktop)); + g_byte_array_append(buf, (guint8*)&desktop_off, sizeof(desktop_off)); + g_byte_array_append(buf, (guint8*)&desktop_pref, sizeof(desktop_pref)); + buf_append_str(buf, set_wallpaper); + buf_append_str(buf, wallpaper_mode); + g_byte_array_append(buf, (guint8*)&show_pref, sizeof(show_pref)); + g_byte_array_append(buf, (guint8*)&find_files, sizeof(find_files)); + g_byte_array_append(buf, (guint8*)&no_desktop, sizeof(no_desktop)); + + len = files_to_open ? g_strv_length(files_to_open) : 0; + g_byte_array_append(buf, (guint8*)&len, sizeof(len)); + for(i = 0; i < len; ++i) + buf_append_str(buf, files_to_open[i]); + + return buf; +} + +inline static gboolean buf_read_bool(const char**p) +{ + gboolean ret; + memcpy(&ret, *p, sizeof(ret)); + *p += sizeof(ret); + return ret; +} + +inline static int buf_read_int(const char**p) +{ + int ret; + memcpy(&ret, *p, sizeof(ret)); + *p += sizeof(ret); + return ret; +} + +inline static char* buf_read_str(const char**p) +{ + char* ret; + int len = buf_read_int(p); + if(len > 0) + { + ret = g_malloc(len); + memcpy(ret, *p, len); + *p += len; + } + else + ret = NULL; + return ret; +} + +inline static void ipc_buf_to_args(GByteArray* buf) +{ + int i, len; + char* p = buf->data; + char* cwd = buf_read_str(&p); + /* new_tab = buf_read_bool(&p); */ + show_desktop = buf_read_bool(&p); + desktop_off = buf_read_bool(&p); + desktop_pref = buf_read_bool(&p); + g_free(set_wallpaper); + set_wallpaper = buf_read_str(&p); + g_free(wallpaper_mode); + wallpaper_mode = buf_read_str(&p); + show_pref = buf_read_int(&p); + find_files = buf_read_bool(&p); + no_desktop = buf_read_bool(&p); + + len = buf_read_int(&p); + /* g_debug("len = %d", len); */ + if(len > 0) + { + files_to_open = g_new(char*, len + 1); + for(i = 0; i < len; ++i) + { + char* file = buf_read_str(&p); + char* scheme = g_uri_parse_scheme(file); + if(scheme) /* a valid URI */ + { + /* FIXME: should we canonicalize URIs? and how about file:///? */ + files_to_open[i] = file; + g_free(scheme); + } + else /* a file path */ + { + files_to_open[i] = fm_canonicalize_filename(file, cwd); + g_free(file); + } + } + files_to_open[i] = NULL; + } + else + files_to_open = NULL; + g_free(cwd); +} + +gboolean on_socket_event( GIOChannel* ioc, GIOCondition cond, gpointer data ) +{ + int client, r; + socklen_t addr_len = 0; + struct sockaddr_un client_addr ={ 0 }; + static char buf[ 1024 ]; + GByteArray* args; + + if ( cond & G_IO_IN ) + { + client = accept( g_io_channel_unix_get_fd( ioc ), (struct sockaddr *)&client_addr, &addr_len ); + if ( client != -1 ) + { + args = g_byte_array_sized_new(4096); + while( (r = read( client, buf, sizeof(buf) )) > 0 ) + g_byte_array_append( args, (guint8*)buf, r); + shutdown( client, 2 ); + close( client ); + ipc_buf_to_args(args); + g_byte_array_free( args, TRUE ); + pcmanfm_run(); + } + } + return TRUE; +} + +void get_socket_name( char* buf, int len ) +{ + char* dpy = gdk_get_display(); + g_snprintf( buf, len, "/tmp/.pcmanfm-socket%s-%s", dpy, g_get_user_name() ); + g_free( dpy ); +} + +gboolean single_instance_check() +{ + struct sockaddr_un addr; + int addr_len; + int ret; + int reuse; + + if((sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) + { + ret = 1; + goto _exit; + } + + /* FIXME: use abstract socket */ + addr.sun_family = AF_UNIX; + get_socket_name(addr.sun_path, sizeof( addr.sun_path )); +#ifdef SUN_LEN + addr_len = SUN_LEN(&addr); +#else + addr_len = strlen( addr.sun_path ) + sizeof( addr.sun_family ); +#endif + + /* try to connect to existing instance */ + if(connect(sock, (struct sockaddr*)&addr, addr_len) == 0) + { + /* connected successfully */ + GByteArray* buf = args_to_ipc_buf(); + write(sock, buf->data, buf->len); + g_byte_array_free(buf, TRUE); + + shutdown( sock, 2 ); + close( sock ); + ret = 0; + goto _exit; + } + + /* There is no existing server, and we are in the first instance. */ + unlink( addr.sun_path ); /* delete old socket file if it exists. */ + reuse = 1; + ret = setsockopt( sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse) ); + if(bind(sock, (struct sockaddr*)&addr, addr_len) == -1) + { + ret = 1; + goto _exit; + } + + io_channel = g_io_channel_unix_new(sock); + g_io_channel_set_encoding(io_channel, NULL, NULL); + g_io_channel_set_buffered(io_channel, FALSE); + g_io_add_watch(io_channel, G_IO_IN, + (GIOFunc)on_socket_event, NULL); + if(listen(sock, 5) == -1) + { + ret = 1; + goto _exit; + } + return TRUE; + +_exit: + + gdk_notify_startup_complete(); + exit( ret ); +} + +void single_instance_finalize() +{ + char lock_file[256]; + shutdown(sock, 2); + g_io_channel_unref(io_channel); + close(sock); + get_socket_name(lock_file, sizeof( lock_file )); + unlink(lock_file); +} + static FmJobErrorAction on_file_info_job_error(FmFileInfoJob* job, GError* err, FmJobErrorSeverity severity, gpointer user_data) { if(err->domain == G_IO_ERROR)