commit 6cdc5a3db2cec4754cf996453e4cd4bb63069556 from: Alexander Arkhipov date: Sun May 28 18:04:13 2023 UTC add mouse support commit - 15aae8ae5a0d503c252bb70a4950982434434443 commit + 6cdc5a3db2cec4754cf996453e4cd4bb63069556 blob - dc31ae0e588c5d0756d0eaef3185e2f76f83b21a blob + e7bbfa00dd3a4e12dfaccc869327b637c66b7d5c --- xitems.c +++ xitems.c @@ -43,6 +43,11 @@ die(int eval, const char *fmt, ...) exit(eval); } +enum direction { + DIR_UP, + DIR_DOWN +}; + /* doubly-linked cyclic list */ struct item { size_t len; /* length of string */ @@ -72,6 +77,30 @@ static Pixmap pm_sel, pm_norm; /* background pixmaps * static XFontStruct *font; static int height, width; /* height and width of one item */ +/* newsel -- mark the item at position y (from top) as selected. */ +static void +newsel(int y) +{ + struct item *unselected = selected; + int top = 1; + + selected = first; + + if (y <= 0) + goto end; + + do { + if (y >= top && y < top+height) + break; + top += height; + selected = selected->next; + } while (selected != first->prev); + +end: + if (unselected != selected) + unselected->dirty = selected->dirty = true; +} + /* redraw -- redraw the entire window */ static void redraw(void) @@ -104,6 +133,27 @@ redraw(void) } while (it != first); } +/* succeed -- exit successfully. If print is true, also print selected item. */ +static void +succeed(bool print) +{ + if (print && selected) + puts(selected->s); + exit(0); +} + +/* scroll -- select the previous, or next item, depending on dir. */ +static void +scroll(enum direction dir) +{ + if (selected) { + selected->dirty = true; + selected = (dir == DIR_UP) ? selected->prev : selected->next; + } else + selected = (dir == DIR_UP) ? first->prev : first; + selected->dirty = true; +} + /* proc -- body of the main event-reading loop. */ static void proc(void) @@ -113,14 +163,36 @@ proc(void) char *dummy = ""; XEvent ev; + static bool inwin = false; + XNextEvent(dpy, &ev); /* XXX try to avoid full redraws */ switch (ev.type) { + case EnterNotify: + inwin = true; + /* FALLTHRU */ + case MotionNotify: + newsel(ev.xbutton.y); + /* FALLTHRU */ case Expose: redraw(); break; - + case LeaveNotify: + inwin = false; + break; + case ButtonPress: + if (ev.xbutton.button == Button4) { + scroll(DIR_UP); + redraw(); + } else if (ev.xbutton.button == Button5) { + scroll(DIR_DOWN); + redraw(); + } else if (inwin) + succeed(true); + else + succeed(false); + break; case KeyPress: ke = ev.xkey; XLookupString(&ke, dummy, 0, &ks, NULL); @@ -153,31 +225,42 @@ proc(void) case XK_j: case XK_J: case XK_Down: - selected->dirty = true; - selected = selected ? selected->next : first; - selected->dirty = true; + scroll(DIR_DOWN); redraw(); break; case XK_k: case XK_K: case XK_Up: - selected->dirty = true; - selected = selected ? selected->prev : first->prev; - selected->dirty = true; + scroll(DIR_UP); redraw(); break; case XK_Return: - if (selected) - puts(selected->s); - exit(0); + succeed(true); /* NOTREACHED */ case XK_Escape: - exit(0); + succeed(false); /* NOTREACHED */ } break; + } +} + +/* grabptr -- try to grab pointer for a second */ +static void +grabptr(void) +{ + struct timespec ts = { .tv_sec = 0, .tv_nsec = 1000000 }; + int i; + + for (i = 0; i < 1000; ++i) { + if (XGrabPointer(dpy, RootWindow(dpy, screen), True, + ButtonPressMask, GrabModeAsync, GrabModeAsync, None, None, + CurrentTime) == GrabSuccess) + return; + nanosleep(&ts, NULL); } + die(1, "couldn't grab pointer"); } /* grabkb -- try to grab keyboard for a second */ @@ -196,6 +279,24 @@ grabkb(void) die(1, "couldn't grab keyboard"); } +/* setfocus -- try setting focus to the menu for a second */ +static void +setfocus(void) +{ + struct timespec ts = { .tv_sec = 0, .tv_nsec = 1000000 }; + Window focuswin; + int i, dummy; + + for (i = 0; i < 1000; ++i) { + XGetInputFocus(dpy, &focuswin, &dummy); + if (focuswin == win) + return; + XSetInputFocus(dpy, win, RevertToParent, CurrentTime); + nanosleep(&ts, NULL); + } + die(1, "couldn't grab keyboard"); +} + /* * setupx -- create and map a window for n items; assign values to the X * globals. @@ -210,7 +311,9 @@ setupx(int n) XSetWindowAttributes swa = { .override_redirect = True, .save_under = True, - .event_mask = ExposureMask | KeyPressMask | StructureNotifyMask, + .event_mask = ExposureMask | StructureNotifyMask | + KeyPressMask | ButtonPressMask | ButtonReleaseMask | + PointerMotionMask | LeaveWindowMask | EnterWindowMask, }; if (!(font = XLoadQueryFont(dpy, o_font))) @@ -263,8 +366,11 @@ setupx(int n) XSetForeground(dpy, gc_sel, col.pixel); grabkb(); + grabptr(); XMapRaised(dpy, win); + + setfocus(); } /*