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; }