Skip to content

Commit a63b8e6

Browse files
committed
add optional single instance guard, closes #7
1 parent f2c0da4 commit a63b8e6

2 files changed

Lines changed: 49 additions & 0 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ DISPLAY OPTIONS
9898
--opacity OPACITY window opacity (0.0 - 1.0) [default: 1.0]
9999
--force-raise raise highlighter on every pointer motion
100100
--hide-highlight start with highlighter hidden
101+
--single-instance exit if another instance is already running
101102
--show-cursor start with cursor shown
102103
103104
TIMEOUT OPTIONS

highlight-pointer.c

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,14 @@
3434
#include <X11/extensions/Xfixes.h>
3535
#include <X11/extensions/shape.h>
3636
#include <errno.h>
37+
#include <fcntl.h>
3738
#include <getopt.h>
3839
#include <limits.h>
3940
#include <signal.h>
4041
#include <stdio.h>
4142
#include <stdlib.h>
4243
#include <string.h>
44+
#include <sys/file.h>
4345
#include <sys/select.h>
4446
#include <unistd.h>
4547

@@ -49,6 +51,7 @@ static Window win;
4951
static Window root;
5052
static int screen;
5153
static int selfpipe[2]; /* for self-pipe trick to cancel select() call */
54+
static int lock_fd = -1;
5255

5356
#define KEY_MODMAP_SIZE 4
5457
static struct {
@@ -97,11 +100,13 @@ static struct {
97100
int highlight_visible;
98101
int outline;
99102
int radius;
103+
int single_instance;
100104
double opacity;
101105
} options;
102106

103107
static void redraw();
104108
static int get_pointer_position(int* x, int* y);
109+
static int acquire_single_instance_lock();
105110

106111
static void show_cursor() {
107112
XFixesShowCursor(dpy, root);
@@ -513,6 +518,7 @@ static void print_usage(const char* name) {
513518
" --opacity OPACITY window opacity (0.0 - 1.0) [default: 1.0]\n"
514519
" --force-raise raise highlighter on every pointer motion\n"
515520
" --hide-highlight start with highlighter hidden\n"
521+
" --single-instance exit if another instance is already running\n"
516522
" --show-cursor start with cursor shown\n"
517523
"\n"
518524
"TIMEOUT OPTIONS\n"
@@ -551,6 +557,7 @@ static struct option long_options[] = {{"auto-hide-cursor", no_argument, &option
551557
{"radius", required_argument, NULL, 'r'},
552558
{"released-color", required_argument, NULL, 'c'},
553559
{"show-cursor", no_argument, &options.cursor_visible, 1},
560+
{"single-instance", no_argument, &options.single_instance, 1},
554561
{"key-quit", required_argument, NULL, KEY_QUIT + KEY_OPTION_OFFSET},
555562
{"key-toggle-cursor", required_argument, NULL, KEY_TOGGLE_CURSOR + KEY_OPTION_OFFSET},
556563
{"key-toggle-highlight", required_argument, NULL, KEY_TOGGLE_HIGHLIGHT + KEY_OPTION_OFFSET},
@@ -597,6 +604,7 @@ static int set_options(int argc, char* argv[]) {
597604
options.opacity = 1.0;
598605
options.radius = 5;
599606
options.outline = 0;
607+
options.single_instance = 0;
600608
options.hide_timeout = 3;
601609
options.pressed_color_string = "#1f77b4";
602610
options.released_color_string = "#d62728";
@@ -660,6 +668,42 @@ static int set_options(int argc, char* argv[]) {
660668
return 0;
661669
}
662670

671+
static int acquire_single_instance_lock() {
672+
const char* dir = getenv("XDG_RUNTIME_DIR");
673+
char path[4096];
674+
675+
if (!dir || dir[0] == '\0') {
676+
dir = getenv("TMPDIR");
677+
}
678+
if (!dir || dir[0] == '\0') {
679+
dir = "/tmp";
680+
}
681+
682+
if (snprintf(path, sizeof(path), "%s/highlight-pointer-%ld.lock", dir, (long)getuid()) >= (int)sizeof(path)) {
683+
fprintf(stderr, "Lock file path too long\n");
684+
return 1;
685+
}
686+
687+
lock_fd = open(path, O_RDWR | O_CREAT, 0600);
688+
if (lock_fd < 0) {
689+
perror("open lock file failed");
690+
return 1;
691+
}
692+
693+
if (flock(lock_fd, LOCK_EX | LOCK_NB) < 0) {
694+
if (errno == EWOULDBLOCK) {
695+
fprintf(stderr, "highlight-pointer is already running\n");
696+
} else {
697+
perror("lock file failed");
698+
}
699+
close(lock_fd);
700+
lock_fd = -1;
701+
return 1;
702+
}
703+
704+
return 0;
705+
}
706+
663707
static int xerror_handler(Display* dpy_p, XErrorEvent* err) {
664708
if (err->request_code == 33 /* XGrabKey */ && err->error_code == BadAccess) {
665709
fprintf(stderr, "Key combination already grabbed by a different process\n");
@@ -685,6 +729,10 @@ int main(int argc, char* argv[]) {
685729
return res;
686730
}
687731

732+
if (options.single_instance && acquire_single_instance_lock()) {
733+
return 1;
734+
}
735+
688736
dpy = XOpenDisplay(NULL); /* defaults to DISPLAY env var */
689737
if (!dpy) {
690738
fprintf(stderr, "Can't open display\n");

0 commit comments

Comments
 (0)