all repos — dwm @ a1d0f819661f2be48f7a03ddd001f2a1a8f325e4

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