diff --git a/docs/wmenu.1.scd b/docs/wmenu.1.scd index 4519e8b..0881ad4 100644 --- a/docs/wmenu.1.scd +++ b/docs/wmenu.1.scd @@ -6,7 +6,7 @@ wmenu - dynamic menu for Wayland # SYNOPSIS -*wmenu* [-biPv] \ +*wmenu* [-1biPv] \ [-f _font_] \ [-l _lines_] \ [-o _output_] \ @@ -29,6 +29,9 @@ $PATH and runs the result. # OPTIONS +*-1* + allow only one instance. + *-b* wmenu appears at the bottom of the screen. diff --git a/menu.c b/menu.c index 9bda76c..9506ca9 100644 --- a/menu.c +++ b/menu.c @@ -85,12 +85,15 @@ static bool parse_color(const char *color, uint32_t *result) { // Parse menu options from command line arguments. void menu_getopts(struct menu *menu, int argc, char *argv[]) { const char *usage = - "Usage: wmenu [-biPv] [-f font] [-l lines] [-o output] [-p prompt]\n" + "Usage: wmenu [-1biPv] [-f font] [-l lines] [-o output] [-p prompt]\n" "\t[-N color] [-n color] [-M color] [-m color] [-S color] [-s color]\n"; int opt; - while ((opt = getopt(argc, argv, "bhiPvf:l:o:p:N:n:M:m:S:s:")) != -1) { + while ((opt = getopt(argc, argv, "1bhiPvf:l:o:p:N:n:M:m:S:s:")) != -1) { switch (opt) { + case '1': + menu->single_instance = true; + break; case 'b': menu->bottom = true; break; diff --git a/menu.h b/menu.h index 8dcd99c..06cd227 100644 --- a/menu.h +++ b/menu.h @@ -29,6 +29,8 @@ struct page { // Menu state. struct menu { + // Allow only one instance + bool single_instance; // Whether the menu appears at the bottom of the screen bool bottom; // The function used to match menu items diff --git a/wmenu-run.c b/wmenu-run.c index 1b7b8c1..e6f0c29 100644 --- a/wmenu-run.c +++ b/wmenu-run.c @@ -4,6 +4,8 @@ #include #include #include +#include +#include #include "menu.h" #include "wayland.h" @@ -71,6 +73,23 @@ static void exec_item(struct menu *menu, char *text, bool exit) { int main(int argc, char *argv[]) { struct menu *menu = menu_create(exec_item); menu_getopts(menu, argc, argv); + if (menu->single_instance) { + // the following code was copied from `bemenu`, thanks. + char *xdg_runtime_dir = getenv("XDG_RUNTIME_DIR"); + + char buffer[1024]; + char *menu_lock_location = (xdg_runtime_dir == NULL ? "/tmp" : xdg_runtime_dir); + snprintf(buffer, sizeof(buffer), "%s/wmenu.lock", menu_lock_location); + + // Create a read-write lock file(if not already exists), + // closes on exec. Creates in tmp due to permissions. + int menu_lock = open(buffer, O_CREAT | O_RDWR | O_CLOEXEC, 0666); + // If we cannot set the lock/the lock is already present. + if (flock(menu_lock, LOCK_EX | LOCK_NB) == -1) { + fprintf(stderr, "\"%s\" instance is already running\n", argv[0]); + exit(EXIT_FAILURE); + } + } read_items(menu); int status = menu_run(menu); menu_destroy(menu); diff --git a/wmenu.c b/wmenu.c index 38e78b9..ed636d0 100644 --- a/wmenu.c +++ b/wmenu.c @@ -1,7 +1,10 @@ #define _POSIX_C_SOURCE 200809L +#include #include #include +#include +#include #include "menu.h" #include "wayland.h" @@ -28,6 +31,23 @@ static void print_item(struct menu *menu, char *text, bool exit) { int main(int argc, char *argv[]) { struct menu *menu = menu_create(print_item); menu_getopts(menu, argc, argv); + if (menu->single_instance) { + // the following code was copied from `bemenu`, thanks. + char *xdg_runtime_dir = getenv("XDG_RUNTIME_DIR"); + + char buffer[1024]; + char *menu_lock_location = (xdg_runtime_dir == NULL ? "/tmp" : xdg_runtime_dir); + snprintf(buffer, sizeof(buffer), "%s/wmenu.lock", menu_lock_location); + + // Create a read-write lock file(if not already exists), + // closes on exec. Creates in tmp due to permissions. + int menu_lock = open(buffer, O_CREAT | O_RDWR | O_CLOEXEC, 0666); + // If we cannot set the lock/the lock is already present. + if (flock(menu_lock, LOCK_EX | LOCK_NB) == -1) { + fprintf(stderr, "\"%s\" instance is already running\n", argv[0]); + exit(EXIT_FAILURE); + } + } read_items(menu); int status = menu_run(menu); menu_destroy(menu);