From 15f09e0e690328cb7bfd35b577435cba7b2e4859 Mon Sep 17 00:00:00 2001 From: Bakkeby Date: Mon, 12 Jun 2023 15:17:51 +0200 Subject: [PATCH 1/2] Banish patch - behaviour similiar to xbanish and unclutter --- config.mk | 6 ++- dwm.c | 150 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 149 insertions(+), 7 deletions(-) diff --git a/config.mk b/config.mk index ef8acf7..ae0a7f3 100644 --- a/config.mk +++ b/config.mk @@ -14,6 +14,10 @@ X11LIB = /usr/X11R6/lib XINERAMALIBS = -lXinerama XINERAMAFLAGS = -DXINERAMA +# Banish: dependency on libxi and libxfixes for mouse related features +XINPUTLIBS = -lXi +XFIXESLIBS = -lXfixes + # freetype FREETYPELIBS = -lfontconfig -lXft FREETYPEINC = /usr/include/freetype2 @@ -23,7 +27,7 @@ FREETYPEINC = /usr/include/freetype2 # includes and libs INCS = -I${X11INC} -I${FREETYPEINC} -LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} +LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} ${XINPUTLIBS} ${XFIXESLIBS} # flags CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} diff --git a/dwm.c b/dwm.c index e5efb6a..bb7760b 100644 --- a/dwm.c +++ b/dwm.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -39,6 +40,8 @@ #ifdef XINERAMA #include #endif /* XINERAMA */ +#include +#include #include #include "drw.h" @@ -49,6 +52,8 @@ #define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask)) #define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x),(m)->wx)) \ * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy))) +#define INTERSECTC(X,Y,W,H,Z) (MAX(0, MIN((X)+(W),(Z)->x+(Z)->w) - MAX((X),(Z)->x)) \ + * MAX(0, MIN((Y)+(H),(Z)->y+(Z)->h) - MAX((Y),(Z)->y))) #define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags])) #define LENGTH(X) (sizeof X / sizeof X[0]) #define MOUSEMASK (BUTTONMASK|PointerMotionMask) @@ -169,12 +174,14 @@ static void focus(Client *c); static void focusin(XEvent *e); static void focusmon(const Arg *arg); static void focusstack(const Arg *arg); +static void genericevent(XEvent *e); static Atom getatomprop(Client *c, Atom prop); static int getrootptr(int *x, int *y); static long getstate(Window w); static int gettextprop(Window w, Atom atom, char *text, unsigned int size); static void grabbuttons(Client *c, int focused); static void grabkeys(void); +static void hidecursor(const Arg *arg); static void incnmaster(const Arg *arg); static void keypress(XEvent *e); static void killclient(const Arg *arg); @@ -185,9 +192,11 @@ static void monocle(Monitor *m); static void motionnotify(XEvent *e); static void movemouse(const Arg *arg); static Client *nexttiled(Client *c); +static unsigned long long now(void); static void pop(Client *c); static void propertynotify(XEvent *e); static void quit(const Arg *arg); +static Client *recttoclient(int x, int y, int w, int h, int include_floating); static Monitor *recttomon(int x, int y, int w, int h); static void resize(Client *c, int x, int y, int w, int h, int interact); static void resizeclient(Client *c, int x, int y, int w, int h); @@ -204,6 +213,7 @@ static void setlayout(const Arg *arg); static void setmfact(const Arg *arg); static void setup(void); static void seturgent(Client *c, int urg); +static void showcursor(const Arg *arg); static void showhide(Client *c); static void sigchld(int unused); static void spawn(const Arg *arg); @@ -244,6 +254,14 @@ static int bh; /* bar height */ static int lrpad; /* sum of left and right padding for text */ static int (*xerrorxlib)(Display *, XErrorEvent *); static unsigned int numlockmask = 0; +static int cursor_hidden = 0; +/* mouse_x and mouse_y are used to store the actual co-ordinates of the mouse cursor when + * hidden. These can be manipulated freely, e.g. when using the warp patch, to set a new + * cursor position for when the cursor is to be revealed again. */ +static int mouse_x = 0; +static int mouse_y = 0; +static int xi_opcode; +static unsigned long long last_button_press = 0; static void (*handler[LASTEvent]) (XEvent *) = { [ButtonPress] = buttonpress, [ClientMessage] = clientmessage, @@ -252,6 +270,7 @@ static void (*handler[LASTEvent]) (XEvent *) = { [DestroyNotify] = destroynotify, [EnterNotify] = enternotify, [Expose] = expose, + [GenericEvent] = genericevent, [FocusIn] = focusin, [KeyPress] = keypress, [MappingNotify] = mappingnotify, @@ -432,7 +451,20 @@ buttonpress(XEvent *e) selmon = m; focus(NULL); } - if (ev->window == selmon->barwin) { + + c = wintoclient(ev->window); + + if (!c && cursor_hidden) { + c = recttoclient(mouse_x, mouse_y, 1, 1, 1); + showcursor(NULL); + } + + if (c) { + focus(c); + restack(selmon); + XAllowEvents(dpy, ReplayPointer, CurrentTime); + click = ClkClientWin; + } else if (ev->window == selmon->barwin) { i = x = 0; do x += TEXTW(tags[i]); @@ -446,16 +478,14 @@ buttonpress(XEvent *e) click = ClkStatusText; else click = ClkWinTitle; - } else if ((c = wintoclient(ev->window))) { - focus(c); - restack(selmon); - XAllowEvents(dpy, ReplayPointer, CurrentTime); - click = ClkClientWin; } + for (i = 0; i < LENGTH(buttons); i++) if (click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)) buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg); + + last_button_press = now(); } void @@ -765,6 +795,9 @@ enternotify(XEvent *e) Monitor *m; XCrossingEvent *ev = &e->xcrossing; + if (cursor_hidden) + return; + if ((ev->mode != NotifyNormal || ev->detail == NotifyInferior) && ev->window != root) return; c = wintoclient(ev->window); @@ -862,6 +895,36 @@ focusstack(const Arg *arg) } } +void +genericevent(XEvent *e) +{ + if (e->xcookie.extension != xi_opcode) + return; + + if (!XGetEventData(dpy, &e->xcookie)) + return; + + switch (e->xcookie.evtype) { + case XI_RawMotion: + if (cursor_hidden) + showcursor(NULL); + break; + case XI_RawTouchBegin: + case XI_RawTouchEnd: + case XI_RawTouchUpdate: + if (!cursor_hidden) + hidecursor(NULL); + break; + case XI_RawKeyRelease: + if (now() - last_button_press > 2000 && !cursor_hidden) { + hidecursor(NULL); + } + break; + } + + XFreeEventData(dpy, &e->xcookie); +} + Atom getatomprop(Client *c, Atom prop) { @@ -885,6 +948,12 @@ getrootptr(int *x, int *y) unsigned int dui; Window dummy; + if (cursor_hidden) { + *x = mouse_x; + *y = mouse_y; + return 1; + } + return XQueryPointer(dpy, root, &dummy, &dummy, x, y, &di, &di, &dui); } @@ -968,6 +1037,20 @@ grabkeys(void) } } +void +hidecursor(const Arg *arg) +{ + if (cursor_hidden) + return; + + XFixesHideCursor(dpy, root); + if (getrootptr(&mouse_x, &mouse_y)) { + XWarpPointer(dpy, None, root, 0, 0, 0, 0, selmon->mx + selmon->mw, selmon->my); + } + + cursor_hidden = 1; +} + void incnmaster(const Arg *arg) { @@ -1200,6 +1283,13 @@ nexttiled(Client *c) return c; } +unsigned long long +now(void) { + struct timespec currentTime; + clock_gettime(CLOCK_REALTIME, ¤tTime); + return currentTime.tv_sec * 1000LL + currentTime.tv_nsec / 1000000LL; +} + void pop(Client *c) { @@ -1252,6 +1342,23 @@ quit(const Arg *arg) running = 0; } +Client * +recttoclient(int x, int y, int w, int h, int include_floating) +{ + Client *c, *r = NULL; + int a, area = 1; + + for (c = selmon->stack; c; c = c->snext) { + if (!ISVISIBLE(c) || (c->isfloating && !include_floating)) + continue; + if ((a = INTERSECTC(x, y, w, h, c)) >= area) { + area = a; + r = c; + } + } + return r; +} + Monitor * recttomon(int x, int y, int w, int h) { @@ -1593,6 +1700,25 @@ setup(void) |LeaveWindowMask|StructureNotifyMask|PropertyChangeMask; XChangeWindowAttributes(dpy, root, CWEventMask|CWCursor, &wa); XSelectInput(dpy, root, wa.event_mask); + + if (!XQueryExtension(dpy, "XInputExtension", &xi_opcode, &i, &i)) { + fprintf(stderr, "Warning: XInput is not available."); + } + /* Tell XInput to send us all RawMotion events. */ + unsigned char mask_bytes[XIMaskLen(XI_LASTEVENT)]; + memset(mask_bytes, 0, sizeof(mask_bytes)); + XISetMask(mask_bytes, XI_RawMotion); + XISetMask(mask_bytes, XI_RawKeyRelease); + XISetMask(mask_bytes, XI_RawTouchBegin); + XISetMask(mask_bytes, XI_RawTouchEnd); + XISetMask(mask_bytes, XI_RawTouchUpdate); + + XIEventMask mask; + mask.deviceid = XIAllMasterDevices; + mask.mask_len = sizeof(mask_bytes); + mask.mask = mask_bytes; + XISelectEvents(dpy, root, &mask, 1); + grabkeys(); focus(NULL); } @@ -1610,6 +1736,18 @@ seturgent(Client *c, int urg) XFree(wmh); } +void +showcursor(const Arg *arg) +{ + if (!cursor_hidden) + return; + + XWarpPointer(dpy, None, root, 0, 0, 0, 0, mouse_x, mouse_y); + XFixesShowCursor(dpy, root); + + cursor_hidden = 0; +} + void showhide(Client *c) { -- 2.19.1 From f1c11a763cb6943839c4e66c419bd84bd1b32459 Mon Sep 17 00:00:00 2001 From: Bakkeby Date: Thu, 21 Sep 2023 11:01:56 +0200 Subject: [PATCH 2/2] Adding cursorwarp patch on top of banish --- dwm.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/dwm.c b/dwm.c index bb7760b..1a8e311 100644 --- a/dwm.c +++ b/dwm.c @@ -238,6 +238,7 @@ static void updatetitle(Client *c); static void updatewindowtype(Client *c); static void updatewmhints(Client *c); static void view(const Arg *arg); +static void warp(Client *c); static Client *wintoclient(Window w); static Monitor *wintomon(Window w); static int xerror(Display *dpy, XErrorEvent *ee); @@ -867,6 +868,8 @@ focusmon(const Arg *arg) unfocus(selmon->sel, 0); selmon = m; focus(NULL); + if (m->sel) + warp(m->sel) } void @@ -892,6 +895,7 @@ focusstack(const Arg *arg) if (c) { focus(c); restack(selmon); + warp(c) } } @@ -1159,6 +1163,8 @@ manage(Window w, XWindowAttributes *wa) c->mon->sel = c; arrange(c->mon); XMapWindow(dpy, c->win); + if (c && c->mon == selmon && ISVISIBLE(c)) + warp(c); focus(NULL); } @@ -1919,6 +1925,8 @@ unmanage(Client *c, int destroyed) focus(NULL); updateclientlist(); arrange(m); + if (m->sel && m == selmon) + warp(m->sel); } void @@ -2182,6 +2190,18 @@ view(const Arg *arg) arrange(selmon); } +void +warp(Client *c) +{ + if (cursor_hidden) { + mouse_x = c->x + c->w/2; + mouse_y = c->y + c->h/2; + return; + } + + XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w/2, c->h/2); +} + Client * wintoclient(Window w) { -- 2.19.1