all repos — dwm @ 66da15324eef4f85c6dda13eba80430dc4cc97bb

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