sites

public wiki contents of suckless.org
git clone git://git.suckless.org/sites
Log | Files | Refs

commit 83137d7d3a2a5195ba64352961eb572dd6489901
parent 26b3d34aadd1baf01f7bd83b4ae666b88cbe7544
Author: Ivan Delalande <colona@ycc.fr>
Date:   Sun,  9 Jul 2017 20:29:11 -0700

dwm: add the new cropwindows patch

Create cropped views of existing windows to optimize screen space.
XReparentWindow is fun!

Diffstat:
Adwm.suckless.org/patches/cropwindows.md | 42++++++++++++++++++++++++++++++++++++++++++
Adwm.suckless.org/patches/dwm-cropwindows-20170709-ceac8c9.diff | 312+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 354 insertions(+), 0 deletions(-)

diff --git a/dwm.suckless.org/patches/cropwindows.md b/dwm.suckless.org/patches/cropwindows.md @@ -0,0 +1,42 @@ +cropwindows +=========== + +Description +----------- + +Create cropped views of existing windows to display only part of them, typically +to reclaim screen space from badly framed videos or programs and websites with +terrible designs. + +Usage +----- + +Look at the changes made to `config.def.h`: pass `1` to `resizemouse` to create +a cropped window and to `movemouse` to move the underlying window in the crop. + + { ClkClientWin, MODKEY|ShiftMask, Button1, movemouse, {.i = 1} }, + { ClkClientWin, MODKEY|ShiftMask, Button3, resizemouse, {.i = 1} }, + +Cropped windows are always in the floating state, use `togglefloating` +(`mod-shift-space` by default) to uncrop and restore the underlying window to +its original size and state. + +Download +-------- + + * [dwm-cropwindows-20170709-ceac8c9.diff](dwm-cropwindows-20170709-ceac8c9.diff) + +Notes +----- + + * tested with a limited set of clients and use-cases, some X11 events are + probably not passed or handled correctly, report bugs if you see any, + * known bug: if you crop a window at the same time another window is unmapped, + there is a chance that dwm will lose control of the window you are cropping + and your only option will be to kill it (this is a pain to fix properly), + * improvements: investigate `xextproto/shape` for non-rectangular crops! + +Authors +------- + + * Ivan Delalande <colona@ycc.fr> diff --git a/dwm.suckless.org/patches/dwm-cropwindows-20170709-ceac8c9.diff b/dwm.suckless.org/patches/dwm-cropwindows-20170709-ceac8c9.diff @@ -0,0 +1,312 @@ +diff --git a/config.def.h b/config.def.h +index a9ac303..a9797ac 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -105,8 +105,10 @@ static Button buttons[] = { + { ClkWinTitle, 0, Button2, zoom, {0} }, + { ClkStatusText, 0, Button2, spawn, {.v = termcmd } }, + { ClkClientWin, MODKEY, Button1, movemouse, {0} }, ++ { ClkClientWin, MODKEY|ShiftMask, Button1, movemouse, {.i = 1} }, + { ClkClientWin, MODKEY, Button2, togglefloating, {0} }, + { ClkClientWin, MODKEY, Button3, resizemouse, {0} }, ++ { ClkClientWin, MODKEY|ShiftMask, Button3, resizemouse, {.i = 1} }, + { ClkTagBar, 0, Button1, view, {0} }, + { ClkTagBar, 0, Button3, toggleview, {0} }, + { ClkTagBar, MODKEY, Button1, tag, {0} }, +diff --git a/dwm.c b/dwm.c +index a5ce993..e922ef6 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -98,6 +98,7 @@ struct Client { + Client *snext; + Monitor *mon; + Window win; ++ Client *crop; + }; + + typedef struct { +@@ -276,6 +277,88 @@ static Window root, wmcheckwin; + struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; + + /* function implementations */ ++Client * ++cropwintoclient(Window w) ++{ ++ Client *c; ++ Monitor *m; ++ ++ for (m = mons; m; m = m->next) ++ for (c = m->clients; c; c = c->next) ++ if (c->crop && c->crop->win == w) ++ return c; ++ return NULL; ++} ++ ++void ++cropwindow(Client *c) ++{ ++ int x, y; ++ XEvent ev; ++ XSetWindowAttributes wa = { .event_mask = SubstructureRedirectMask }; ++ ++ if (!getrootptr(&x, &y)) ++ return; ++ if (!c->crop) { ++ c->crop = ecalloc(1, sizeof(Client)); ++ memcpy(c->crop, c, sizeof(Client)); ++ c->crop->crop = NULL; ++ c->crop->x = c->crop->y = c->crop->bw = 0; ++ c->basew = c->baseh = c->mina = c->maxa = 0; ++ c->maxw = c->maxh = c->incw = c->inch = 0; ++ c->minw = c->minh = 1; ++ if (!c->isfloating) ++ togglefloating(NULL); ++ c->win = XCreateWindow(dpy, root, x, y, 1, 1, c->bw, ++ 0, 0, 0, CWEventMask, &wa); ++ XReparentWindow(dpy, c->crop->win, c->win, 0, 0); ++ XMapWindow(dpy, c->win); ++ focus(c); ++ XCheckTypedWindowEvent(dpy, c->crop->win, UnmapNotify, &ev); ++ if (XCheckTypedWindowEvent(dpy, root, UnmapNotify, &ev) ++ && ev.xunmap.window != c->crop->win) ++ XPutBackEvent(dpy, &ev); ++ } ++ resizeclient(c->crop, c->crop->x + c->x - x, c->crop->y + c->y - y, ++ c->crop->w, c->crop->h); ++ resizeclient(c, x, y, 1, 1); ++} ++ ++void ++cropdelete(Client *c) ++{ ++ Client *crop; ++ XEvent ev; ++ ++ c->crop->x += c->x; ++ c->crop->y += c->y; ++ c->crop->bw = c->bw; ++ c->crop->next = c->next; ++ c->crop->snext = c->snext; ++ c->crop->tags = c->tags; ++ c->crop->mon = c->mon; ++ XReparentWindow(dpy, c->crop->win, root, c->crop->x, c->crop->y); ++ XDestroyWindow(dpy, c->win); ++ crop = c->crop; ++ memcpy(c, c->crop, sizeof(Client)); ++ free(crop); ++ resize(c, c->x, c->y, c->w, c->h, 0); ++ focus(c); ++ XCheckTypedWindowEvent(dpy, c->win, UnmapNotify, &ev); ++} ++ ++void ++cropresize(Client* c) ++{ ++ resizeclient(c->crop, ++ BETWEEN(c->crop->x, -(c->crop->w), 0) ? c->crop->x : 0, ++ BETWEEN(c->crop->y, -(c->crop->h), 0) ? c->crop->y : 0, ++ c->crop->w, c->crop->h); ++ resize(c, c->x, c->y, ++ MIN(c->w, c->crop->x + c->crop->w), ++ MIN(c->h, c->crop->y + c->crop->h), 0); ++} ++ + void + applyrules(Client *c) + { +@@ -516,7 +599,7 @@ clientmessage(XEvent *e) + XClientMessageEvent *cme = &e->xclient; + Client *c = wintoclient(cme->window); + +- if (!c) ++ if (!c && !(c = cropwintoclient(cme->window))) + return; + if (cme->message_type == netatom[NetWMState]) { + if (cme->data.l[1] == netatom[NetWMFullscreen] +@@ -579,16 +662,19 @@ configurenotify(XEvent *e) + void + configurerequest(XEvent *e) + { +- Client *c; ++ Client *c, *cc = NULL; + Monitor *m; + XConfigureRequestEvent *ev = &e->xconfigurerequest; + XWindowChanges wc; + +- if ((c = wintoclient(ev->window))) { ++ if ((c = wintoclient(ev->window)) ++ || (c = cc = cropwintoclient(ev->window))) { + if (ev->value_mask & CWBorderWidth) + c->bw = ev->border_width; + else if (c->isfloating || !selmon->lt[selmon->sellt]->arrange) { + m = c->mon; ++ if (c->crop) ++ c = c->crop; + if (ev->value_mask & CWX) { + c->oldx = c->x; + c->x = m->mx + ev->x; +@@ -613,6 +699,8 @@ configurerequest(XEvent *e) + configure(c); + if (ISVISIBLE(c)) + XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); ++ if (cc) ++ cropresize(cc); + } else + configure(c); + } else { +@@ -651,7 +739,7 @@ destroynotify(XEvent *e) + Client *c; + XDestroyWindowEvent *ev = &e->xdestroywindow; + +- if ((c = wintoclient(ev->window))) ++ if ((c = wintoclient(ev->window)) || (c = cropwintoclient(ev->window))) + unmanage(c, 1); + } + +@@ -762,6 +850,8 @@ enternotify(XEvent *e) + if ((ev->mode != NotifyNormal || ev->detail == NotifyInferior) && ev->window != root) + return; + c = wintoclient(ev->window); ++ if (!c) ++ c = cropwintoclient(ev->window); + m = c ? c->mon : wintomon(ev->window); + if (m != selmon) { + unfocus(selmon->sel, 1); +@@ -1005,6 +1095,8 @@ killclient(const Arg *arg) + { + if (!selmon->sel) + return; ++ if (selmon->sel->crop) ++ cropdelete(selmon->sel); + if (!sendevent(selmon->sel, wmatom[WMDelete])) { + XGrabServer(dpy); + XSetErrorHandler(xerrordummy); +@@ -1150,6 +1242,10 @@ movemouse(const Arg *arg) + restack(selmon); + ocx = c->x; + ocy = c->y; ++ if (arg->i == 1 && c->crop) { ++ ocx = c->crop->x; ++ ocy = c->crop->y; ++ } + if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, + None, cursor[CurMove]->cursor, CurrentTime) != GrabSuccess) + return; +@@ -1170,6 +1266,12 @@ movemouse(const Arg *arg) + + nx = ocx + (ev.xmotion.x - x); + ny = ocy + (ev.xmotion.y - y); ++ if (arg->i == 1 && c->crop) { ++ c->crop->x = nx; ++ c->crop->y = ny; ++ cropresize(c); ++ continue; ++ } + if (abs(selmon->wx - nx) < snap) + nx = selmon->wx; + else if (abs((selmon->wx + selmon->ww) - (nx + WIDTH(c))) < snap) +@@ -1221,7 +1323,10 @@ propertynotify(XEvent *e) + updatestatus(); + else if (ev->state == PropertyDelete) + return; /* ignore */ +- else if ((c = wintoclient(ev->window))) { ++ else if ((c = wintoclient(ev->window)) ++ || (c = cropwintoclient(ev->window))) { ++ if (c->crop) ++ c = c->crop; + switch(ev->atom) { + default: break; + case XA_WM_TRANSIENT_FOR: +@@ -1303,12 +1408,16 @@ resizemouse(const Arg *arg) + if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */ + return; + restack(selmon); ++ if (arg->i == 1) ++ cropwindow(c); + ocx = c->x; + ocy = c->y; + if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, + None, cursor[CurResize]->cursor, CurrentTime) != GrabSuccess) + return; +- XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); ++ if (arg->i != 1) ++ XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, ++ c->w + c->bw - 1, c->h + c->bw - 1); + do { + XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); + switch(ev.type) { +@@ -1324,6 +1433,10 @@ resizemouse(const Arg *arg) + + nw = MAX(ev.xmotion.x - ocx - 2 * c->bw + 1, 1); + nh = MAX(ev.xmotion.y - ocy - 2 * c->bw + 1, 1); ++ if (c->crop) { ++ nw = MIN(nw, c->crop->w + c->crop->x); ++ nh = MIN(nh, c->crop->h + c->crop->y); ++ } + if (c->mon->wx + nw >= selmon->wx && c->mon->wx + nw <= selmon->wx + selmon->ww + && c->mon->wy + nh >= selmon->wy && c->mon->wy + nh <= selmon->wy + selmon->wh) + { +@@ -1430,6 +1543,8 @@ setclientstate(Client *c, long state) + { + long data[] = { state, None }; + ++ if (c->crop) ++ c = c->crop; + XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32, + PropModeReplace, (unsigned char *)data, 2); + } +@@ -1462,6 +1577,8 @@ sendevent(Client *c, Atom proto) + void + setfocus(Client *c) + { ++ if (c->crop) ++ c = c->crop; + if (!c->neverfocus) { + XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); + XChangeProperty(dpy, root, netatom[NetActiveWindow], +@@ -1474,6 +1591,8 @@ setfocus(Client *c) + void + setfullscreen(Client *c, int fullscreen) + { ++ if (c->crop) ++ c = c->crop; + if (fullscreen && !c->isfullscreen) { + XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, + PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1); +@@ -1718,6 +1837,8 @@ togglefloating(const Arg *arg) + if (selmon->sel->isfloating) + resize(selmon->sel, selmon->sel->x, selmon->sel->y, + selmon->sel->w, selmon->sel->h, 0); ++ if (!selmon->sel->isfloating && selmon->sel->crop) ++ cropdelete(selmon->sel); + arrange(selmon); + } + +@@ -1767,6 +1888,8 @@ unmanage(Client *c, int destroyed) + Monitor *m = c->mon; + XWindowChanges wc; + ++ if (c->crop) ++ cropdelete(c); + detach(c); + detachstack(c); + if (!destroyed) { +@@ -1792,7 +1915,8 @@ unmapnotify(XEvent *e) + Client *c; + XUnmapEvent *ev = &e->xunmap; + +- if ((c = wintoclient(ev->window))) { ++ if ((c = wintoclient(ev->window)) ++ || (c = cropwintoclient(ev->window))) { + if (ev->send_event) + setclientstate(c, WithdrawnState); + else +@@ -2070,7 +2194,7 @@ wintomon(Window w) + for (m = mons; m; m = m->next) + if (w == m->barwin) + return m; +- if ((c = wintoclient(w))) ++ if ((c = wintoclient(w)) || (c = cropwintoclient(w))) + return c->mon; + return selmon; + }