From ca26e9e8533da2657fcbb56a407eb5a6d2735719 Mon Sep 17 00:00:00 2001 From: Bakkeby Date: Fri, 28 Jun 2024 10:08:07 +0200 Subject: [PATCH] placemouse patch --- config.def.h | 12 +++- dwm.c | 166 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 177 insertions(+), 1 deletion(-) diff --git a/config.def.h b/config.def.h index 1c0b587..86f878a 100644 --- a/config.def.h +++ b/config.def.h @@ -104,7 +104,17 @@ static Button buttons[] = { { ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} }, { ClkWinTitle, 0, Button2, zoom, {0} }, { ClkStatusText, 0, Button2, spawn, {.v = termcmd } }, - { ClkClientWin, MODKEY, Button1, movemouse, {0} }, + /* placemouse options, choose which feels more natural: + * 0 - tiled position is relative to mouse cursor + * 1 - tiled postiion is relative to window center + * 2 - mouse pointer warps to window center + * + * The moveorplace uses movemouse or placemouse depending on the floating state + * of the selected client. Set up individual keybindings for the two if you want + * to control these separately (i.e. to retain the feature to move a tiled window + * into a floating position). + */ + { ClkClientWin, MODKEY, Button1, moveorplace, {.i = 1} }, { ClkClientWin, MODKEY, Button2, togglefloating, {0} }, { ClkClientWin, MODKEY, Button3, resizemouse, {0} }, { ClkTagBar, 0, Button1, view, {0} }, diff --git a/dwm.c b/dwm.c index 4465af1..8a0ae73 100644 --- a/dwm.c +++ b/dwm.c @@ -49,6 +49,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) @@ -93,6 +95,7 @@ struct Client { int bw, oldbw; unsigned int tags; int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen; + int beingmoved; Client *next; Client *snext; Monitor *mon; @@ -183,10 +186,13 @@ static void maprequest(XEvent *e); static void monocle(Monitor *m); static void motionnotify(XEvent *e); static void movemouse(const Arg *arg); +static void moveorplace(const Arg *arg); static Client *nexttiled(Client *c); +static void placemouse(const Arg *arg); static void pop(Client *); static void propertynotify(XEvent *e); static void quit(const Arg *arg); +static Client *recttoclient(int x, int y, int w, int h); 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); @@ -1132,6 +1138,14 @@ motionnotify(XEvent *e) mon = m; } +void +moveorplace(const Arg *arg) { + if ((!selmon->lt[selmon->sellt]->arrange || (selmon->sel && selmon->sel->isfloating))) + movemouse(arg); + else + placemouse(arg); +} + void movemouse(const Arg *arg) { @@ -1199,6 +1213,139 @@ nexttiled(Client *c) return c; } +void +placemouse(const Arg *arg) +{ + int x, y, px, py, ocx, ocy, nx = -9999, ny = -9999, freemove = 0; + Client *c, *r = NULL, *at, *prevr; + Monitor *m; + XEvent ev; + XWindowAttributes wa; + Time lasttime = 0; + int attachmode, prevattachmode; + attachmode = prevattachmode = -1; + + if (!(c = selmon->sel) || !c->mon->lt[c->mon->sellt]->arrange) /* no support for placemouse when floating layout is used */ + return; + if (c->isfullscreen) /* no support placing fullscreen windows by mouse */ + return; + restack(selmon); + prevr = c; + if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, + None, cursor[CurMove]->cursor, CurrentTime) != GrabSuccess) + return; + + c->isfloating = 0; + c->beingmoved = 1; + + XGetWindowAttributes(dpy, c->win, &wa); + ocx = wa.x; + ocy = wa.y; + + if (arg->i == 2) // warp cursor to client center + XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, WIDTH(c) / 2, HEIGHT(c) / 2); + + if (!getrootptr(&x, &y)) + return; + + do { + XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); + switch (ev.type) { + case ConfigureRequest: + case Expose: + case MapRequest: + handler[ev.type](&ev); + break; + case MotionNotify: + if ((ev.xmotion.time - lasttime) <= (1000 / 60)) + continue; + lasttime = ev.xmotion.time; + + nx = ocx + (ev.xmotion.x - x); + ny = ocy + (ev.xmotion.y - y); + + if (!freemove && (abs(nx - ocx) > snap || abs(ny - ocy) > snap)) + freemove = 1; + + if (freemove) + XMoveWindow(dpy, c->win, nx, ny); + + if ((m = recttomon(ev.xmotion.x, ev.xmotion.y, 1, 1)) && m != selmon) + selmon = m; + + if (arg->i == 1) { // tiled position is relative to the client window center point + px = nx + wa.width / 2; + py = ny + wa.height / 2; + } else { // tiled position is relative to the mouse cursor + px = ev.xmotion.x; + py = ev.xmotion.y; + } + + r = recttoclient(px, py, 1, 1); + + if (!r || r == c) + break; + + attachmode = 0; // below + if (((float)(r->y + r->h - py) / r->h) > ((float)(r->x + r->w - px) / r->w)) { + if (abs(r->y - py) < r->h / 2) + attachmode = 1; // above + } else if (abs(r->x - px) < r->w / 2) + attachmode = 1; // above + + if ((r && r != prevr) || (attachmode != prevattachmode)) { + detachstack(c); + detach(c); + if (c->mon != r->mon) { + arrangemon(c->mon); + c->tags = r->mon->tagset[r->mon->seltags]; + } + + c->mon = r->mon; + r->mon->sel = r; + + if (attachmode) { + if (r == r->mon->clients) + attach(c); + else { + for (at = r->mon->clients; at->next != r; at = at->next); + c->next = at->next; + at->next = c; + } + } else { + c->next = r->next; + r->next = c; + } + + attachstack(c); + arrangemon(r->mon); + prevr = r; + prevattachmode = attachmode; + } + break; + } + } while (ev.type != ButtonRelease); + XUngrabPointer(dpy, CurrentTime); + + if ((m = recttomon(ev.xmotion.x, ev.xmotion.y, 1, 1)) && m != c->mon) { + detach(c); + detachstack(c); + arrangemon(c->mon); + c->mon = m; + c->tags = m->tagset[m->seltags]; + attach(c); + attachstack(c); + selmon = m; + } + + focus(c); + c->beingmoved = 0; + + if (nx != -9999) + resize(c, nx, ny, c->w, c->h, 0); + arrangemon(c->mon); +} + void pop(Client *c) { @@ -1251,6 +1398,21 @@ quit(const Arg *arg) running = 0; } +Client * +recttoclient(int x, int y, int w, int h) +{ + Client *c, *r = NULL; + int a, area = 0; + + for (c = nexttiled(selmon->clients); c; c = nexttiled(c->next)) { + 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) { @@ -1281,6 +1443,10 @@ resizeclient(Client *c, int x, int y, int w, int h) c->oldy = c->y; c->y = wc.y = y; c->oldw = c->w; c->w = wc.width = w; c->oldh = c->h; c->h = wc.height = h; + + if (c->beingmoved) + return; + wc.border_width = c->bw; XConfigureWindow(dpy, c->win, CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc); configure(c); -- 2.45.2