all repos — dwm @ c47da143bdf5b4e3924a411f42648d4b3e86ff00

fork of suckless dynamic window manager

client.c (view raw)

  1/*
  2 * (C)opyright MMVI Anselm R. Garbe <garbeam at gmail dot com>
  3 * See LICENSE file for license details.
  4 */
  5
  6#include <math.h>
  7#include <stdlib.h>
  8#include <string.h>
  9#include <X11/Xatom.h>
 10#include <X11/Xutil.h>
 11
 12#include "dwm.h"
 13
 14static void (*arrange)(Arg *) = floating;
 15
 16static Client *
 17next(Client *c)
 18{
 19	for(c = c->next; c && !c->tags[tsel]; c = c->next);
 20	return c;
 21}
 22
 23static Client *
 24prev(Client *c)
 25{
 26	for(c = c->prev; c && !c->tags[tsel]; c = c->prev);
 27	return c;
 28}
 29
 30void
 31max(Arg *arg)
 32{
 33	if(!csel)
 34		return;
 35	csel->x = sx;
 36	csel->y = sy;
 37	csel->w = sw - 2 * csel->border;
 38	csel->h = sh - 2 * csel->border;
 39	craise(csel);
 40	resize(csel);
 41	discard_events(EnterWindowMask);
 42}
 43
 44void
 45tag(Arg *arg)
 46{
 47	if(!csel)
 48		return;
 49
 50	if(arg->i == tsel)
 51		return;
 52
 53	if(csel->tags[arg->i])
 54		csel->tags[arg->i] = NULL; /* toggle tag */
 55	else
 56		csel->tags[arg->i] = tags[arg->i];
 57	arrange(NULL);
 58}
 59
 60void
 61floating(Arg *arg)
 62{
 63	Client *c;
 64
 65	arrange = floating;
 66	if(!csel)
 67		return;
 68	for(c = csel; c; c = next(c))
 69		resize(c);
 70	discard_events(EnterWindowMask);
 71}
 72
 73void
 74tiling(Arg *arg)
 75{
 76	Client *c;
 77	int n, cols, rows, gw, gh, i, j;
 78    float rt, fd;
 79
 80	arrange = tiling;
 81	if(!csel)
 82		return;
 83	for(n = 0, c = csel; c; c = next(c), n++);
 84	rt = sqrt(n);
 85	if(modff(rt, &fd) < 0.5)
 86		rows = floor(rt);
 87	else
 88		rows = ceil(rt);
 89	if(rows * rows < n)
 90		cols = rows + 1;
 91	else
 92		cols = rows;
 93
 94	gw = (sw - 2)  / cols;
 95	gh = (sh - 2) / rows;
 96
 97	for(i = j = 0, c = csel; c; c = next(c)) {
 98		c->x = i * gw;
 99		c->y = j * gh;
100		c->w = gw;
101		c->h = gh;
102		resize(c);
103		if(++i == cols) {
104			j++;
105			i = 0;
106		}
107	}
108	discard_events(EnterWindowMask);
109}
110
111void
112prevc(Arg *arg)
113{
114	Client *c;
115
116	if(!csel)
117		return;
118
119	if(!(c = prev(csel)))
120		c = prev(cend);
121	if(c) {
122		craise(c);
123		XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w / 2, c->h / 2);
124		focus(c);
125	}
126}
127
128void
129nextc(Arg *arg)
130{
131	Client *c;
132   
133	if(!csel)
134		return;
135
136	if(!(c = next(csel)))
137		c = next(cstart);
138
139	if(c) {
140		craise(c);
141		XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w / 2, c->h / 2);
142		focus(c);
143	}
144}
145
146void
147ckill(Arg *arg)
148{
149	Client *c = csel;
150
151	if(!c)
152		return;
153	if(c->proto & WM_PROTOCOL_DELWIN)
154		send_message(c->win, wm_atom[WMProtocols], wm_atom[WMDelete]);
155	else
156		XKillClient(dpy, c->win);
157}
158
159static void
160resize_title(Client *c)
161{
162	int i;
163
164	c->tw = 0;
165	for(i = 0; i < TLast; i++)
166		if(c->tags[i])
167			c->tw += textw(c->tags[i]) + dc.font.height;
168	c->tw += textw(c->name) + dc.font.height;
169	if(c->tw > c->w)
170		c->tw = c->w + 2;
171	c->tx = c->x + c->w - c->tw + 2;
172	c->ty = c->y;
173	XMoveResizeWindow(dpy, c->title, c->tx, c->ty, c->tw, c->th);
174}
175
176void
177update_name(Client *c)
178{
179	XTextProperty name;
180	int n;
181	char **list = NULL;
182
183	name.nitems = 0;
184	c->name[0] = 0;
185	XGetTextProperty(dpy, c->win, &name, net_atom[NetWMName]);
186	if(!name.nitems)
187		XGetWMName(dpy, c->win, &name);
188	if(!name.nitems)
189		return;
190	if(name.encoding == XA_STRING)
191		strncpy(c->name, (char *)name.value, sizeof(c->name));
192	else {
193		if(XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success
194				&& n > 0 && *list)
195		{
196			strncpy(c->name, *list, sizeof(c->name));
197			XFreeStringList(list);
198		}
199	}
200	XFree(name.value);
201	resize_title(c);
202}
203
204void
205update_size(Client *c)
206{
207	XSizeHints size;
208	long msize;
209	if(!XGetWMNormalHints(dpy, c->win, &size, &msize) || !size.flags)
210		size.flags = PSize;
211	c->flags = size.flags;
212	if(c->flags & PBaseSize) {
213		c->basew = size.base_width;
214		c->baseh = size.base_height;
215	}
216	else
217		c->basew = c->baseh = 0;
218	if(c->flags & PResizeInc) {
219		c->incw = size.width_inc;
220		c->inch = size.height_inc;
221	}
222	else
223		c->incw = c->inch = 0;
224	if(c->flags & PMaxSize) {
225		c->maxw = size.max_width;
226		c->maxh = size.max_height;
227	}
228	else
229		c->maxw = c->maxh = 0;
230	if(c->flags & PMinSize) {
231		c->minw = size.min_width;
232		c->minh = size.min_height;
233	}
234	else
235		c->minw = c->minh = 0;
236	if(c->flags & PWinGravity)
237		c->grav = size.win_gravity;
238	else
239		c->grav = NorthWestGravity;
240}
241
242void
243craise(Client *c)
244{
245	XRaiseWindow(dpy, c->win);
246	XRaiseWindow(dpy, c->title);
247}
248
249void
250lower(Client *c)
251{
252	XLowerWindow(dpy, c->title);
253	XLowerWindow(dpy, c->win);
254}
255
256void
257focus(Client *c)
258{
259	if(csel && csel != c) {
260		XSetWindowBorder(dpy, csel->win, dc.bg);
261		XMapWindow(dpy, csel->title);
262		draw_client(csel);
263	}
264	csel = c;
265	XUnmapWindow(dpy, c->title);
266	XSetWindowBorder(dpy, c->win, dc.fg);
267	draw_client(c);
268	XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime);
269	XFlush(dpy);
270	discard_events(EnterWindowMask);
271}
272
273void
274manage(Window w, XWindowAttributes *wa)
275{
276	Client *c;
277	XSetWindowAttributes twa;
278
279	c = emallocz(sizeof(Client));
280	c->win = w;
281	c->tx = c->x = wa->x;
282	c->ty = c->y = wa->y;
283	c->tw = c->w = wa->width;
284	c->h = wa->height;
285	c->th = th;
286	c->border = 1;
287	update_size(c);
288	XSelectInput(dpy, c->win,
289			StructureNotifyMask | PropertyChangeMask | EnterWindowMask);
290	XGetTransientForHint(dpy, c->win, &c->trans);
291	twa.override_redirect = 1;
292	twa.background_pixmap = ParentRelative;
293	twa.event_mask = ExposureMask;
294
295	c->tags[tsel] = tags[tsel];
296	c->title = XCreateWindow(dpy, root, c->tx, c->ty, c->tw, c->th,
297			0, DefaultDepth(dpy, screen), CopyFromParent,
298			DefaultVisual(dpy, screen),
299			CWOverrideRedirect | CWBackPixmap | CWEventMask, &twa);
300
301	update_name(c);
302
303	if(!cstart)
304		cstart = cend = c;
305	else {
306		cend->next = c;
307		c->prev = cend;
308		cend = c;
309	}
310
311	XSetWindowBorderWidth(dpy, c->win, 1);
312	XMapRaised(dpy, c->win);
313	XMapRaised(dpy, c->title);
314	XGrabButton(dpy, Button1, Mod1Mask, c->win, False, ButtonPressMask,
315			GrabModeAsync, GrabModeSync, None, None);
316	XGrabButton(dpy, Button2, Mod1Mask, c->win, False, ButtonPressMask,
317			GrabModeAsync, GrabModeSync, None, None);
318	XGrabButton(dpy, Button3, Mod1Mask, c->win, False, ButtonPressMask,
319			GrabModeAsync, GrabModeSync, None, None);
320	arrange(NULL);
321	XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w / 2, c->h / 2);
322	focus(c);
323}
324
325void
326gravitate(Client *c, Bool invert)
327{
328	int dx = 0, dy = 0;
329
330	switch(c->grav) {
331	case StaticGravity:
332	case NorthWestGravity:
333	case NorthGravity:
334	case NorthEastGravity:
335		dy = c->border;
336		break;
337	case EastGravity:
338	case CenterGravity:
339	case WestGravity:
340		dy = -(c->h / 2) + c->border;
341		break;
342	case SouthEastGravity:
343	case SouthGravity:
344	case SouthWestGravity:
345		dy = -c->h;
346		break;
347	default:
348		break;
349	}
350
351	switch (c->grav) {
352	case StaticGravity:
353	case NorthWestGravity:
354	case WestGravity:
355	case SouthWestGravity:
356		dx = c->border;
357		break;
358	case NorthGravity:
359	case CenterGravity:
360	case SouthGravity:
361		dx = -(c->w / 2) + c->border;
362		break;
363	case NorthEastGravity:
364	case EastGravity:
365	case SouthEastGravity:
366		dx = -(c->w + c->border);
367		break;
368	default:
369		break;
370	}
371
372	if(invert) {
373		dx = -dx;
374		dy = -dy;
375	}
376	c->x += dx;
377	c->y += dy;
378}
379
380
381void
382resize(Client *c)
383{
384	XConfigureEvent e;
385
386	if(c->incw)
387		c->w -= (c->w - c->basew) % c->incw;
388	if(c->inch)
389		c->h -= (c->h - c->baseh) % c->inch;
390	if(c->minw && c->w < c->minw)
391		c->w = c->minw;
392	if(c->minh && c->h < c->minh)
393		c->h = c->minh;
394	if(c->maxw && c->w > c->maxw)
395		c->w = c->maxw;
396	if(c->maxh && c->h > c->maxh)
397		c->h = c->maxh;
398	resize_title(c);
399	XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h);
400	e.type = ConfigureNotify;
401	e.event = c->win;
402	e.window = c->win;
403	e.x = c->x;
404	e.y = c->y;
405	e.width = c->w;
406	e.height = c->h;
407	e.border_width = c->border;
408	e.above = None;
409	e.override_redirect = False;
410	XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&e);
411	XFlush(dpy);
412}
413
414static int
415dummy_error_handler(Display *dsply, XErrorEvent *err)
416{
417	return 0;
418}
419
420void
421unmanage(Client *c)
422{
423	XGrabServer(dpy);
424	XSetErrorHandler(dummy_error_handler);
425
426	XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
427	XDestroyWindow(dpy, c->title);
428
429	if(c->prev) {
430		c->prev->next = c->next;
431		if(csel == c)
432			csel = c->prev;
433	}
434	if(c->next) {
435		c->next->prev = c->prev;
436		if(csel == c)
437			csel = c->next;
438	}
439	if(cstart == c)
440		cstart = c->next;
441	if(cend == c)
442		cend = c->prev;
443
444	free(c);
445
446	XFlush(dpy);
447	XSetErrorHandler(error_handler);
448	XUngrabServer(dpy);
449	arrange(NULL);
450	if(csel)
451		focus(csel);
452}
453
454Client *
455gettitle(Window w)
456{
457	Client *c;
458	for(c = cstart; c; c = c->next)
459		if(c->title == w)
460			return c;
461	return NULL;
462}
463
464Client *
465getclient(Window w)
466{
467	Client *c;
468	for(c = cstart; c; c = c->next)
469		if(c->win == w)
470			return c;
471	return NULL;
472}
473
474void
475draw_client(Client *c)
476{
477	int i;
478	if(c == csel)
479		return;
480
481	dc.x = dc.y = 0;
482	dc.h = c->th;
483
484	dc.w = 0;
485	for(i = 0; i < TLast; i++) {
486		if(c->tags[i]) {
487			dc.x += dc.w;
488			dc.w = textw(c->tags[i]) + dc.font.height;
489			draw(True, c->tags[i]);
490		}
491	}
492	dc.x += dc.w;
493	dc.w = textw(c->name) + dc.font.height;
494	draw(True, c->name);
495	XCopyArea(dpy, dc.drawable, c->title, dc.gc,
496			0, 0, c->tw, c->th, 0, 0);
497	XFlush(dpy);
498}