itAdd monitoring of events on root window - xmenu - drop-down menu for X11 Err z3bra.org 70 hgit clone git://git.z3bra.org/xmenu.git URL:git://git.z3bra.org/xmenu.git z3bra.org 70 1Log /scm/xmenu/log.gph z3bra.org 70 1Files /scm/xmenu/files.gph z3bra.org 70 1Refs /scm/xmenu/refs.gph z3bra.org 70 i--- Err z3bra.org 70 1commit b02590ceeb90f07ab4ffe23bfa71ae1c8d096e9a /scm/xmenu/commit/b02590ceeb90f07ab4ffe23bfa71ae1c8d096e9a.gph z3bra.org 70 1parent 61c19bc23c3509c5b1e32afe95f335516fd39d32 /scm/xmenu/commit/61c19bc23c3509c5b1e32afe95f335516fd39d32.gph z3bra.org 70 hAuthor: Willy Goiffon URL:mailto:dev@z3bra.org z3bra.org 70 iDate: Wed, 20 Nov 2019 18:49:06 +0100 Err z3bra.org 70 i Err z3bra.org 70 iAdd monitoring of events on root window Err z3bra.org 70 i Err z3bra.org 70 iThis will let the menu run as a daemon watching click events on the root Err z3bra.org 70 iwindow and pop the menu on such events. Err z3bra.org 70 i Err z3bra.org 70 iThis saves a click compared to firing the application from an external Err z3bra.org 70 iprogramming that's listenning for events. Err z3bra.org 70 i Err z3bra.org 70 iDiffstat: Err z3bra.org 70 i M xmenu.c | 150 ++++++++++++++++++++++++------- Err z3bra.org 70 i Err z3bra.org 70 i1 file changed, 117 insertions(+), 33 deletions(-) Err z3bra.org 70 i--- Err z3bra.org 70 1diff --git a/xmenu.c b/xmenu.c /scm/xmenu/file/xmenu.c.gph z3bra.org 70 it@@ -15,6 +15,10 @@ Err z3bra.org 70 i #define MAX(a,b) (((a)>(b))?(a):(b)) Err z3bra.org 70 i #define MIN(a,b) (((a)<(b))?(a):(b)) Err z3bra.org 70 i Err z3bra.org 70 i+struct geom { Err z3bra.org 70 i+ int x, y, w, h; Err z3bra.org 70 i+}; Err z3bra.org 70 i+ Err z3bra.org 70 i int verbose = 0; Err z3bra.org 70 i int current = -1; Err z3bra.org 70 i xcb_connection_t *dpy; Err z3bra.org 70 it@@ -24,13 +28,12 @@ xcb_window_t wid; Err z3bra.org 70 i size_t nent = 0; Err z3bra.org 70 i size_t maxwidth = 0; Err z3bra.org 70 i size_t maxheight = 0; Err z3bra.org 70 i-int width = 0; Err z3bra.org 70 i-int height = 0; Err z3bra.org 70 i+struct geom menu = { 0, 0, 0, 0 }; Err z3bra.org 70 i Err z3bra.org 70 i void Err z3bra.org 70 i usage(FILE *fd, char *name) Err z3bra.org 70 i { Err z3bra.org 70 i- fprintf(fd, "usage: %s [-h]\n", name); Err z3bra.org 70 i+ fprintf(fd, "usage: %s [-hd]\n", name); Err z3bra.org 70 i } Err z3bra.org 70 i Err z3bra.org 70 i int Err z3bra.org 70 it@@ -63,8 +66,8 @@ drawentries() Err z3bra.org 70 i size_t i, w, h; Err z3bra.org 70 i Err z3bra.org 70 i for (i = 0; i < nent; i++) { Err z3bra.org 70 i- w = (width - xft_txtw(entries[i])) / 2; Err z3bra.org 70 i- h = (height/nent) * (i + 0.5) - maxheight/2 + (maxheight - xft_txth(entries[i])); Err z3bra.org 70 i+ w = (menu.w - xft_txtw(entries[i])) / 2; Err z3bra.org 70 i+ h = (menu.h/nent) * (i + 0.5) - maxheight/2 + (maxheight - xft_txth(entries[i])); Err z3bra.org 70 i xft_drawtext(dpy, wid, w, h, foreground, entries[i]); Err z3bra.org 70 i } Err z3bra.org 70 i Err z3bra.org 70 it@@ -72,7 +75,7 @@ drawentries() Err z3bra.org 70 i } Err z3bra.org 70 i Err z3bra.org 70 i int Err z3bra.org 70 i-popwindow(int w, int h) Err z3bra.org 70 i+popwindow(int x, int y, int w, int h) Err z3bra.org 70 i { Err z3bra.org 70 i int mask, val[4]; Err z3bra.org 70 i Err z3bra.org 70 it@@ -84,14 +87,13 @@ popwindow(int w, int h) Err z3bra.org 70 i | XCB_EVENT_MASK_KEY_PRESS Err z3bra.org 70 i | XCB_EVENT_MASK_ENTER_WINDOW Err z3bra.org 70 i | XCB_EVENT_MASK_LEAVE_WINDOW Err z3bra.org 70 i- | XCB_EVENT_MASK_BUTTON_PRESS Err z3bra.org 70 i | XCB_EVENT_MASK_BUTTON_RELEASE Err z3bra.org 70 i | XCB_EVENT_MASK_POINTER_MOTION Err z3bra.org 70 i | XCB_EVENT_MASK_STRUCTURE_NOTIFY; Err z3bra.org 70 i Err z3bra.org 70 i wid = xcb_generate_id(dpy); Err z3bra.org 70 i xcb_create_window(dpy, screen->root_depth, wid, screen->root, Err z3bra.org 70 i- 0, 0, w, h, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, Err z3bra.org 70 i+ x, y, w, h, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, Err z3bra.org 70 i screen->root_visual, mask, val); Err z3bra.org 70 i Err z3bra.org 70 i xcb_map_window(dpy, wid); Err z3bra.org 70 it@@ -103,10 +105,10 @@ popwindow(int w, int h) Err z3bra.org 70 i int Err z3bra.org 70 i eventloop() Err z3bra.org 70 i { Err z3bra.org 70 i- int loop = 1; Err z3bra.org 70 i xcb_generic_event_t *ev; Err z3bra.org 70 i+ xcb_motion_notify_event_t *e; Err z3bra.org 70 i Err z3bra.org 70 i- while(loop) { Err z3bra.org 70 i+ for (;;) { Err z3bra.org 70 i int last = current; Err z3bra.org 70 i Err z3bra.org 70 i ev = xcb_wait_for_event(dpy); Err z3bra.org 70 it@@ -114,18 +116,48 @@ eventloop() Err z3bra.org 70 i break; Err z3bra.org 70 i Err z3bra.org 70 i switch(ev->response_type & ~0x80) { Err z3bra.org 70 i+ Err z3bra.org 70 i+ /* Err z3bra.org 70 i+ * pressing escape (keycode 9) clears the selection and Err z3bra.org 70 i+ * closes the menu Err z3bra.org 70 i+ */ Err z3bra.org 70 i case XCB_KEY_PRESS: Err z3bra.org 70 i- if (((xcb_key_press_event_t *)ev)->detail == 9) Err z3bra.org 70 i- return -1; Err z3bra.org 70 i+ if (((xcb_key_press_event_t *)ev)->detail == 9) { Err z3bra.org 70 i+ current = -1; Err z3bra.org 70 i+ return 0; Err z3bra.org 70 i+ } Err z3bra.org 70 i break; Err z3bra.org 70 i- case XCB_BUTTON_RELEASE: Err z3bra.org 70 i- if (((xcb_button_release_event_t *)ev)->event == wid) Err z3bra.org 70 i- return current; Err z3bra.org 70 i+ Err z3bra.org 70 i+ /* Err z3bra.org 70 i+ * button press events MUST be caught on root window Err z3bra.org 70 i+ * only to avoid popup loops Err z3bra.org 70 i+ */ Err z3bra.org 70 i+ case XCB_BUTTON_PRESS: Err z3bra.org 70 i+ menu.x = ((xcb_button_press_event_t *)ev)->root_x; Err z3bra.org 70 i+ menu.y = ((xcb_button_press_event_t *)ev)->root_y; Err z3bra.org 70 i+ popwindow(menu.x, menu.y, menu.w, menu.h); Err z3bra.org 70 i break; Err z3bra.org 70 i+ Err z3bra.org 70 i+ /* Err z3bra.org 70 i+ * releasing the mouse button closes the menu while Err z3bra.org 70 i+ * keeping the current selection active. In other words, Err z3bra.org 70 i+ * it validates the current selection (if any) Err z3bra.org 70 i+ */ Err z3bra.org 70 i+ case XCB_BUTTON_RELEASE: Err z3bra.org 70 i+ return 0; Err z3bra.org 70 i+ break; /* NOTREACHED */ Err z3bra.org 70 i+ Err z3bra.org 70 i+ /* Err z3bra.org 70 i+ * make sure to keep track of window geometry changes Err z3bra.org 70 i+ * to calculate cursor position correctly Err z3bra.org 70 i+ */ Err z3bra.org 70 i case XCB_CONFIGURE_NOTIFY: Err z3bra.org 70 i- width = ((xcb_configure_notify_event_t *)ev)->width; Err z3bra.org 70 i- height = ((xcb_configure_notify_event_t *)ev)->height; Err z3bra.org 70 i+ menu.x = ((xcb_configure_notify_event_t *)ev)->x; Err z3bra.org 70 i+ menu.y = ((xcb_configure_notify_event_t *)ev)->y; Err z3bra.org 70 i+ menu.w = ((xcb_configure_notify_event_t *)ev)->width; Err z3bra.org 70 i+ menu.h = ((xcb_configure_notify_event_t *)ev)->height; Err z3bra.org 70 i break; Err z3bra.org 70 i+ Err z3bra.org 70 i case XCB_EXPOSE: Err z3bra.org 70 i /* Err z3bra.org 70 i * TODO: clean after someone mess up current entry Err z3bra.org 70 it@@ -139,17 +171,30 @@ eventloop() Err z3bra.org 70 i * Err z3bra.org 70 i * Damn punks. Err z3bra.org 70 i */ Err z3bra.org 70 i- if (last >= 0) Err z3bra.org 70 i- hilight(wid, 0, (height/nent) * last, width, height/nent); Err z3bra.org 70 i+ if (last >= 0 && last != current) Err z3bra.org 70 i+ hilight(wid, 0, (menu.h/nent) * last, menu.w, menu.h/nent); Err z3bra.org 70 i drawentries(); Err z3bra.org 70 i if (current >= 0) Err z3bra.org 70 i- hilight(wid, 0, (height/nent) * current, width, height/nent); Err z3bra.org 70 i+ hilight(wid, 0, (menu.h/nent) * current, menu.w, menu.h/nent); Err z3bra.org 70 i break; Err z3bra.org 70 i+ Err z3bra.org 70 i+ /* Err z3bra.org 70 i+ * moving the cursor outside the menu clears the current Err z3bra.org 70 i+ * selection, so that you can "cancel" the menu by releasing Err z3bra.org 70 i+ * the mouse outside the menu Err z3bra.org 70 i+ */ Err z3bra.org 70 i case XCB_LEAVE_NOTIFY: Err z3bra.org 70 i- hilight(wid, 0, (height/nent) * current, width, height/nent); Err z3bra.org 70 i+ if (current >= 0) Err z3bra.org 70 i+ hilight(wid, 0, (menu.h/nent) * current, menu.w, menu.h/nent); Err z3bra.org 70 i current = -1; Err z3bra.org 70 i break; Err z3bra.org 70 i+ Err z3bra.org 70 i+ /* Err z3bra.org 70 i+ * calculate current item based on cursor position Err z3bra.org 70 i+ * relative to the root window and current window dimensions Err z3bra.org 70 i+ */ Err z3bra.org 70 i case XCB_MOTION_NOTIFY: Err z3bra.org 70 i+ e = (xcb_motion_notify_event_t *)ev; Err z3bra.org 70 i /* Err z3bra.org 70 i * in some special cases, MOTION events can be Err z3bra.org 70 i * reported in the window when the pointer is Err z3bra.org 70 it@@ -160,13 +205,21 @@ eventloop() Err z3bra.org 70 i * cases, so we must check first that the pointer Err z3bra.org 70 i * is actually inside the window Err z3bra.org 70 i */ Err z3bra.org 70 i- if (((xcb_motion_notify_event_t *)ev)->event_x > width Err z3bra.org 70 i- || ((xcb_motion_notify_event_t *)ev)->event_y > height) Err z3bra.org 70 i+ if (e->root_x > menu.x + menu.w Err z3bra.org 70 i+ || e->root_y > menu.y + menu.h Err z3bra.org 70 i+ || e->root_x < menu.x Err z3bra.org 70 i+ || e->root_y < menu.y) Err z3bra.org 70 i break; Err z3bra.org 70 i Err z3bra.org 70 i- current = nent * (((xcb_motion_notify_event_t *)ev)->event_y * 1.0 / height); Err z3bra.org 70 i- hilight(wid, 0, (height/nent) * last, width, height/nent); Err z3bra.org 70 i- hilight(wid, 0, (height/nent) * current, width, height/nent); Err z3bra.org 70 i+ current = nent * ((e->root_y - menu.y) * 1.0 / menu.h); Err z3bra.org 70 i+ Err z3bra.org 70 i+ /* don't bother redrawing selection that didn't change */ Err z3bra.org 70 i+ if (last == current) Err z3bra.org 70 i+ break; Err z3bra.org 70 i+ if (last >= 0) Err z3bra.org 70 i+ hilight(wid, 0, (menu.h/nent) * last, menu.w, menu.h/nent); Err z3bra.org 70 i+ if (current >= 0) Err z3bra.org 70 i+ hilight(wid, 0, (menu.h/nent) * current, menu.w, menu.h/nent); Err z3bra.org 70 i break; Err z3bra.org 70 i } Err z3bra.org 70 i Err z3bra.org 70 it@@ -180,11 +233,14 @@ eventloop() Err z3bra.org 70 i int Err z3bra.org 70 i main(int argc, char *argv[]) Err z3bra.org 70 i { Err z3bra.org 70 i- int selected = 0; Err z3bra.org 70 i+ int dflag = 0; Err z3bra.org 70 i long dpi; Err z3bra.org 70 i char *argv0; Err z3bra.org 70 i Err z3bra.org 70 i ARGBEGIN { Err z3bra.org 70 i+ case 'd': Err z3bra.org 70 i+ dflag = 1; Err z3bra.org 70 i+ break; Err z3bra.org 70 i case 'h': Err z3bra.org 70 i usage(stdout, argv0); Err z3bra.org 70 i return 0; Err z3bra.org 70 it@@ -216,15 +272,43 @@ main(int argc, char *argv[]) Err z3bra.org 70 i maxheight = MAX(xft_txth(entries[nent]), maxheight); Err z3bra.org 70 i } Err z3bra.org 70 i Err z3bra.org 70 i- width = maxwidth * 1.2; Err z3bra.org 70 i- height = maxheight * nent * 2.5; Err z3bra.org 70 i+ menu.w = maxwidth * 1.5; Err z3bra.org 70 i+ menu.h = maxheight * nent * 2; Err z3bra.org 70 i+ Err z3bra.org 70 i+ /* Err z3bra.org 70 i+ * daemon mode will catch mouse clicks on the root window and Err z3bra.org 70 i+ * pop the menu everytime it is clicked. Err z3bra.org 70 i+ * selection happens instantly and is validated upon releasing Err z3bra.org 70 i+ * the mouse button while hovering an item Err z3bra.org 70 i+ */ Err z3bra.org 70 i+ if (dflag) { Err z3bra.org 70 i+ int val[] = { Err z3bra.org 70 i+ XCB_EVENT_MASK_BUTTON_PRESS Err z3bra.org 70 i+ | XCB_EVENT_MASK_BUTTON_RELEASE Err z3bra.org 70 i+ | XCB_EVENT_MASK_POINTER_MOTION Err z3bra.org 70 i+ }; Err z3bra.org 70 i+ Err z3bra.org 70 i+ xcb_change_window_attributes(dpy, screen->root, XCB_CW_EVENT_MASK, val); Err z3bra.org 70 i+ xcb_flush(dpy); Err z3bra.org 70 i+ Err z3bra.org 70 i+ for (;;) { Err z3bra.org 70 i+ current = -1; Err z3bra.org 70 i+ eventloop(); Err z3bra.org 70 i+ if (current >= 0) { Err z3bra.org 70 i+ printf("%s\n", entries[current]); Err z3bra.org 70 i+ fflush(stdout); Err z3bra.org 70 i+ } Err z3bra.org 70 i Err z3bra.org 70 i- popwindow(width, height); Err z3bra.org 70 i- selected = eventloop(); Err z3bra.org 70 i+ xcb_destroy_window(dpy, wid); Err z3bra.org 70 i+ } Err z3bra.org 70 i+ } Err z3bra.org 70 i Err z3bra.org 70 i- if (selected >= 0) Err z3bra.org 70 i- printf("%s\n", entries[selected]); Err z3bra.org 70 i+ popwindow(menu.x, menu.y, menu.w, menu.h); Err z3bra.org 70 i+ eventloop(); Err z3bra.org 70 i+ if (current >= 0) Err z3bra.org 70 i+ printf("%s\n", entries[current]); Err z3bra.org 70 i Err z3bra.org 70 i+ xcb_destroy_window(dpy, wid); Err z3bra.org 70 i xft_unload(); Err z3bra.org 70 i xcb_disconnect(dpy); Err z3bra.org 70 i return 0; Err z3bra.org 70 .