diff --git a/doc/rofi-manpage.markdown b/doc/rofi-manpage.markdown index cacdd133..5cc978c2 100644 --- a/doc/rofi-manpage.markdown +++ b/doc/rofi-manpage.markdown @@ -39,6 +39,7 @@ rofi - A window switcher, run dialog and dmenu replacement [ -eh *element height* ] [ -lazy-filter-limit *limit* ] [ -e *message*] +[ -pid *path* ] [ -now ] [ -rnow ] [ -snow ] @@ -419,6 +420,13 @@ The default key combinations are: Popup a message dialog (used internally for showing errors) with *message*. Message can be multi-line. +### Other + +'-pid' *path* + + Make **rofi** create a pid file and check this on startup. Avoiding multiple copies running + simultaneous. This is useful when running rofi from a keybinding daemon. + ### Debug `-dump-xresources` diff --git a/doc/rofi.1 b/doc/rofi.1 index a6cdc40c..a7346154 100644 --- a/doc/rofi.1 +++ b/doc/rofi.1 @@ -37,6 +37,7 @@ rofi \- A window switcher, run dialog and dmenu replacement [ \-eh \fIelement height\fP ] [ \-lazy\-filter\-limit \fIlimit\fP ] [ \-e \fImessage\fP] +[ \-pid \fIpath\fP ] [ \-now ] [ \-rnow ] [ \-snow ] @@ -516,6 +517,16 @@ Popup a message dialog (used internally for showing errors) with *message*. Message can be multi\-line. .fi .RE +.SS Other +.PP +\&'\-pid' \fIpath\fP +.PP +.RS +.nf +Make **rofi** create a pid file and check this on startup. Avoiding multiple copies running +simultaneous. This is useful when running rofi from a keybinding daemon. +.fi +.RE .SS Debug .PP \fB\fC\-dump\-xresources\fR @@ -652,10 +663,10 @@ Check quotes used on the commandline: e.g. used “ instead of ". .UE .SH AUTHOR .PP -Qball Cow +Qball Cow .MT qball@gmpclient.org .ME .PP -Original code based on work by: Sean Pringle +Original code based on work by: Sean Pringle .MT sean.pringle@gmail.com .ME diff --git a/include/helper.h b/include/helper.h index 9e75c336..98f18627 100644 --- a/include/helper.h +++ b/include/helper.h @@ -89,6 +89,18 @@ int find_arg_int ( const int argc, char * const argv[], const char * const key, * @returns TRUE if key was found and val was set. */ int find_arg_str ( const int argc, char * const argv[], const char * const key, char** val ); +/** + * @param argc Number of arguments. + * @param argv 2 dimensional array of arguments. + * @param key The key to search for + * @param val Pointer to the string to set to the key value (if found) + * + * Parse command line argument 'key' to string. + * Creates an allocated copy of the string. + * + * @returns TRUE if key was found and val was set. + */ +int find_arg_str_alloc ( const int argc, char * const argv[], const char * const key, char** val ); /** * @param argc Number of arguments. diff --git a/source/helper.c b/source/helper.c index cc077a83..e04a6ea2 100644 --- a/source/helper.c +++ b/source/helper.c @@ -185,6 +185,16 @@ int find_arg_str ( const int argc, char * const argv[], const char * const key, } return FALSE; } +int find_arg_str_alloc ( const int argc, char * const argv[], const char * const key, char** val ) +{ + int i = find_arg ( argc, argv, key ); + + if ( val != NULL && i > 0 && i < argc - 1 ) { + *val = g_strdup ( argv[i + 1] ); + return TRUE; + } + return FALSE; +} int find_arg_int ( const int argc, char * const argv[], const char * const key, int *val ) { diff --git a/source/rofi.c b/source/rofi.c index a3795307..f88c6215 100644 --- a/source/rofi.c +++ b/source/rofi.c @@ -46,6 +46,7 @@ #include #include +#include #ifdef HAVE_I3_IPC_H #include @@ -74,6 +75,8 @@ int config_i3_mode = 0; char *i3_socket_path = NULL; #endif +char *pidfile = NULL; + const char *cache_dir = NULL; unsigned int NumlockMask = 0; Display *display = NULL; @@ -1800,7 +1803,8 @@ MenuReturn menu ( char **lines, unsigned int num_lines, char **input, char *prom menu_refilter ( &state, lines, mmc, mmc_data, sorting, config.case_sensitive ); menu_update ( &state ); } - // Return like normal. + } + if ( mle == ML_TIMEOUT ) { continue; } // Get next event. (might block) @@ -2453,6 +2457,8 @@ static void parse_cmd_options ( int argc, char ** argv ) exit ( EXIT_SUCCESS ); } + find_arg_str_alloc ( argc, argv, "-pid", &( pidfile ) ); + find_arg_str ( argc, argv, "-switchers", &( config.switchers ) ); // Parse commandline arguments about the looks. find_arg_uint ( argc, argv, "-opacity", &( config.window_opacity ) ); @@ -2560,6 +2566,12 @@ static void cleanup () } } g_free ( switchers ); + + // Cleanup pid file. + if ( pidfile ) { + unlink ( pidfile ); + g_free ( pidfile ); + } } /** @@ -2701,7 +2713,42 @@ static void hup_action_handler ( int num ) XCloseDisplay ( display ); } } +/** + * @param pidfile The pidfile to create. + */ +static void create_pid_file ( const char *pidfile ) +{ + if ( pidfile == NULL ) { + return; + } + int fd = open ( pidfile, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR ); + if ( fd < 0 ) { + fprintf ( stderr, "Failed to create pid file." ); + exit ( 1 ); + } + // Set it to close the File Descriptor on exit. + int flags = fcntl ( fd, F_GETFD, NULL ); + flags = flags | FD_CLOEXEC; + if ( fcntl ( fd, F_SETFD, flags, NULL ) < 0 ) { + fprintf ( stderr, "Failed to set CLOEXEC on pidfile." ); + close ( fd ); + exit ( 1 ); + } + // Try to get exclusive write lock on FD + int retv = flock ( fd, LOCK_EX | LOCK_NB ); + if ( retv != 0 ) { + fprintf ( stderr, "Failed to set lock on pidfile: Rofi already running?\n" ); + fprintf ( stderr, "%d %s\n", retv, strerror ( errno ) ); + exit ( 1 ); + } + ftruncate ( fd, (off_t) 0 ); + + // Write pid. + char buffer[64]; + int length = snprintf ( buffer, 64, "%i", getpid () ); + write ( fd, buffer, length ); +} int main ( int argc, char *argv[] ) { @@ -2711,6 +2758,12 @@ int main ( int argc, char *argv[] ) // Get the path to the cache dir. cache_dir = g_get_user_cache_dir (); + // Create pid file path. + const char *path = g_get_user_runtime_dir (); + if ( path ) { + pidfile = g_build_filename ( path, "rofi.pid", NULL ); + } + // Register cleanup function. atexit ( cleanup ); @@ -2723,6 +2776,7 @@ int main ( int argc, char *argv[] ) return EXIT_FAILURE; } + load_configuration ( display ); // Dump. @@ -2731,6 +2785,11 @@ int main ( int argc, char *argv[] ) exit ( EXIT_SUCCESS ); } + // Create pid file + if ( pidfile != NULL ) { + create_pid_file ( pidfile ); + } + // setup_switchers setup_switchers ();