aboutsummaryrefslogtreecommitdiffstats
path: root/client.c
diff options
context:
space:
mode:
Diffstat (limited to 'client.c')
-rw-r--r--client.c935
1 files changed, 935 insertions, 0 deletions
diff --git a/client.c b/client.c
new file mode 100644
index 0000000..961be7c
--- /dev/null
+++ b/client.c
@@ -0,0 +1,935 @@
+/*
+ * calmwm - the calm window manager
+ *
+ * Copyright (c) 2004 Marius Aamodt Eriksen <marius@monkey.org>
+ * All rights reserved.
+ *
+ * $Id: client.c,v 1.1.1.1 2007/04/27 17:58:48 bernd Exp $
+ */
+
+#include "headers.h"
+#include "calmwm.h"
+
+static struct client_ctx *client__cycle(struct client_ctx *cc,
+ struct client_ctx *(*iter)(struct client_ctx *));
+int _inwindowbounds(struct client_ctx *, int, int);
+
+static char emptystring[] = "";
+
+struct client_ctx *_curcc = NULL;
+
+void
+client_setup(void)
+{
+ TAILQ_INIT(&G_clientq);
+}
+
+struct client_ctx *
+client_find(Window win)
+{
+ struct client_ctx *cc;
+
+ TAILQ_FOREACH(cc, &G_clientq, entry)
+ if (cc->pwin == win || cc->win == win)
+ return (cc);
+
+ return (NULL);
+}
+
+struct client_ctx *
+client_new(Window win, struct screen_ctx *sc, int mapped)
+{
+ struct client_ctx *cc;
+ long tmp;
+ XSetWindowAttributes pxattr;
+ XWindowAttributes wattr;
+ int x, y, height, width, state;
+ XWMHints *wmhints;
+
+ if (win == None)
+ return (NULL);
+
+ XCALLOC(cc, struct client_ctx);
+
+ XGrabServer(G_dpy);
+
+ cc->state = mapped ? NormalState : IconicState;
+ cc->sc = sc;
+ cc->win = win;
+ cc->size= XAllocSizeHints();
+ if (cc->size->width_inc == 0)
+ cc->size->width_inc = 1;
+ if (cc->size->height_inc == 0)
+ cc->size->height_inc = 1;
+
+ TAILQ_INIT(&cc->nameq);
+ client_setname(cc);
+
+ /*
+ * conf_client() needs at least cc->win and cc->name
+ */
+ conf_client(cc);
+
+ XGetWMNormalHints(G_dpy, cc->win, cc->size, &tmp);
+ XGetWindowAttributes(G_dpy, cc->win, &wattr);
+
+ if (cc->size->flags & PBaseSize) {
+ cc->geom.min_dx = cc->size->base_width;
+ cc->geom.min_dy = cc->size->base_height;
+ } else if (cc->size->flags & PMinSize) {
+ cc->geom.min_dx = cc->size->min_width;
+ cc->geom.min_dy = cc->size->min_height;
+ }
+
+ /* Saved pointer position */
+ cc->ptr.x = -1;
+ cc->ptr.y = -1;
+
+ client_gravitate(cc, 1);
+
+ cc->geom.x = wattr.x;
+ cc->geom.y = wattr.y;
+ cc->geom.width = wattr.width;
+ cc->geom.height = wattr.height;
+ cc->geom.height = wattr.height;
+ cc->cmap = wattr.colormap;
+
+ if (wattr.map_state != IsViewable) {
+ client_placecalc(cc);
+ if ((wmhints = XGetWMHints(G_dpy, cc->win)) != NULL) {
+ if (wmhints->flags & StateHint)
+ xu_setstate(cc, wmhints->initial_state);
+
+ XFree(wmhints);
+ }
+ }
+
+ if (xu_getstate(cc, &state) < 0)
+ state = NormalState;
+
+ XSelectInput(G_dpy, cc->win,
+ ColormapChangeMask|EnterWindowMask|PropertyChangeMask|KeyReleaseMask);
+
+ x = cc->geom.x - cc->bwidth;
+ y = cc->geom.y - cc->bwidth;
+
+ width = cc->geom.width;
+ height = cc->geom.height;
+ if (cc->bwidth > 1) {
+ width += (cc->bwidth)*2;
+ height += (cc->bwidth)*2;
+ }
+
+ pxattr.override_redirect = True;
+ pxattr.background_pixel = sc->bgcolor.pixel;
+ pxattr.event_mask =
+ ChildMask|ButtonPressMask|ButtonReleaseMask|
+ ExposureMask|EnterWindowMask;
+/* pxattr.border_pixel = sc->blackpix; */
+/* pxattr.background_pixel = sc->whitepix; */
+
+
+/* cc->pwin = XCreateSimpleWindow(G_dpy, sc->rootwin, */
+/* x, y, width, height, 1, sc->blackpix, sc->whitepix); */
+
+ cc->pwin = XCreateWindow(G_dpy, sc->rootwin, x, y,
+ width, height, 0, /* XXX */
+ DefaultDepth(G_dpy, sc->which), CopyFromParent,
+ DefaultVisual(G_dpy, sc->which),
+ CWOverrideRedirect | CWBackPixel | CWEventMask, &pxattr);
+
+ if (G_doshape) {
+ XRectangle *r;
+ int n, tmp;
+
+ XShapeSelectInput(G_dpy, cc->win, ShapeNotifyMask);
+
+ r = XShapeGetRectangles(G_dpy, cc->win, ShapeBounding, &n, &tmp);
+ if (n > 1)
+ XShapeCombineShape(G_dpy, cc->pwin, ShapeBounding,
+ 0, 0, /* XXX border */
+ cc->win, ShapeBounding, ShapeSet);
+ XFree(r);
+ }
+
+ cc->active = 0;
+ client_draw_border(cc);
+
+ XAddToSaveSet(G_dpy, cc->win);
+ XSetWindowBorderWidth(G_dpy, cc->win, 0);
+ XReparentWindow(G_dpy, cc->win, cc->pwin, cc->bwidth, cc->bwidth);
+
+ /* Notify client of its configuration. */
+ xev_reconfig(cc);
+
+ XMapRaised(G_dpy, cc->pwin);
+ XMapWindow(G_dpy, cc->win);
+ xu_setstate(cc, cc->state);
+
+ XSync(G_dpy, False);
+ XUngrabServer(G_dpy);
+
+ TAILQ_INSERT_TAIL(&sc->mruq, cc, mru_entry);
+ TAILQ_INSERT_TAIL(&G_clientq, cc, entry);
+
+ client_gethints(cc);
+ client_update(cc);
+
+ if (mapped) {
+ if (G_conf.flags & CONF_STICKY_GROUPS)
+ group_sticky(cc);
+ else
+ group_autogroup(cc);
+ }
+
+ return (cc);
+}
+
+int
+client_delete(struct client_ctx *cc, int sendevent, int ignorewindow)
+{
+ struct screen_ctx *sc = CCTOSC(cc);
+ struct winname *wn;
+
+ if (cc->state == IconicState && !sendevent)
+ return (1);
+
+ group_client_delete(cc);
+ XGrabServer(G_dpy);
+
+ xu_setstate(cc, WithdrawnState);
+ XRemoveFromSaveSet(G_dpy, cc->win);
+
+ if (!ignorewindow) {
+ client_gravitate(cc, 0);
+ XSetWindowBorderWidth(G_dpy, cc->win, 1); /* XXX */
+ XReparentWindow(G_dpy, cc->win,
+ sc->rootwin, cc->geom.x, cc->geom.y);
+ }
+ if (cc->pwin)
+ XDestroyWindow(G_dpy, cc->pwin);
+
+ XSync(G_dpy, False);
+ XUngrabServer(G_dpy);
+
+ TAILQ_REMOVE(&sc->mruq, cc, mru_entry);
+ TAILQ_REMOVE(&G_clientq, cc, entry);
+
+ if (_curcc == cc)
+ _curcc = NULL;
+
+ if (sc->cycle_client == cc)
+ sc->cycle_client = NULL;
+
+ XFree(cc->size);
+
+ while ((wn = TAILQ_FIRST(&cc->nameq)) != NULL) {
+ TAILQ_REMOVE(&cc->nameq, wn, entry);
+ if (wn->name != emptystring)
+ XFree(wn->name);
+ xfree(wn);
+ }
+
+ client_freehints(cc);
+
+ xfree(cc);
+
+ return (0);
+}
+
+void
+client_leave(struct client_ctx *cc)
+{
+ struct screen_ctx *sc;
+
+ if (cc == NULL)
+ cc = _curcc;
+ if (cc == NULL)
+ return;
+ sc = CCTOSC(cc);
+
+ xu_btn_ungrab(sc->rootwin, AnyModifier, Button1);
+}
+
+void
+client_nocurrent(void)
+{
+ if (_curcc != NULL)
+ client_setactive(_curcc, 0);
+
+ _curcc = NULL;
+}
+
+void
+client_setactive(struct client_ctx *cc, int fg)
+{
+ struct screen_ctx* sc;
+
+ if (cc == NULL)
+ cc = _curcc;
+ if (cc == NULL)
+ return;
+
+ sc = CCTOSC(cc);
+
+ if (fg) {
+ XInstallColormap(G_dpy, cc->cmap);
+ XSetInputFocus(G_dpy, cc->win,
+ RevertToPointerRoot, CurrentTime);
+ xu_btn_grab(cc->pwin, Mod1Mask, AnyButton);
+ xu_btn_grab(cc->pwin, ControlMask|Mod1Mask, Button1);
+ /*
+ * If we're in the middle of alt-tabbing, don't change
+ * the order please.
+ */
+ if (!sc->altpersist)
+ client_mtf(cc);
+ } else
+ client_leave(cc);
+
+ if (fg && _curcc != cc) {
+ client_setactive(NULL, 0);
+ _curcc = cc;
+ }
+
+ cc->active = fg;
+ client_draw_border(cc);
+}
+
+struct client_ctx *
+client_current(void)
+{
+ return (_curcc);
+}
+
+void
+client_gravitate(struct client_ctx *cc, int yes)
+{
+ int dx = 0, dy = 0, mult = yes ? 1 : -1;
+ int gravity = (cc->size->flags & PWinGravity) ?
+ cc->size->win_gravity : NorthWestGravity;
+
+ switch (gravity) {
+ case NorthWestGravity:
+ case SouthWestGravity:
+ case NorthEastGravity:
+ case StaticGravity:
+ dx = cc->bwidth;
+ case NorthGravity:
+ dy = cc->bwidth;
+ break;
+ }
+
+ cc->geom.x += mult*dx;
+ cc->geom.y += mult*dy;
+}
+
+void
+client_maximize(struct client_ctx *cc)
+{
+ if (cc->flags & CLIENT_MAXIMIZED) {
+ cc->flags &= ~CLIENT_MAXIMIZED;
+ cc->geom = cc->savegeom;
+ } else {
+ XWindowAttributes rootwin_geom;
+ struct screen_ctx *sc = CCTOSC(cc);
+
+ XGetWindowAttributes(G_dpy, sc->rootwin, &rootwin_geom);
+ cc->savegeom = cc->geom;
+ cc->geom.x = 0;
+ cc->geom.y = 0;
+ cc->geom.height = rootwin_geom.height;
+ cc->geom.width = rootwin_geom.width;
+ cc->flags |= CLIENT_MAXIMIZED;
+ }
+
+ client_resize(cc);
+}
+
+void
+client_push_geometry(struct client_ctx *cc)
+{
+ cc->savegeom = cc->geom;
+}
+
+void
+client_restore_geometry(struct client_ctx *cc)
+{
+ cc->geom = cc->savegeom;
+ client_resize(cc);
+}
+
+void
+client_resize(struct client_ctx *cc)
+{
+ XMoveResizeWindow(G_dpy, cc->pwin, cc->geom.x - cc->bwidth,
+ cc->geom.y - cc->bwidth, cc->geom.width + cc->bwidth*2,
+ cc->geom.height + cc->bwidth*2);
+ XMoveResizeWindow(G_dpy, cc->win, cc->bwidth, cc->bwidth,
+ cc->geom.width, cc->geom.height);
+ xev_reconfig(cc);
+ client_draw_border(cc);
+}
+
+void
+client_move(struct client_ctx *cc)
+{
+ XMoveWindow(G_dpy, cc->pwin,
+ cc->geom.x - cc->bwidth, cc->geom.y - cc->bwidth);
+ xev_reconfig(cc);
+}
+
+void
+client_lower(struct client_ctx *cc)
+{
+ XLowerWindow(G_dpy, cc->pwin);
+}
+
+void
+client_raise(struct client_ctx *cc)
+{
+ XRaiseWindow(G_dpy, cc->pwin);
+ client_draw_border(cc);
+}
+
+void
+client_warp(struct client_ctx *cc)
+{
+ client_raise(cc);
+ xu_ptr_setpos(cc->pwin, 0, 0);
+}
+
+void
+client_ptrwarp(struct client_ctx *cc)
+{
+ int x = cc->ptr.x, y = cc->ptr.y;
+
+ if (x == -1 || y == -1) {
+ x = cc->geom.width / 2;
+ y = cc->geom.height / 2;
+ }
+
+ client_raise(cc);
+ xu_ptr_setpos(cc->pwin, x, y);
+}
+
+void
+client_ptrsave(struct client_ctx *cc)
+{
+ int x, y;
+
+ xu_ptr_getpos(cc->pwin, &x, &y);
+ if (_inwindowbounds(cc, x, y)) {
+ cc->ptr.x = x;
+ cc->ptr.y = y;
+ }
+}
+
+void
+client_hide(struct client_ctx *cc)
+{
+ /* XXX - add wm_state stuff */
+ XUnmapWindow(G_dpy, cc->pwin);
+ XUnmapWindow(G_dpy, cc->win);
+
+ cc->active = 0;
+ cc->flags |= CLIENT_HIDDEN;
+ xu_setstate(cc, IconicState);
+
+ if (cc == _curcc)
+ _curcc = NULL;
+}
+
+void
+client_unhide(struct client_ctx *cc)
+{
+ XMapWindow(G_dpy, cc->win);
+ XMapRaised(G_dpy, cc->pwin);
+
+ cc->flags &= ~CLIENT_HIDDEN;
+ xu_setstate(cc, NormalState);
+}
+
+void
+client_draw_border(struct client_ctx *cc)
+{
+ struct screen_ctx *sc = CCTOSC(cc);
+
+ if (cc->active) {
+ XSetWindowBackground(G_dpy, cc->pwin, client_bg_pixel(cc));
+ XClearWindow(G_dpy, cc->pwin);
+
+ if (!cc->highlight && cc->bwidth > 1)
+ XDrawRectangle(G_dpy, cc->pwin, sc->gc, 1, 1,
+ cc->geom.width + cc->bwidth,
+ cc->geom.height + cc->bwidth);
+ } else {
+ XSetWindowBackgroundPixmap(G_dpy, cc->pwin,
+ client_bg_pixmap(cc));
+ if (cc->bwidth > 1)
+ XSetWindowBackgroundPixmap(G_dpy,
+ cc->pwin, client_bg_pixmap(cc));
+
+ XClearWindow(G_dpy, cc->pwin);
+ }
+}
+
+u_long
+client_bg_pixel(struct client_ctx *cc)
+{
+ struct screen_ctx *sc = CCTOSC(cc);
+ u_long pixl;
+
+ switch (cc->highlight) {
+ case CLIENT_HIGHLIGHT_BLUE:
+ pixl = sc->bluepixl;
+ break;
+ case CLIENT_HIGHLIGHT_RED:
+ pixl = sc->redpixl;
+ break;
+ default:
+ pixl = sc->blackpixl;
+ break;
+ }
+
+ return (pixl);
+}
+
+Pixmap
+client_bg_pixmap(struct client_ctx *cc)
+{
+ struct screen_ctx *sc = CCTOSC(cc);
+ Pixmap pix;
+
+ switch (cc->highlight) {
+ case CLIENT_HIGHLIGHT_BLUE:
+ pix = sc->blue;
+ break;
+ case CLIENT_HIGHLIGHT_RED:
+ pix = sc->red;
+ break;
+ default:
+ pix = sc->gray;
+ break;
+ }
+
+ return (pix);
+}
+
+void
+client_update(struct client_ctx *cc)
+{
+ Atom *p, wm_delete, wm_protocols, wm_take_focus;
+ int i;
+ long n;
+
+ /* XXX cache these. */
+ wm_delete = XInternAtom(G_dpy, "WM_DELETE_WINDOW", False);
+ wm_protocols = XInternAtom(G_dpy, "WM_PROTOCOLS", False);
+ wm_take_focus = XInternAtom(G_dpy, "WM_TAKE_FOCUS", False);
+
+ if ((n = xu_getprop(cc, wm_protocols,
+ XA_ATOM, 20L, (u_char **)&p)) <= 0)
+ return;
+
+ for (i = 0; i < n; i++)
+ if (p[i] == wm_delete)
+ cc->xproto |= CLIENT_PROTO_DELETE;
+ else if (p[i] == wm_take_focus)
+ cc->xproto |= CLIENT_PROTO_TAKEFOCUS;
+
+ XFree(p);
+}
+
+void
+client_send_delete(struct client_ctx *cc)
+{
+ Atom wm_delete, wm_protocols;
+
+ /* XXX - cache */
+ wm_delete = XInternAtom(G_dpy, "WM_DELETE_WINDOW", False);
+ wm_protocols = XInternAtom(G_dpy, "WM_PROTOCOLS", False);
+
+ if (cc->xproto & CLIENT_PROTO_DELETE)
+ xu_sendmsg(cc, wm_protocols, wm_delete);
+ else
+ XKillClient(G_dpy, cc->win);
+}
+
+void
+client_setname(struct client_ctx *cc)
+{
+ char *newname;
+ struct winname *wn;
+
+ XFetchName(G_dpy, cc->win, &newname);
+ if (newname == NULL)
+ newname = emptystring;
+
+ TAILQ_FOREACH(wn, &cc->nameq, entry)
+ if (strcmp(wn->name, newname) == 0) {
+ /* Move to the last since we got a hit. */
+ TAILQ_REMOVE(&cc->nameq, wn, entry);
+ TAILQ_INSERT_TAIL(&cc->nameq, wn, entry);
+ goto match;
+ }
+
+ XMALLOC(wn, struct winname);
+ wn->name = newname;
+ TAILQ_INSERT_TAIL(&cc->nameq, wn, entry);
+ cc->nameqlen++;
+
+match:
+ cc->name = wn->name;
+
+ /* Now, do some garbage collection. */
+ if (cc->nameqlen > CLIENT_MAXNAMEQLEN) {
+ wn = TAILQ_FIRST(&cc->nameq);
+ assert(wn != NULL);
+ TAILQ_REMOVE(&cc->nameq, wn, entry);
+ if (wn->name != emptystring)
+ XFree(wn->name);
+ xfree(wn);
+ cc->nameqlen--;
+ }
+
+ return;
+}
+
+/*
+ * TODO: seems to have some issues still on the first invocation
+ * (globally the first)
+ */
+
+struct client_ctx *
+client_cyclenext(struct client_ctx *cc, int reverse)
+{
+ struct screen_ctx *sc = CCTOSC(cc);
+ struct client_ctx *(*iter)(struct client_ctx *) =
+ reverse ? &client_mruprev : &client_mrunext;
+
+ /* TODO: maybe this should just be a CIRCLEQ. */
+
+ /* if altheld; then reset the iterator to the beginning */
+ if (!sc->altpersist || sc->cycle_client == NULL)
+ sc->cycle_client = TAILQ_FIRST(&sc->mruq);
+
+ if (sc->cycle_client == NULL)
+ return (NULL);
+
+ /*
+ * INVARIANT: as long as sc->cycle_client != NULL here, we
+ * won't exit with sc->cycle_client == NULL
+ */
+
+ if ((sc->cycle_client = client__cycle(cc, iter)) == NULL)
+ sc->cycle_client = cc;
+
+ /* Do the actual warp. */
+ client_ptrsave(cc);
+ client_ptrwarp(sc->cycle_client);
+ sc->altpersist = 1; /* This is reset when alt is let go... */
+
+ /* Draw window. */
+ client_cycleinfo(sc->cycle_client);
+
+ return (sc->cycle_client);
+}
+
+/*
+ * XXX - have to have proper exposure handling here. we will probably
+ * have to do this by registering with the event loop a function to
+ * redraw, then match that on windows.
+ */
+
+void
+client_cycleinfo(struct client_ctx *cc)
+{
+ int w, h, nlines, i, n, oneh, curn = -1, x, y, diff;
+ struct client_ctx *ccc, *list[3];
+ struct screen_ctx *sc = CCTOSC(cc);
+ struct fontdesc *font = DefaultFont;
+
+ memset(list, 0, sizeof(list));
+
+ nlines = 0;
+ TAILQ_FOREACH(ccc, &sc->mruq, mru_entry)
+ nlines++;
+ nlines = MIN(nlines, 3);
+
+ oneh = font_ascent(font) + font_descent(font) + 1;
+ h = nlines*oneh;
+
+ list[1] = cc;
+
+ if (nlines > 1)
+ list[2] = client__cycle(cc, &client_mrunext);
+ if (nlines > 2)
+ list[0] = client__cycle(cc, &client_mruprev);
+
+ w = 0;
+ for (i = 0; i < sizeof(list)/sizeof(list[0]); i++) {
+ if ((ccc = list[i]) == NULL)
+ continue;
+ w = MAX(w, font_width(font, ccc->name, strlen(ccc->name)));
+ }
+
+ w += 4;
+
+ /* try to fit. */
+
+ if ((x = cc->ptr.x) < 0 || (y = cc->ptr.y) < 0) {
+ x = cc->geom.width / 2;
+ y = cc->geom.height / 2;
+ }
+
+ if ((diff = cc->geom.width - (x + w)) < 0)
+ x += diff;
+
+ if ((diff = cc->geom.height - (y + h)) < 0)
+ y += diff;
+
+ XReparentWindow(G_dpy, sc->infowin, cc->win, 0, 0);
+ XMoveResizeWindow(G_dpy, sc->infowin, x, y, w, h);
+ XMapRaised(G_dpy, sc->infowin);
+ XClearWindow(G_dpy, sc->infowin);
+
+ for (i = 0, n = 0; i < sizeof(list)/sizeof(list[0]); i++) {
+ if ((ccc = list[i]) == NULL)
+ continue;
+ font_draw(font, ccc->name, strlen(ccc->name), sc->infowin,
+ 2, n*oneh + font_ascent(font) + 1);
+ if (i == 1)
+ curn = n;
+ n++;
+ }
+
+ assert(curn != -1);
+
+ /* Highlight the current entry. */
+ XFillRectangle(G_dpy, sc->infowin, sc->hlgc, 0, curn*oneh, w, oneh);
+}
+
+struct client_ctx *
+client_mrunext(struct client_ctx *cc)
+{
+ struct screen_ctx *sc = CCTOSC(cc);
+ struct client_ctx *ccc;
+
+ return ((ccc = TAILQ_NEXT(cc, mru_entry)) != NULL ?
+ ccc : TAILQ_FIRST(&sc->mruq));
+}
+
+struct client_ctx *
+client_mruprev(struct client_ctx *cc)
+{
+ struct screen_ctx *sc = CCTOSC(cc);
+ struct client_ctx *ccc;
+
+ return ((ccc = TAILQ_PREV(cc, cycle_entry_q, mru_entry)) != NULL ?
+ ccc : TAILQ_LAST(&sc->mruq, cycle_entry_q));
+}
+
+static struct client_ctx *
+client__cycle(struct client_ctx *cc,
+ struct client_ctx *(*iter)(struct client_ctx *))
+{
+ struct client_ctx *save = cc;
+
+ do {
+ if (!((cc = (*iter)(cc))->flags & CLIENT_HIDDEN))
+ break;
+ } while (cc != save);
+
+ return cc != save ? cc : NULL;
+}
+
+void
+client_altrelease()
+{
+ struct client_ctx *cc = _curcc;
+ struct screen_ctx *sc;
+
+ if (cc == NULL)
+ return;
+ sc = CCTOSC(cc);
+
+ XUnmapWindow(G_dpy, sc->infowin);
+ XReparentWindow(G_dpy, sc->infowin, sc->rootwin, 0, 0);
+}
+
+void
+client_placecalc(struct client_ctx *cc)
+{
+ struct screen_ctx *sc = CCTOSC(cc);
+ int yslack, xslack;
+ int x, y, height, width, ymax, xmax, mousex, mousey;
+
+ y = cc->geom.y;
+ x = cc->geom.x;
+
+ height = cc->geom.height;
+ width = cc->geom.width;
+
+ ymax = DisplayHeight(G_dpy, sc->which) - cc->bwidth;
+ xmax = DisplayWidth(G_dpy, sc->which) - cc->bwidth;
+
+ yslack = ymax - cc->geom.height;
+ xslack = xmax - cc->geom.width;
+
+ xu_ptr_getpos(sc->rootwin, &mousex, &mousey);
+
+ mousex = MAX(mousex, cc->bwidth) - cc->geom.width/2;
+ mousey = MAX(mousey, cc->bwidth) - cc->geom.height/2;
+
+ mousex = MAX(mousex, (int)cc->bwidth);
+ mousey = MAX(mousey, (int)cc->bwidth);
+
+ if (cc->size->flags & USPosition) {
+ x = cc->size->x;
+ if (x <= 0 || x >= xmax)
+ x = cc->bwidth;
+ y = cc->size->y;
+ if (y <= 0 || y >= ymax)
+ y = cc->bwidth;
+ } else {
+ if (yslack < 0) {
+ y = cc->bwidth;
+ height = ymax;
+ } else {
+ if (y == 0 || y > yslack)
+ y = MIN(mousey, yslack);
+ height = cc->geom.height;
+ }
+
+ if (xslack < 0) {
+ x = cc->bwidth;
+ width = xmax;
+ } else {
+ if (x == 0 || x > xslack)
+ x = MIN(mousex, xslack);
+ width = cc->geom.width;
+ }
+ }
+
+ cc->geom.y = y;
+ cc->geom.x = x;
+
+ cc->geom.height = height;
+ cc->geom.width = width;
+}
+
+void
+client_vertmaximize(struct client_ctx *cc)
+{
+ if (cc->flags & CLIENT_MAXIMIZED) {
+ cc->flags &= ~CLIENT_MAXIMIZED;
+ cc->geom = cc->savegeom;
+ } else {
+ struct screen_ctx *sc = CCTOSC(cc);
+ int display_height = DisplayHeight(G_dpy, sc->which) -
+ cc->bwidth*2;
+
+ cc->savegeom = cc->geom;
+ cc->geom.y = cc->bwidth;
+ if (cc->geom.min_dx == 0)
+ cc->geom.height = display_height;
+ else
+ cc->geom.height = display_height -
+ (display_height % cc->geom.min_dx);
+ cc->flags |= CLIENT_MAXIMIZED;
+ }
+
+ client_resize(cc);
+}
+
+void
+client_map(struct client_ctx *cc)
+{
+ /* mtf? */
+ client_ptrwarp(cc);
+}
+
+void
+client_mtf(struct client_ctx *cc)
+{
+ struct screen_ctx *sc;
+
+ if (cc == NULL)
+ cc = _curcc;
+ if (cc == NULL)
+ return;
+
+ sc = CCTOSC(cc);
+
+ /* Move to front. */
+ TAILQ_REMOVE(&sc->mruq, cc, mru_entry);
+ TAILQ_INSERT_HEAD(&sc->mruq, cc, mru_entry);
+}
+
+void
+client_gethints(struct client_ctx *cc)
+{
+ XClassHint xch;
+ int argc;
+ char **argv;
+ Atom mha;
+ struct mwm_hints *mwmh;
+
+ if (XGetClassHint(G_dpy, cc->win, &xch)) {
+ if (xch.res_name != NULL)
+ cc->app_name = xch.res_name;
+ if (xch.res_class != NULL)
+ cc->app_class = xch.res_class;
+ }
+
+ mha = XInternAtom(G_dpy, "_MOTIF_WM_HINTS", False);
+ if (xu_getprop(cc, mha, mha, PROP_MWM_HINTS_ELEMENTS,
+ (u_char **)&mwmh) == MWM_NUMHINTS)
+ if (mwmh->flags & MWM_HINTS_DECORATIONS &&
+ !(mwmh->decorations & MWM_DECOR_ALL) &&
+ !(mwmh->decorations & MWM_DECOR_BORDER))
+ cc->bwidth = 0;
+ if (XGetCommand(G_dpy, cc->win, &argv, &argc)) {
+#define MAX_ARGLEN 512
+#define ARG_SEP_ " "
+ int len = MAX_ARGLEN;
+ int i, o;
+ char *buf;
+
+ buf = xmalloc(len);
+ buf[0] = '\0';
+
+ for (o = 0, i = 0; o < len && i < argc; i++) {
+ if (argv[i] == NULL)
+ break;
+ strlcat(buf, argv[i], len);
+ o += strlen(buf);
+ strlcat(buf, ARG_SEP_, len);
+ o += strlen(ARG_SEP_);
+ }
+
+ if (strlen(buf) > 0)
+ cc->app_cliarg = buf;
+
+ XFreeStringList(argv);
+ }
+}
+
+void
+client_freehints(struct client_ctx *cc)
+{
+ if (cc->app_name != NULL)
+ XFree(cc->app_name);
+ if (cc->app_class != NULL)
+ XFree(cc->app_class);
+ if (cc->app_cliarg != NULL)
+ xfree(cc->app_cliarg);
+}
+
+int
+_inwindowbounds(struct client_ctx *cc, int x, int y)
+{
+ return (x < cc->geom.width && x >= 0 &&
+ y < cc->geom.height && y >= 0);
+}