Commit Diff


commit - 7bb4fd609f572b48d95a3328750a34a7add0916e
commit + c2ca3402b92d106af09010320b729704fb5c379b
blob - ee6b7aad582c69a1d19eb4900b05e370dafcdfbf
blob + 7e4e035b5bc8a122de59be66afa1586aa74232c2
--- bxnotify.c
+++ bxnotify.c
@@ -16,8 +16,9 @@
 #include <unistd.h>
 #include <sys/file.h>
 #include <X11/Xlib.h>
-#include <X11/Xutil.h>
 #include <X11/Xft/Xft.h>
+#include <X11/keysym.h>
+#include <X11/Xutil.h>
 
 #include "config.h"
 
@@ -44,11 +45,13 @@ static void die(const char *fmt, ...);
 static void cleanup(void);
 static void sigusr1(int sig);
 static int measure_text(const char *text);
+static int (*xerrorxlib)(Display *, XErrorEvent *);
 static void init_display(void);
 static void calc_layout(void);
 static void init_reg_path(void);
 static int get_and_register_stack_y(int my_h);
 static void unregister_position(void);
+static int get_oldest_pid(void);
 static void setup(int y, int win_h);
 static void readstdin(void);
 static void draw(void);
@@ -146,12 +149,23 @@ readstdin(void)
         die("bxnotify: no input received\n");
 }
 
+static int
+xerror(Display *dpy, XErrorEvent *ee)
+{
+    /* Silently ignore BadAccess from XGrabKey - another instance has the grab */
+    if (ee->error_code == BadAccess && ee->request_code == 33)
+        return 0;
+    return xerrorxlib(dpy, ee);
+}
+
 static void
 init_display(void)
 {
     if (!(dpy = XOpenDisplay(NULL)))
         die("bxnotify: cannot open display\n");
 
+    xerrorxlib = XSetErrorHandler(xerror);
+
     screen = DefaultScreen(dpy);
 
     if (!(font = XftFontOpenName(dpy, screen, FONT)))
@@ -294,6 +308,38 @@ unregister_position(void)
     }
 }
 
+static int
+get_oldest_pid(void)
+{
+    int fd = open(reg_path, O_RDONLY);
+    if (fd == -1) return getpid();
+
+    flock(fd, LOCK_SH);
+    FILE *f = fdopen(fd, "r");
+
+    int min_y = 99999;
+    int oldest_pid = getpid();
+
+    if (f) {
+        char line[64];
+        int pid, y, h;
+
+        while (fgets(line, sizeof(line), f)) {
+            if (sscanf(line, "%d %d %d", &pid, &y, &h) == 3) {
+                if (kill(pid, 0) == 0 && y < min_y) {
+                    min_y = y;
+                    oldest_pid = pid;
+                }
+            }
+        }
+        fclose(f);
+    } else {
+        close(fd);
+    }
+
+    return oldest_pid;
+}
+
 static void
 setup(int y, int win_h)
 {
@@ -304,7 +350,7 @@ setup(int y, int win_h)
     swa.override_redirect = True;
     swa.background_pixel = bg.pixel;
     swa.border_pixel = 0;
-    swa.event_mask = ExposureMask;
+    swa.event_mask = ExposureMask | KeyPressMask;
 
     win = XCreateWindow(dpy, RootWindow(dpy, screen), x, y,
                         win_w, win_h, 0,
@@ -326,6 +372,9 @@ setup(int y, int win_h)
     XSetClassHint(dpy, win, &ch);
 
     XMapWindow(dpy, win);
+
+    /* Register the passive grab for key press Mod4+q */
+    XGrabKey(dpy, XKeysymToKeycode(dpy, XK_q), Mod4Mask, RootWindow(dpy, screen), True, GrabModeAsync, GrabModeAsync);
 }
 
 static void
@@ -359,20 +408,29 @@ run(void)
 {
     XEvent ev;
 
+    KeySym q = XStringToKeysym("q");
+
     while (running) {
         if (XPending(dpy)) {
             XNextEvent(dpy, &ev);
-            if (ev.type == Expose)
+            if (ev.type == Expose) {
                 draw();
+            } else if (ev.type == KeyPress) {
+                if (XLookupKeysym(&ev.xkey, 0) == q && (ev.xkey.state & Mod4Mask)) {
+                    int oldest = get_oldest_pid();
+                    if (oldest==getpid())
+                        running = 0;
+                    else if (oldest > 0)
+                        kill(oldest, SIGUSR1);
+                }
+            }
         }
-
         /* Check duration timeout */
         if (DURATION > 0 && (time(NULL) - start_time) >= DURATION)
             running = 0;
 
         struct timespec ts = {0, 10000000};  /* 10 milliseconds */
         nanosleep(&ts, NULL);
-
     }
 }