all repos — dwm @ efa7e514012865fcb3e9ea6e7d5b5c87d84353e5

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