commit 83137d7d3a2a5195ba64352961eb572dd6489901
Author: Ivan Delalande <[email protected]>
Date:   Sun Jul 9 20:29:11 2017 -0700

    dwm: add the new cropwindows patch
    
    Create cropped views of existing windows to optimize screen space.
    XReparentWindow is fun!

diff --git a/dwm.suckless.org/patches/cropwindows.md 
b/dwm.suckless.org/patches/cropwindows.md
new file mode 100644
index 00000000..3e725edb
--- /dev/null
+++ 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 <[email protected]>
diff --git a/dwm.suckless.org/patches/dwm-cropwindows-20170709-ceac8c9.diff 
b/dwm.suckless.org/patches/dwm-cropwindows-20170709-ceac8c9.diff
new file mode 100644
index 00000000..798cf8eb
--- /dev/null
+++ 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;
+ }


Reply via email to