123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086 |
- /*
- * calmwm - the calm window manager
- *
- * Copyright (c) 2004 Marius Aamodt Eriksen <marius@monkey.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * $OpenBSD: client.c,v 1.198 2015/07/01 14:36:42 okan Exp $
- */
- #include <sys/types.h>
- #include <sys/queue.h>
- #include <err.h>
- #include <errno.h>
- #include <limits.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <unistd.h>
- #include "calmwm.h"
- static struct client_ctx *client_next(struct client_ctx *);
- static struct client_ctx *client_prev(struct client_ctx *);
- static void client_mtf(struct client_ctx *);
- static void client_none(struct screen_ctx *);
- static void client_placecalc(struct client_ctx *);
- static void client_wm_protocols(struct client_ctx *);
- static void client_mwm_hints(struct client_ctx *);
- static int client_inbound(struct client_ctx *, int, int);
- struct client_ctx *curcc = NULL;
- struct client_ctx *
- client_init(Window win, struct screen_ctx *sc)
- {
- struct client_ctx *cc;
- XWindowAttributes wattr;
- int mapped;
- if (win == None)
- return(NULL);
- if (!XGetWindowAttributes(X_Dpy, win, &wattr))
- return(NULL);
- if (sc == NULL) {
- if ((sc = screen_find(wattr.root)) == NULL)
- return(NULL);
- mapped = 1;
- } else {
- if (wattr.override_redirect || wattr.map_state != IsViewable)
- return(NULL);
- mapped = wattr.map_state != IsUnmapped;
- }
- cc = xcalloc(1, sizeof(*cc));
- XGrabServer(X_Dpy);
- cc->sc = sc;
- cc->win = win;
- TAILQ_INIT(&cc->nameq);
- client_setname(cc);
- conf_client(cc);
- XGetClassHint(X_Dpy, cc->win, &cc->ch);
- client_wm_hints(cc);
- client_wm_protocols(cc);
- client_getsizehints(cc);
- client_mwm_hints(cc);
- /* Saved pointer position */
- cc->ptr.x = -1;
- cc->ptr.y = -1;
- cc->geom.x = wattr.x;
- cc->geom.y = wattr.y;
- cc->geom.w = wattr.width;
- cc->geom.h = wattr.height;
- cc->colormap = wattr.colormap;
- if (wattr.map_state != IsViewable) {
- client_placecalc(cc);
- client_move(cc);
- if ((cc->wmh) && (cc->wmh->flags & StateHint))
- client_set_wm_state(cc, cc->wmh->initial_state);
- }
- XSelectInput(X_Dpy, cc->win, ColormapChangeMask | EnterWindowMask |
- PropertyChangeMask | KeyReleaseMask);
- XAddToSaveSet(X_Dpy, cc->win);
- client_transient(cc);
- /* Notify client of its configuration. */
- client_config(cc);
- TAILQ_INSERT_TAIL(&sc->clientq, cc, entry);
- xu_ewmh_net_client_list(sc);
- xu_ewmh_restore_net_wm_state(cc);
- if (client_get_wm_state(cc) == IconicState)
- client_hide(cc);
- else
- client_unhide(cc);
- if (mapped)
- group_autogroup(cc);
- XSync(X_Dpy, False);
- XUngrabServer(X_Dpy);
- return(cc);
- }
- struct client_ctx *
- client_find(Window win)
- {
- struct screen_ctx *sc;
- struct client_ctx *cc;
- TAILQ_FOREACH(sc, &Screenq, entry) {
- TAILQ_FOREACH(cc, &sc->clientq, entry) {
- if (cc->win == win)
- return(cc);
- }
- }
- return(NULL);
- }
- void
- client_delete(struct client_ctx *cc)
- {
- struct screen_ctx *sc = cc->sc;
- struct winname *wn;
- TAILQ_REMOVE(&sc->clientq, cc, entry);
- xu_ewmh_net_client_list(sc);
- if (cc->group != NULL)
- TAILQ_REMOVE(&cc->group->clientq, cc, group_entry);
- if (cc == client_current())
- client_none(sc);
- while ((wn = TAILQ_FIRST(&cc->nameq)) != NULL) {
- TAILQ_REMOVE(&cc->nameq, wn, entry);
- free(wn->name);
- free(wn);
- }
- if (cc->ch.res_class)
- XFree(cc->ch.res_class);
- if (cc->ch.res_name)
- XFree(cc->ch.res_name);
- if (cc->wmh)
- XFree(cc->wmh);
- free(cc);
- }
- void
- client_setactive(struct client_ctx *cc)
- {
- struct screen_ctx *sc = cc->sc;
- struct client_ctx *oldcc;
- if (cc->flags & CLIENT_HIDDEN)
- return;
- XInstallColormap(X_Dpy, cc->colormap);
- if ((cc->flags & CLIENT_INPUT) ||
- (!(cc->flags & CLIENT_WM_TAKE_FOCUS))) {
- XSetInputFocus(X_Dpy, cc->win,
- RevertToPointerRoot, CurrentTime);
- }
- if (cc->flags & CLIENT_WM_TAKE_FOCUS)
- client_msg(cc, cwmh[WM_TAKE_FOCUS], Last_Event_Time);
- if ((oldcc = client_current())) {
- oldcc->flags &= ~CLIENT_ACTIVE;
- client_draw_border(oldcc);
- }
- /* If we're in the middle of cycing, don't change the order. */
- if (!sc->cycling)
- client_mtf(cc);
- curcc = cc;
- cc->flags |= CLIENT_ACTIVE;
- cc->flags &= ~CLIENT_URGENCY;
- client_draw_border(cc);
- conf_grab_mouse(cc->win);
- xu_ewmh_net_active_window(sc, cc->win);
- }
- /*
- * set when there is no active client
- */
- static void
- client_none(struct screen_ctx *sc)
- {
- Window none = None;
- xu_ewmh_net_active_window(sc, none);
- curcc = NULL;
- }
- struct client_ctx *
- client_current(void)
- {
- return(curcc);
- }
- void
- client_toggle_freeze(struct client_ctx *cc)
- {
- if (cc->flags & CLIENT_FREEZE)
- cc->flags &= ~CLIENT_FREEZE;
- else
- cc->flags |= CLIENT_FREEZE;
- }
- void
- client_toggle_hidden(struct client_ctx *cc)
- {
- if (cc->flags & CLIENT_HIDDEN)
- cc->flags &= ~CLIENT_HIDDEN;
- else
- cc->flags |= CLIENT_HIDDEN;
- xu_ewmh_set_net_wm_state(cc);
- }
- void
- client_toggle_sticky(struct client_ctx *cc)
- {
- if (cc->flags & CLIENT_STICKY)
- cc->flags &= ~CLIENT_STICKY;
- else
- cc->flags |= CLIENT_STICKY;
- xu_ewmh_set_net_wm_state(cc);
- }
- void
- client_toggle_fullscreen(struct client_ctx *cc)
- {
- struct screen_ctx *sc = cc->sc;
- struct geom area;
- if ((cc->flags & CLIENT_FREEZE) &&
- !(cc->flags & CLIENT_FULLSCREEN))
- return;
- if (cc->flags & CLIENT_FULLSCREEN) {
- cc->bwidth = Conf.bwidth;
- cc->geom = cc->fullgeom;
- cc->flags &= ~(CLIENT_FULLSCREEN | CLIENT_FREEZE);
- goto resize;
- }
- cc->fullgeom = cc->geom;
- area = screen_area(sc,
- cc->geom.x + cc->geom.w / 2,
- cc->geom.y + cc->geom.h / 2, CWM_NOGAP);
- cc->bwidth = 0;
- cc->geom = area;
- cc->flags |= (CLIENT_FULLSCREEN | CLIENT_FREEZE);
- resize:
- client_resize(cc, 0);
- xu_ewmh_set_net_wm_state(cc);
- }
- void
- client_toggle_maximize(struct client_ctx *cc)
- {
- struct screen_ctx *sc = cc->sc;
- struct geom area;
- if (cc->flags & (CLIENT_FREEZE|CLIENT_STICKY))
- return;
- if ((cc->flags & CLIENT_MAXFLAGS) == CLIENT_MAXIMIZED) {
- cc->geom = cc->savegeom;
- cc->flags &= ~CLIENT_MAXIMIZED;
- goto resize;
- }
- if (!(cc->flags & CLIENT_VMAXIMIZED)) {
- cc->savegeom.h = cc->geom.h;
- cc->savegeom.y = cc->geom.y;
- }
- if (!(cc->flags & CLIENT_HMAXIMIZED)) {
- cc->savegeom.w = cc->geom.w;
- cc->savegeom.x = cc->geom.x;
- }
- /*
- * pick screen that the middle of the window is on.
- * that's probably more fair than if just the origin of
- * a window is poking over a boundary
- */
- area = screen_area(sc,
- cc->geom.x + cc->geom.w / 2,
- cc->geom.y + cc->geom.h / 2, CWM_GAP);
- cc->geom.x = area.x;
- cc->geom.y = area.y;
- cc->geom.w = area.w - (cc->bwidth * 2);
- cc->geom.h = area.h - (cc->bwidth * 2);
- cc->flags |= CLIENT_MAXIMIZED;
- resize:
- client_resize(cc, 0);
- xu_ewmh_set_net_wm_state(cc);
- }
- void
- client_toggle_vmaximize(struct client_ctx *cc)
- {
- struct screen_ctx *sc = cc->sc;
- struct geom area;
- if (cc->flags & (CLIENT_FREEZE|CLIENT_STICKY))
- return;
- if (cc->flags & CLIENT_VMAXIMIZED) {
- cc->geom.y = cc->savegeom.y;
- cc->geom.h = cc->savegeom.h;
- cc->flags &= ~CLIENT_VMAXIMIZED;
- goto resize;
- }
- cc->savegeom.y = cc->geom.y;
- cc->savegeom.h = cc->geom.h;
- area = screen_area(sc,
- cc->geom.x + cc->geom.w / 2,
- cc->geom.y + cc->geom.h / 2, CWM_GAP);
- cc->geom.y = area.y;
- cc->geom.h = area.h - (cc->bwidth * 2);
- cc->flags |= CLIENT_VMAXIMIZED;
- resize:
- client_resize(cc, 0);
- xu_ewmh_set_net_wm_state(cc);
- }
- void
- client_toggle_hmaximize(struct client_ctx *cc)
- {
- struct screen_ctx *sc = cc->sc;
- struct geom area;
- if (cc->flags & (CLIENT_FREEZE|CLIENT_STICKY))
- return;
- if (cc->flags & CLIENT_HMAXIMIZED) {
- cc->geom.x = cc->savegeom.x;
- cc->geom.w = cc->savegeom.w;
- cc->flags &= ~CLIENT_HMAXIMIZED;
- goto resize;
- }
- cc->savegeom.x = cc->geom.x;
- cc->savegeom.w = cc->geom.w;
- area = screen_area(sc,
- cc->geom.x + cc->geom.w / 2,
- cc->geom.y + cc->geom.h / 2, CWM_GAP);
- cc->geom.x = area.x;
- cc->geom.w = area.w - (cc->bwidth * 2);
- cc->flags |= CLIENT_HMAXIMIZED;
- resize:
- client_resize(cc, 0);
- xu_ewmh_set_net_wm_state(cc);
- }
- void
- client_resize(struct client_ctx *cc, int reset)
- {
- if (reset) {
- cc->flags &= ~CLIENT_MAXIMIZED;
- xu_ewmh_set_net_wm_state(cc);
- }
- client_draw_border(cc);
- XMoveResizeWindow(X_Dpy, cc->win, cc->geom.x,
- cc->geom.y, cc->geom.w, cc->geom.h);
- client_config(cc);
- }
- void
- client_move(struct client_ctx *cc)
- {
- XMoveWindow(X_Dpy, cc->win, cc->geom.x, cc->geom.y);
- client_config(cc);
- }
- void
- client_lower(struct client_ctx *cc)
- {
- XLowerWindow(X_Dpy, cc->win);
- }
- void
- client_raise(struct client_ctx *cc)
- {
- XRaiseWindow(X_Dpy, cc->win);
- }
- void
- client_config(struct client_ctx *cc)
- {
- XConfigureEvent cn;
- (void)memset(&cn, 0, sizeof(cn));
- cn.type = ConfigureNotify;
- cn.event = cc->win;
- cn.window = cc->win;
- cn.x = cc->geom.x;
- cn.y = cc->geom.y;
- cn.width = cc->geom.w;
- cn.height = cc->geom.h;
- cn.border_width = cc->bwidth;
- cn.above = None;
- cn.override_redirect = 0;
- XSendEvent(X_Dpy, cc->win, False, StructureNotifyMask, (XEvent *)&cn);
- }
- void
- client_ptrwarp(struct client_ctx *cc)
- {
- int x = cc->ptr.x, y = cc->ptr.y;
- if (x == -1 || y == -1) {
- x = cc->geom.w / 2;
- y = cc->geom.h / 2;
- }
- if (cc->flags & CLIENT_HIDDEN)
- client_unhide(cc);
- else
- client_raise(cc);
- xu_ptr_setpos(cc->win, x, y);
- }
- void
- client_ptrsave(struct client_ctx *cc)
- {
- int x, y;
- xu_ptr_getpos(cc->win, &x, &y);
- if (client_inbound(cc, x, y)) {
- cc->ptr.x = x;
- cc->ptr.y = y;
- } else {
- cc->ptr.x = -1;
- cc->ptr.y = -1;
- }
- }
- void
- client_hide(struct client_ctx *cc)
- {
- if (cc->flags & CLIENT_STICKY)
- return;
- XUnmapWindow(X_Dpy, cc->win);
- cc->flags &= ~CLIENT_ACTIVE;
- cc->flags |= CLIENT_HIDDEN;
- client_set_wm_state(cc, IconicState);
- if (cc == client_current())
- client_none(cc->sc);
- }
- void
- client_unhide(struct client_ctx *cc)
- {
- if (cc->flags & CLIENT_STICKY)
- return;
- XMapRaised(X_Dpy, cc->win);
- cc->flags &= ~CLIENT_HIDDEN;
- client_set_wm_state(cc, NormalState);
- client_draw_border(cc);
- }
- void
- client_urgency(struct client_ctx *cc)
- {
- if (!(cc->flags & CLIENT_ACTIVE))
- cc->flags |= CLIENT_URGENCY;
- }
- void
- client_draw_border(struct client_ctx *cc)
- {
- struct screen_ctx *sc = cc->sc;
- unsigned long pixel;
- if (cc->flags & CLIENT_ACTIVE)
- switch (cc->flags & CLIENT_HIGHLIGHT) {
- case CLIENT_GROUP:
- pixel = sc->xftcolor[CWM_COLOR_BORDER_GROUP].pixel;
- break;
- case CLIENT_UNGROUP:
- pixel = sc->xftcolor[CWM_COLOR_BORDER_UNGROUP].pixel;
- break;
- default:
- pixel = sc->xftcolor[CWM_COLOR_BORDER_ACTIVE].pixel;
- break;
- }
- else
- pixel = sc->xftcolor[CWM_COLOR_BORDER_INACTIVE].pixel;
- if (cc->flags & CLIENT_URGENCY)
- pixel = sc->xftcolor[CWM_COLOR_BORDER_URGENCY].pixel;
- XSetWindowBorderWidth(X_Dpy, cc->win, cc->bwidth);
- XSetWindowBorder(X_Dpy, cc->win, pixel);
- }
- static void
- client_wm_protocols(struct client_ctx *cc)
- {
- Atom *p;
- int i, j;
- if (XGetWMProtocols(X_Dpy, cc->win, &p, &j)) {
- for (i = 0; i < j; i++) {
- if (p[i] == cwmh[WM_DELETE_WINDOW])
- cc->flags |= CLIENT_WM_DELETE_WINDOW;
- else if (p[i] == cwmh[WM_TAKE_FOCUS])
- cc->flags |= CLIENT_WM_TAKE_FOCUS;
- }
- XFree(p);
- }
- }
- void
- client_wm_hints(struct client_ctx *cc)
- {
- if ((cc->wmh = XGetWMHints(X_Dpy, cc->win)) == NULL)
- return;
- if ((cc->wmh->flags & InputHint) && (cc->wmh->input))
- cc->flags |= CLIENT_INPUT;
- if ((cc->wmh->flags & XUrgencyHint))
- client_urgency(cc);
- }
- void
- client_msg(struct client_ctx *cc, Atom proto, Time ts)
- {
- XClientMessageEvent cm;
- (void)memset(&cm, 0, sizeof(cm));
- cm.type = ClientMessage;
- cm.window = cc->win;
- cm.message_type = cwmh[WM_PROTOCOLS];
- cm.format = 32;
- cm.data.l[0] = proto;
- cm.data.l[1] = ts;
- XSendEvent(X_Dpy, cc->win, False, NoEventMask, (XEvent *)&cm);
- }
- void
- client_send_delete(struct client_ctx *cc)
- {
- if (cc->flags & CLIENT_WM_DELETE_WINDOW)
- client_msg(cc, cwmh[WM_DELETE_WINDOW], CurrentTime);
- else
- XKillClient(X_Dpy, cc->win);
- }
- void
- client_setname(struct client_ctx *cc)
- {
- struct winname *wn;
- char *newname;
- if (!xu_getstrprop(cc->win, ewmh[_NET_WM_NAME], &newname))
- if (!xu_getstrprop(cc->win, XA_WM_NAME, &newname))
- newname = xstrdup("");
- 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;
- }
- }
- wn = xmalloc(sizeof(*wn));
- 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) {
- if ((wn = TAILQ_FIRST(&cc->nameq)) == NULL)
- errx(1, "client_setname: window name queue empty");
- TAILQ_REMOVE(&cc->nameq, wn, entry);
- free(wn->name);
- free(wn);
- cc->nameqlen--;
- }
- }
- void
- client_cycle(struct screen_ctx *sc, int flags)
- {
- struct client_ctx *oldcc, *newcc;
- int again = 1;
- if (TAILQ_EMPTY(&sc->clientq))
- return;
- oldcc = client_current();
- if (oldcc == NULL)
- oldcc = ((flags & CWM_RCYCLE) ?
- TAILQ_LAST(&sc->clientq, client_ctx_q) :
- TAILQ_FIRST(&sc->clientq));
- newcc = oldcc;
- while (again) {
- again = 0;
- newcc = ((flags & CWM_RCYCLE) ? client_prev(newcc) :
- client_next(newcc));
- /* Only cycle visible and non-ignored windows. */
- if ((newcc->flags & (CLIENT_HIDDEN|CLIENT_IGNORE))
- || ((flags & CWM_INGROUP) &&
- (newcc->group != oldcc->group)))
- again = 1;
- /* Is oldcc the only non-hidden window? */
- if (newcc == oldcc) {
- if (again)
- return; /* No windows visible. */
- break;
- }
- }
- /* reset when cycling mod is released. XXX I hate this hack */
- sc->cycling = 1;
- client_ptrsave(oldcc);
- client_ptrwarp(newcc);
- }
- void
- client_cycle_leave(struct screen_ctx *sc)
- {
- struct client_ctx *cc;
- sc->cycling = 0;
- if ((cc = client_current())) {
- client_mtf(cc);
- group_toggle_membership_leave(cc);
- XUngrabKeyboard(X_Dpy, CurrentTime);
- }
- }
- static struct client_ctx *
- client_next(struct client_ctx *cc)
- {
- struct screen_ctx *sc = cc->sc;
- struct client_ctx *ccc;
- return(((ccc = TAILQ_NEXT(cc, entry)) != NULL) ?
- ccc : TAILQ_FIRST(&sc->clientq));
- }
- static struct client_ctx *
- client_prev(struct client_ctx *cc)
- {
- struct screen_ctx *sc = cc->sc;
- struct client_ctx *ccc;
- return(((ccc = TAILQ_PREV(cc, client_ctx_q, entry)) != NULL) ?
- ccc : TAILQ_LAST(&sc->clientq, client_ctx_q));
- }
- static void
- client_placecalc(struct client_ctx *cc)
- {
- struct screen_ctx *sc = cc->sc;
- int xslack, yslack;
- if (cc->hint.flags & (USPosition|PPosition)) {
- /*
- * Ignore XINERAMA screens, just make sure it's somewhere
- * in the virtual desktop. else it stops people putting xterms
- * at startup in the screen the mouse doesn't start in *sigh*.
- * XRandR bits mean that {x,y}max shouldn't be outside what's
- * currently there.
- */
- xslack = sc->view.w - cc->geom.w - cc->bwidth * 2;
- yslack = sc->view.h - cc->geom.h - cc->bwidth * 2;
- cc->geom.x = MIN(cc->geom.x, xslack);
- cc->geom.y = MIN(cc->geom.y, yslack);
- } else {
- struct geom area;
- int xmouse, ymouse;
- xu_ptr_getpos(sc->rootwin, &xmouse, &ymouse);
- area = screen_area(sc, xmouse, ymouse, CWM_GAP);
- area.w += area.x;
- area.h += area.y;
- xmouse = MAX(xmouse, area.x) - cc->geom.w / 2;
- ymouse = MAX(ymouse, area.y) - cc->geom.h / 2;
- xmouse = MAX(xmouse, area.x);
- ymouse = MAX(ymouse, area.y);
- xslack = area.w - cc->geom.w - cc->bwidth * 2;
- yslack = area.h - cc->geom.h - cc->bwidth * 2;
- if (xslack >= area.x) {
- cc->geom.x = MAX(MIN(xmouse, xslack), area.x);
- } else {
- cc->geom.x = area.x;
- cc->geom.w = area.w;
- }
- if (yslack >= area.y) {
- cc->geom.y = MAX(MIN(ymouse, yslack), area.y);
- } else {
- cc->geom.y = area.y;
- cc->geom.h = area.h;
- }
- }
- }
- static void
- client_mtf(struct client_ctx *cc)
- {
- struct screen_ctx *sc = cc->sc;
- TAILQ_REMOVE(&sc->clientq, cc, entry);
- TAILQ_INSERT_HEAD(&sc->clientq, cc, entry);
- }
- void
- client_getsizehints(struct client_ctx *cc)
- {
- long tmp;
- XSizeHints size;
- if (!XGetWMNormalHints(X_Dpy, cc->win, &size, &tmp))
- size.flags = 0;
- cc->hint.flags = size.flags;
- if (size.flags & PBaseSize) {
- cc->hint.basew = size.base_width;
- cc->hint.baseh = size.base_height;
- } else if (size.flags & PMinSize) {
- cc->hint.basew = size.min_width;
- cc->hint.baseh = size.min_height;
- }
- if (size.flags & PMinSize) {
- cc->hint.minw = size.min_width;
- cc->hint.minh = size.min_height;
- } else if (size.flags & PBaseSize) {
- cc->hint.minw = size.base_width;
- cc->hint.minh = size.base_height;
- }
- if (size.flags & PMaxSize) {
- cc->hint.maxw = size.max_width;
- cc->hint.maxh = size.max_height;
- }
- if (size.flags & PResizeInc) {
- cc->hint.incw = size.width_inc;
- cc->hint.inch = size.height_inc;
- }
- cc->hint.incw = MAX(1, cc->hint.incw);
- cc->hint.inch = MAX(1, cc->hint.inch);
- if (size.flags & PAspect) {
- if (size.min_aspect.x > 0)
- cc->hint.mina = (float)size.min_aspect.y /
- size.min_aspect.x;
- if (size.max_aspect.y > 0)
- cc->hint.maxa = (float)size.max_aspect.x /
- size.max_aspect.y;
- }
- }
- void
- client_applysizehints(struct client_ctx *cc)
- {
- Bool baseismin;
- baseismin = (cc->hint.basew == cc->hint.minw) &&
- (cc->hint.baseh == cc->hint.minh);
- /* temporarily remove base dimensions, ICCCM 4.1.2.3 */
- if (!baseismin) {
- cc->geom.w -= cc->hint.basew;
- cc->geom.h -= cc->hint.baseh;
- }
- /* adjust for aspect limits */
- if (cc->hint.mina && cc->hint.maxa) {
- if (cc->hint.maxa < (float)cc->geom.w / cc->geom.h)
- cc->geom.w = cc->geom.h * cc->hint.maxa;
- else if (cc->hint.mina < (float)cc->geom.h / cc->geom.w)
- cc->geom.h = cc->geom.w * cc->hint.mina;
- }
- /* remove base dimensions for increment */
- if (baseismin) {
- cc->geom.w -= cc->hint.basew;
- cc->geom.h -= cc->hint.baseh;
- }
- /* adjust for increment value */
- cc->geom.w -= cc->geom.w % cc->hint.incw;
- cc->geom.h -= cc->geom.h % cc->hint.inch;
- /* restore base dimensions */
- cc->geom.w += cc->hint.basew;
- cc->geom.h += cc->hint.baseh;
- /* adjust for min width/height */
- cc->geom.w = MAX(cc->geom.w, cc->hint.minw);
- cc->geom.h = MAX(cc->geom.h, cc->hint.minh);
- /* adjust for max width/height */
- if (cc->hint.maxw)
- cc->geom.w = MIN(cc->geom.w, cc->hint.maxw);
- if (cc->hint.maxh)
- cc->geom.h = MIN(cc->geom.h, cc->hint.maxh);
- cc->geom.w = MAX(cc->geom.w, 1);
- cc->geom.h = MAX(cc->geom.h, 1);
- cc->dim.w = (cc->geom.w - cc->hint.basew) / cc->hint.incw;
- cc->dim.h = (cc->geom.h - cc->hint.baseh) / cc->hint.inch;
- }
- static void
- client_mwm_hints(struct client_ctx *cc)
- {
- struct mwm_hints *mwmh;
- if (xu_getprop(cc->win, cwmh[_MOTIF_WM_HINTS], cwmh[_MOTIF_WM_HINTS],
- MWM_HINTS_ELEMENTS, (unsigned char **)&mwmh) == MWM_HINTS_ELEMENTS) {
- if (mwmh->flags & MWM_FLAGS_DECORATIONS &&
- !(mwmh->decorations & MWM_DECOR_ALL) &&
- !(mwmh->decorations & MWM_DECOR_BORDER))
- cc->bwidth = 0;
- XFree(mwmh);
- }
- }
- void
- client_transient(struct client_ctx *cc)
- {
- struct client_ctx *tc;
- Window trans;
- if (XGetTransientForHint(X_Dpy, cc->win, &trans)) {
- if ((tc = client_find(trans)) && tc->group) {
- group_movetogroup(cc, tc->group->num);
- if (tc->flags & CLIENT_IGNORE)
- cc->flags |= CLIENT_IGNORE;
- }
- }
- }
- static int
- client_inbound(struct client_ctx *cc, int x, int y)
- {
- return(x < cc->geom.w && x >= 0 &&
- y < cc->geom.h && y >= 0);
- }
- int
- client_snapcalc(int n0, int n1, int e0, int e1, int snapdist)
- {
- int s0, s1;
- s0 = s1 = 0;
- if (abs(e0 - n0) <= snapdist)
- s0 = e0 - n0;
- if (abs(e1 - n1) <= snapdist)
- s1 = e1 - n1;
- /* possible to snap in both directions */
- if (s0 != 0 && s1 != 0)
- if (abs(s0) < abs(s1))
- return(s0);
- else
- return(s1);
- else if (s0 != 0)
- return(s0);
- else if (s1 != 0)
- return(s1);
- else
- return(0);
- }
- void
- client_htile(struct client_ctx *cc)
- {
- struct client_ctx *ci;
- struct group_ctx *gc = cc->group;
- struct screen_ctx *sc = cc->sc;
- struct geom area;
- int i, n, mh, x, h, w;
- if (!gc)
- return;
- i = n = 0;
- TAILQ_FOREACH(ci, &gc->clientq, group_entry) {
- if (ci->flags & CLIENT_HIDDEN ||
- ci->flags & CLIENT_IGNORE || (ci == cc))
- continue;
- n++;
- }
- if (n == 0)
- return;
- area = screen_area(sc,
- cc->geom.x + cc->geom.w / 2,
- cc->geom.y + cc->geom.h / 2, CWM_GAP);
- if (cc->flags & CLIENT_VMAXIMIZED ||
- cc->geom.h + (cc->bwidth * 2) >= area.h)
- return;
- cc->flags &= ~CLIENT_HMAXIMIZED;
- cc->geom.x = area.x;
- cc->geom.y = area.y;
- cc->geom.w = area.w - (cc->bwidth * 2);
- client_resize(cc, 1);
- client_ptrwarp(cc);
- mh = cc->geom.h + (cc->bwidth * 2);
- x = area.x;
- w = area.w / n;
- h = area.h - mh;
- TAILQ_FOREACH(ci, &gc->clientq, group_entry) {
- if (ci->flags & CLIENT_HIDDEN ||
- ci->flags & CLIENT_IGNORE || (ci == cc))
- continue;
- ci->bwidth = Conf.bwidth;
- ci->geom.y = area.y + mh;
- ci->geom.x = x;
- ci->geom.h = h - (ci->bwidth * 2);
- ci->geom.w = w - (ci->bwidth * 2);
- if (i + 1 == n)
- ci->geom.w = area.x + area.w -
- ci->geom.x - (ci->bwidth * 2);
- x += w;
- client_resize(ci, 1);
- i++;
- }
- }
- void
- client_vtile(struct client_ctx *cc)
- {
- struct client_ctx *ci;
- struct group_ctx *gc = cc->group;
- struct screen_ctx *sc = cc->sc;
- struct geom area;
- int i, n, mw, y, h, w;
- if (!gc)
- return;
- i = n = 0;
- TAILQ_FOREACH(ci, &gc->clientq, group_entry) {
- if (ci->flags & CLIENT_HIDDEN ||
- ci->flags & CLIENT_IGNORE || (ci == cc))
- continue;
- n++;
- }
- if (n == 0)
- return;
- area = screen_area(sc,
- cc->geom.x + cc->geom.w / 2,
- cc->geom.y + cc->geom.h / 2, CWM_GAP);
- if (cc->flags & CLIENT_HMAXIMIZED ||
- cc->geom.w + (cc->bwidth * 2) >= area.w)
- return;
- cc->flags &= ~CLIENT_VMAXIMIZED;
- cc->geom.x = area.x;
- cc->geom.y = area.y;
- cc->geom.h = area.h - (cc->bwidth * 2);
- client_resize(cc, 1);
- client_ptrwarp(cc);
- mw = cc->geom.w + (cc->bwidth * 2);
- y = area.y;
- h = area.h / n;
- w = area.w - mw;
- TAILQ_FOREACH(ci, &gc->clientq, group_entry) {
- if (ci->flags & CLIENT_HIDDEN ||
- ci->flags & CLIENT_IGNORE || (ci == cc))
- continue;
- ci->bwidth = Conf.bwidth;
- ci->geom.y = y;
- ci->geom.x = area.x + mw;
- ci->geom.h = h - (ci->bwidth * 2);
- ci->geom.w = w - (ci->bwidth * 2);
- if (i + 1 == n)
- ci->geom.h = area.y + area.h -
- ci->geom.y - (ci->bwidth * 2);
- y += h;
- client_resize(ci, 1);
- i++;
- }
- }
- long
- client_get_wm_state(struct client_ctx *cc)
- {
- long *p, state = -1;
- if (xu_getprop(cc->win, cwmh[WM_STATE], cwmh[WM_STATE], 2L,
- (unsigned char **)&p) > 0) {
- state = *p;
- XFree(p);
- }
- return(state);
- }
- void
- client_set_wm_state(struct client_ctx *cc, long state)
- {
- long data[] = { state, None };
- XChangeProperty(X_Dpy, cc->win, cwmh[WM_STATE], cwmh[WM_STATE], 32,
- PropModeReplace, (unsigned char *)data, 2);
- }
|