diff client.c @ 0:7024076fa948

initial add
author paulo@localhost
date Sun, 22 Mar 2009 23:26:35 -0700
parents
children a54de16f8277
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/client.c	Sun Mar 22 23:26:35 2009 -0700
     1.3 @@ -0,0 +1,517 @@
     1.4 +/* (C)opyright MMVI-MMVII Anselm R. Garbe <garbeam at gmail dot com>
     1.5 + * See LICENSE file for license details.
     1.6 + */
     1.7 +#include "dwm.h"
     1.8 +#include <stdio.h>
     1.9 +#include <stdlib.h>
    1.10 +#include <string.h>
    1.11 +#include <X11/Xatom.h>
    1.12 +#include <X11/Xutil.h>
    1.13 +
    1.14 +/* static */
    1.15 +
    1.16 +static void
    1.17 +attachstack(Client *c) {
    1.18 +	c->snext = stack;
    1.19 +	stack = c;
    1.20 +}
    1.21 +
    1.22 +static void
    1.23 +detachstack(Client *c) {
    1.24 +	Client **tc;
    1.25 +
    1.26 +	for(tc=&stack; *tc && *tc != c; tc=&(*tc)->snext);
    1.27 +	*tc = c->snext;
    1.28 +}
    1.29 +
    1.30 +static void
    1.31 +grabbuttons(Client *c, Bool focused) {
    1.32 +	XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
    1.33 +
    1.34 +	if(focused) {
    1.35 +		XGrabButton(dpy, Button1, MODKEY, c->win, False, BUTTONMASK,
    1.36 +				GrabModeAsync, GrabModeSync, None, None);
    1.37 +		XGrabButton(dpy, Button1, MODKEY | LockMask, c->win, False, BUTTONMASK,
    1.38 +				GrabModeAsync, GrabModeSync, None, None);
    1.39 +		XGrabButton(dpy, Button1, MODKEY | numlockmask, c->win, False, BUTTONMASK,
    1.40 +				GrabModeAsync, GrabModeSync, None, None);
    1.41 +		XGrabButton(dpy, Button1, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK,
    1.42 +				GrabModeAsync, GrabModeSync, None, None);
    1.43 +
    1.44 +		XGrabButton(dpy, Button2, MODKEY, c->win, False, BUTTONMASK,
    1.45 +				GrabModeAsync, GrabModeSync, None, None);
    1.46 +		XGrabButton(dpy, Button2, MODKEY | LockMask, c->win, False, BUTTONMASK,
    1.47 +				GrabModeAsync, GrabModeSync, None, None);
    1.48 +		XGrabButton(dpy, Button2, MODKEY | numlockmask, c->win, False, BUTTONMASK,
    1.49 +				GrabModeAsync, GrabModeSync, None, None);
    1.50 +		XGrabButton(dpy, Button2, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK,
    1.51 +				GrabModeAsync, GrabModeSync, None, None);
    1.52 +
    1.53 +		XGrabButton(dpy, Button3, MODKEY, c->win, False, BUTTONMASK,
    1.54 +				GrabModeAsync, GrabModeSync, None, None);
    1.55 +		XGrabButton(dpy, Button3, MODKEY | LockMask, c->win, False, BUTTONMASK,
    1.56 +				GrabModeAsync, GrabModeSync, None, None);
    1.57 +		XGrabButton(dpy, Button3, MODKEY | numlockmask, c->win, False, BUTTONMASK,
    1.58 +				GrabModeAsync, GrabModeSync, None, None);
    1.59 +		XGrabButton(dpy, Button3, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK,
    1.60 +				GrabModeAsync, GrabModeSync, None, None);
    1.61 +	}
    1.62 +	else
    1.63 +		XGrabButton(dpy, AnyButton, AnyModifier, c->win, False, BUTTONMASK,
    1.64 +				GrabModeAsync, GrabModeSync, None, None);
    1.65 +}
    1.66 +
    1.67 +static Bool
    1.68 +isprotodel(Client *c) {
    1.69 +	int i, n;
    1.70 +	Atom *protocols;
    1.71 +	Bool ret = False;
    1.72 +
    1.73 +	if(XGetWMProtocols(dpy, c->win, &protocols, &n)) {
    1.74 +		for(i = 0; !ret && i < n; i++)
    1.75 +			if(protocols[i] == wmatom[WMDelete])
    1.76 +				ret = True;
    1.77 +		XFree(protocols);
    1.78 +	}
    1.79 +	return ret;
    1.80 +}
    1.81 +
    1.82 +static void
    1.83 +setclientstate(Client *c, long state) {
    1.84 +	long data[] = {state, None};
    1.85 +
    1.86 +	XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32,
    1.87 +			PropModeReplace, (unsigned char *)data, 2);
    1.88 +}
    1.89 +
    1.90 +static void
    1.91 +togglemax(Client *c) {
    1.92 +	XEvent ev;
    1.93 +
    1.94 +	if(c->isfixed)
    1.95 +		return;
    1.96 +	if((c->ismax = !c->ismax)) {
    1.97 +		c->rx = c->x;
    1.98 +		c->ry = c->y;
    1.99 +		c->rw = c->w;
   1.100 +		c->rh = c->h;
   1.101 +		resize(c, wax, way, waw - 2 * BORDERPX, wah - 2 * BORDERPX, True);
   1.102 +	}
   1.103 +	else
   1.104 +		resize(c, c->rx, c->ry, c->rw, c->rh, True);
   1.105 +	while(XCheckMaskEvent(dpy, EnterWindowMask, &ev));
   1.106 +}
   1.107 +
   1.108 +static int
   1.109 +xerrordummy(Display *dsply, XErrorEvent *ee) {
   1.110 +	return 0;
   1.111 +}
   1.112 +
   1.113 +/* extern */
   1.114 +
   1.115 +void
   1.116 +attach(Client *c) {
   1.117 +	if(clients)
   1.118 +		clients->prev = c;
   1.119 +	c->next = clients;
   1.120 +	clients = c;
   1.121 +}
   1.122 +
   1.123 +void
   1.124 +configure(Client *c) {
   1.125 +	XConfigureEvent ce;
   1.126 +
   1.127 +	ce.type = ConfigureNotify;
   1.128 +	ce.display = dpy;
   1.129 +	ce.event = c->win;
   1.130 +	ce.window = c->win;
   1.131 +	ce.x = c->x;
   1.132 +	ce.y = c->y;
   1.133 +	ce.width = c->w;
   1.134 +	ce.height = c->h;
   1.135 +	ce.border_width = c->border;
   1.136 +	ce.above = None;
   1.137 +	ce.override_redirect = False;
   1.138 +	XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce);
   1.139 +}
   1.140 +
   1.141 +void
   1.142 +detach(Client *c) {
   1.143 +	if(c->prev)
   1.144 +		c->prev->next = c->next;
   1.145 +	if(c->next)
   1.146 +		c->next->prev = c->prev;
   1.147 +	if(c == clients)
   1.148 +		clients = c->next;
   1.149 +	c->next = c->prev = NULL;
   1.150 +}
   1.151 +
   1.152 +void
   1.153 +focus(Client *c) {
   1.154 +	if(c && !isvisible(c))
   1.155 +		return;
   1.156 +	if(sel && sel != c) {
   1.157 +		grabbuttons(sel, False);
   1.158 +		XSetWindowBorder(dpy, sel->win, dc.norm[ColBorder]);
   1.159 +	}
   1.160 +	if(c) {
   1.161 +		detachstack(c);
   1.162 +		attachstack(c);
   1.163 +		grabbuttons(c, True);
   1.164 +	}
   1.165 +	sel = c;
   1.166 +	drawstatus();
   1.167 +	if(!selscreen)
   1.168 +		return;
   1.169 +	if(c) {
   1.170 +		XSetWindowBorder(dpy, c->win, dc.sel[ColBorder]);
   1.171 +		XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime);
   1.172 +	}
   1.173 +	else
   1.174 +		XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
   1.175 +}
   1.176 +
   1.177 +void
   1.178 +killclient(Arg *arg) {
   1.179 +	if(!sel)
   1.180 +		return;
   1.181 +	if(isprotodel(sel))
   1.182 +		sendevent(sel->win, wmatom[WMProtocols], wmatom[WMDelete]);
   1.183 +	else
   1.184 +		XKillClient(dpy, sel->win);
   1.185 +}
   1.186 +
   1.187 +void
   1.188 +manage(Window w, XWindowAttributes *wa) {
   1.189 +	Client *c, *t;
   1.190 +	Window trans;
   1.191 +	XWindowChanges wc;
   1.192 +
   1.193 +	c = emallocz(sizeof(Client));
   1.194 +	c->tags = emallocz(ntags * sizeof(Bool));
   1.195 +	c->win = w;
   1.196 +	c->x = wa->x;
   1.197 +	c->y = wa->y;
   1.198 +	c->w = wa->width;
   1.199 +	c->h = wa->height;
   1.200 +	if(c->w == sw && c->h == sh) {
   1.201 +		c->border = 0;
   1.202 +		c->x = sx;
   1.203 +		c->y = sy;
   1.204 +	}
   1.205 +	else { 
   1.206 +		c->border = BORDERPX;
   1.207 +		if(c->x > wax + waw && c-> w < waw)
   1.208 +			c->x = wax + waw - c->w - 2 * c->border;
   1.209 +		if(c->y > way + wah && c-> h < wah)
   1.210 +			c->y = way + wah - c->h - 2 * c->border;
   1.211 +		if(c->x < wax && c->w < waw)
   1.212 +			c->x = wax;
   1.213 +		if(c->y < way && c->h < wah)
   1.214 +			c->y = way;
   1.215 +	}
   1.216 +	XSelectInput(dpy, w,
   1.217 +		StructureNotifyMask | PropertyChangeMask | EnterWindowMask);
   1.218 +	XGetTransientForHint(dpy, w, &trans);
   1.219 +	grabbuttons(c, False);
   1.220 +	wc.border_width = c->border;
   1.221 +	XConfigureWindow(dpy, w, CWBorderWidth, &wc);
   1.222 +	XSetWindowBorder(dpy, w, dc.norm[ColBorder]);
   1.223 +	configure(c); /* propagates border_width, if size doesn't change */
   1.224 +	updatetitle(c);
   1.225 +	for(t = clients; t && t->win != trans; t = t->next);
   1.226 +	settags(c, t);
   1.227 +	if(!c->isversatile)
   1.228 +		c->isversatile = (t != NULL) || c->isfixed;
   1.229 +	attach(c);
   1.230 +	attachstack(c);
   1.231 +	c->isbanned = True;
   1.232 +	XMoveWindow(dpy, w, c->x + 2 * sw, c->y);
   1.233 +	XMapWindow(dpy, w);
   1.234 +	setclientstate(c, NormalState);
   1.235 +	if(isvisible(c))
   1.236 +		focus(c);
   1.237 +	updatesizehints(c);
   1.238 +	lt->arrange();
   1.239 +}
   1.240 +
   1.241 +void
   1.242 +resize(Client *c, int x, int y, int w, int h, Bool sizehints) {
   1.243 +	float actual, dx, dy, max, min;
   1.244 +	XWindowChanges wc;
   1.245 +
   1.246 +	if(w <= 0 || h <= 0)
   1.247 +		return;
   1.248 +	if(sizehints) {
   1.249 +		if(c->minw && w < c->minw)
   1.250 +			w = c->minw;
   1.251 +		if(c->minh && h < c->minh)
   1.252 +			h = c->minh;
   1.253 +		if(c->maxw && w > c->maxw)
   1.254 +			w = c->maxw;
   1.255 +		if(c->maxh && h > c->maxh)
   1.256 +			h = c->maxh;
   1.257 +		/* inspired by algorithm from fluxbox */
   1.258 +		if(c->minay > 0 && c->maxay && (h - c->baseh) > 0) {
   1.259 +			dx = (float)(w - c->basew);
   1.260 +			dy = (float)(h - c->baseh);
   1.261 +			min = (float)(c->minax) / (float)(c->minay);
   1.262 +			max = (float)(c->maxax) / (float)(c->maxay);
   1.263 +			actual = dx / dy;
   1.264 +			if(max > 0 && min > 0 && actual > 0) {
   1.265 +				if(actual < min) {
   1.266 +					dy = (dx * min + dy) / (min * min + 1);
   1.267 +					dx = dy * min;
   1.268 +					w = (int)dx + c->basew;
   1.269 +					h = (int)dy + c->baseh;
   1.270 +				}
   1.271 +				else if(actual > max) {
   1.272 +					dy = (dx * min + dy) / (max * max + 1);
   1.273 +					dx = dy * min;
   1.274 +					w = (int)dx + c->basew;
   1.275 +					h = (int)dy + c->baseh;
   1.276 +				}
   1.277 +			}
   1.278 +		}
   1.279 +		if(c->incw)
   1.280 +			w -= (w - c->basew) % c->incw;
   1.281 +		if(c->inch)
   1.282 +			h -= (h - c->baseh) % c->inch;
   1.283 +	}
   1.284 +	if(w == sw && h == sh)
   1.285 +		c->border = 0; 
   1.286 +	else
   1.287 +		c->border = BORDERPX;
   1.288 +	/* offscreen appearance fixes */
   1.289 +	if(x > sw)
   1.290 +		x = sw - w - 2 * c->border;
   1.291 +	if(y > sh)
   1.292 +		y = sh - h - 2 * c->border;
   1.293 +	if(x + w + 2 * c->border < sx)
   1.294 +		x = sx;
   1.295 +	if(y + h + 2 * c->border < sy)
   1.296 +		y = sy;
   1.297 +	/* if(c->x != x || c->y != y || c->w != w || c->h != h) { */
   1.298 +		c->x = wc.x = x;
   1.299 +		c->y = wc.y = y;
   1.300 +		c->w = wc.width = w;
   1.301 +		c->h = wc.height = h;
   1.302 +		wc.border_width = c->border;
   1.303 +		XConfigureWindow(dpy, c->win, CWX | CWY | CWWidth | CWHeight | CWBorderWidth, &wc);
   1.304 +		configure(c);
   1.305 +		XSync(dpy, False);
   1.306 +	/* } */
   1.307 +}
   1.308 +
   1.309 +void
   1.310 +toggleversatile(Arg *arg) {
   1.311 +	if(!sel || lt->arrange == versatile)
   1.312 +		return;
   1.313 +	sel->isversatile = !sel->isversatile;
   1.314 +	lt->arrange();
   1.315 +}
   1.316 +
   1.317 +void
   1.318 +updatesizehints(Client *c) {
   1.319 +	long msize;
   1.320 +	XSizeHints size;
   1.321 +
   1.322 +	if(!XGetWMNormalHints(dpy, c->win, &size, &msize) || !size.flags)
   1.323 +		size.flags = PSize;
   1.324 +	c->flags = size.flags;
   1.325 +	if(c->flags & PBaseSize) {
   1.326 +		c->basew = size.base_width;
   1.327 +		c->baseh = size.base_height;
   1.328 +	}
   1.329 +	else
   1.330 +		c->basew = c->baseh = 0;
   1.331 +	if(c->flags & PResizeInc) {
   1.332 +		c->incw = size.width_inc;
   1.333 +		c->inch = size.height_inc;
   1.334 +	}
   1.335 +	else
   1.336 +		c->incw = c->inch = 0;
   1.337 +	if(c->flags & PMaxSize) {
   1.338 +		c->maxw = size.max_width;
   1.339 +		c->maxh = size.max_height;
   1.340 +	}
   1.341 +	else
   1.342 +		c->maxw = c->maxh = 0;
   1.343 +	if(c->flags & PMinSize) {
   1.344 +		c->minw = size.min_width;
   1.345 +		c->minh = size.min_height;
   1.346 +	}
   1.347 +	else
   1.348 +		c->minw = c->minh = 0;
   1.349 +	if(c->flags & PAspect) {
   1.350 +		c->minax = size.min_aspect.x;
   1.351 +		c->minay = size.min_aspect.y;
   1.352 +		c->maxax = size.max_aspect.x;
   1.353 +		c->maxay = size.max_aspect.y;
   1.354 +	}
   1.355 +	else
   1.356 +		c->minax = c->minay = c->maxax = c->maxay = 0;
   1.357 +	c->isfixed = (c->maxw && c->minw && c->maxh && c->minh
   1.358 +			&& c->maxw == c->minw && c->maxh == c->minh);
   1.359 +}
   1.360 +
   1.361 +void
   1.362 +updatetitle(Client *c) {
   1.363 +	char **list = NULL;
   1.364 +	int n;
   1.365 +	XTextProperty name;
   1.366 +
   1.367 +	name.nitems = 0;
   1.368 +	c->name[0] = 0;
   1.369 +	XGetTextProperty(dpy, c->win, &name, netatom[NetWMName]);
   1.370 +	if(!name.nitems)
   1.371 +		XGetWMName(dpy, c->win, &name);
   1.372 +	if(!name.nitems)
   1.373 +		return;
   1.374 +	if(name.encoding == XA_STRING)
   1.375 +		strncpy(c->name, (char *)name.value, sizeof c->name);
   1.376 +	else {
   1.377 +		if(XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success
   1.378 +		&& n > 0 && *list)
   1.379 +		{
   1.380 +			strncpy(c->name, *list, sizeof c->name);
   1.381 +			XFreeStringList(list);
   1.382 +		}
   1.383 +	}
   1.384 +	XFree(name.value);
   1.385 +}
   1.386 +
   1.387 +void
   1.388 +unmanage(Client *c) {
   1.389 +	Client *nc;
   1.390 +
   1.391 +	/* The server grab construct avoids race conditions. */
   1.392 +	XGrabServer(dpy);
   1.393 +	XSetErrorHandler(xerrordummy);
   1.394 +	detach(c);
   1.395 +	detachstack(c);
   1.396 +	if(sel == c) {
   1.397 +		for(nc = stack; nc && !isvisible(nc); nc = nc->snext);
   1.398 +		focus(nc);
   1.399 +	}
   1.400 +	XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
   1.401 +	setclientstate(c, WithdrawnState);
   1.402 +	free(c->tags);
   1.403 +	free(c);
   1.404 +	XSync(dpy, False);
   1.405 +	XSetErrorHandler(xerror);
   1.406 +	XUngrabServer(dpy);
   1.407 +	lt->arrange();
   1.408 +}
   1.409 +
   1.410 +void
   1.411 +zoom(Arg *arg) {
   1.412 +	unsigned int n;
   1.413 +	Client *c;
   1.414 +
   1.415 +	if(!sel)
   1.416 +		return;
   1.417 +	if(sel->isversatile || (lt->arrange == versatile)) {
   1.418 +		togglemax(sel);
   1.419 +		return;
   1.420 +	}
   1.421 +	for(n = 0, c = nexttiled(clients); c; c = nexttiled(c->next))
   1.422 +		n++;
   1.423 +	if((c = sel) == nexttiled(clients))
   1.424 +		if(!(c = nexttiled(c->next)))
   1.425 +			return;
   1.426 +	detach(c);
   1.427 +	attach(c);
   1.428 +	focus(c);
   1.429 +	lt->arrange();
   1.430 +}
   1.431 +
   1.432 +Client *
   1.433 +prevtiled(Client *c) {
   1.434 +	for(; c && (c->isversatile || !isvisible(c)); c = c->prev);
   1.435 +	return c;
   1.436 +}
   1.437 +
   1.438 +void
   1.439 +pushup(Arg *arg) {
   1.440 +	Client *c;
   1.441 +
   1.442 +	if(!sel || sel->isversatile)
   1.443 +		return;
   1.444 +	if((c = prevtiled(sel->prev))) {
   1.445 +		/* attach before c */
   1.446 +		detach(sel);
   1.447 +		sel->next = c;
   1.448 +		sel->prev = c->prev;
   1.449 +		c->prev = sel;
   1.450 +		if(sel->prev)
   1.451 +			sel->prev->next = sel;
   1.452 +		else
   1.453 +			clients = sel;
   1.454 +	} else {
   1.455 +		/* move to the end */
   1.456 +		for(c = sel; c->next; c = c->next);
   1.457 +		detach(sel);
   1.458 +		sel->prev = c;
   1.459 +		c->next = sel;
   1.460 +	}
   1.461 +	focus(sel);
   1.462 +	lt->arrange();
   1.463 +}
   1.464 +
   1.465 +void
   1.466 +pushdown(Arg *arg) {
   1.467 +	Client *c;
   1.468 +
   1.469 +	if(!sel || sel->isversatile)
   1.470 +		return;
   1.471 +	if((c = nexttiled(sel->next))) {
   1.472 +		/* attach after c */
   1.473 +		detach(sel);
   1.474 +		sel->prev = c;
   1.475 +		sel->next = c->next;
   1.476 +		c->next = sel;
   1.477 +		if(sel->next)
   1.478 +			sel->next->prev = sel;
   1.479 +	} else {
   1.480 +		/* move to the front */
   1.481 +		detach(sel);
   1.482 +		attach(sel);
   1.483 +	}
   1.484 +	focus(sel);
   1.485 +	lt->arrange();
   1.486 +}
   1.487 +
   1.488 +void
   1.489 +moveresize(Arg *arg) {
   1.490 +	int x, y, w, h, nx, ny, nw, nh, ox, oy, ow, oh;
   1.491 +	char xAbs, yAbs, wAbs, hAbs;
   1.492 +	int mx, my, dx, dy, nmx, nmy;
   1.493 +	unsigned int dui;
   1.494 +	Window dummy;
   1.495 +
   1.496 +	if (lt->arrange != versatile)
   1.497 +		if (!sel || !sel->isversatile || !arg)
   1.498 +			return;
   1.499 +	if(sscanf(arg->cmd, "%d%c %d%c %d%c %d%c", &x, &xAbs, &y, &yAbs, &w, &wAbs, &h, &hAbs) != 8)
   1.500 +		return;
   1.501 +	nx = xAbs == 'X' ? x : sel->x + x;
   1.502 +	ny = yAbs == 'Y' ? y : sel->y + y;
   1.503 +	nw = wAbs == 'W' ? w : sel->w + w;
   1.504 +	nh = hAbs == 'H' ? h : sel->h + h;
   1.505 +
   1.506 +	ox = sel->x;
   1.507 +	oy = sel->y;
   1.508 +	ow = sel->w;
   1.509 +	oh = sel->h;
   1.510 +
   1.511 +	Bool xqp = XQueryPointer(dpy, root, &dummy, &dummy, &mx, &my, &dx, &dy, &dui);
   1.512 +	resize(sel, nx, ny, nw, nh, True);
   1.513 +	if (xqp && ox <= mx && (ox + ow) >= mx && oy <= my && (oy + oh) >= my)
   1.514 +	{
   1.515 +		nmx = mx-ox+sel->w-ow-1 < 0 ? 0 : mx-ox+sel->w-ow-1;
   1.516 +		nmy = my-oy+sel->h-oh-1 < 0 ? 0 : my-oy+sel->h-oh-1;
   1.517 +		XWarpPointer(dpy, None, sel->win, 0, 0, 0, 0, nmx, nmy);
   1.518 +	}
   1.519 +}
   1.520 +