all repos — dwm @ b597fa46370af6594c0980f6c2ddefbe943ee933

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#include "dwm.h"
  6#include <stdlib.h>
  7#include <string.h>
  8#include <X11/Xatom.h>
  9#include <X11/Xutil.h>
 10
 11/* static functions */
 12
 13static void
 14detachstack(Client *c)
 15{
 16	Client **tc;
 17	for(tc=&stack; *tc && *tc != c; tc=&(*tc)->snext);
 18	*tc = c->snext;
 19}
 20
 21static void
 22grabbuttons(Client *c, Bool focused)
 23{
 24	XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
 25
 26	if(focused) {
 27		XGrabButton(dpy, Button1, MODKEY, c->win, False, BUTTONMASK,
 28				GrabModeAsync, GrabModeSync, None, None);
 29		XGrabButton(dpy, Button1, MODKEY | LockMask, c->win, False, BUTTONMASK,
 30				GrabModeAsync, GrabModeSync, None, None);
 31		XGrabButton(dpy, Button1, MODKEY | numlockmask, c->win, False, BUTTONMASK,
 32				GrabModeAsync, GrabModeSync, None, None);
 33		XGrabButton(dpy, Button1, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK,
 34				GrabModeAsync, GrabModeSync, None, None);
 35
 36		XGrabButton(dpy, Button2, MODKEY, c->win, False, BUTTONMASK,
 37				GrabModeAsync, GrabModeSync, None, None);
 38		XGrabButton(dpy, Button2, MODKEY | LockMask, c->win, False, BUTTONMASK,
 39				GrabModeAsync, GrabModeSync, None, None);
 40		XGrabButton(dpy, Button2, MODKEY | numlockmask, c->win, False, BUTTONMASK,
 41				GrabModeAsync, GrabModeSync, None, None);
 42		XGrabButton(dpy, Button2, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK,
 43				GrabModeAsync, GrabModeSync, None, None);
 44
 45		XGrabButton(dpy, Button3, MODKEY, c->win, False, BUTTONMASK,
 46				GrabModeAsync, GrabModeSync, None, None);
 47		XGrabButton(dpy, Button3, MODKEY | LockMask, c->win, False, BUTTONMASK,
 48				GrabModeAsync, GrabModeSync, None, None);
 49		XGrabButton(dpy, Button3, MODKEY | numlockmask, c->win, False, BUTTONMASK,
 50				GrabModeAsync, GrabModeSync, None, None);
 51		XGrabButton(dpy, Button3, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK,
 52				GrabModeAsync, GrabModeSync, None, None);
 53	}
 54	else
 55		XGrabButton(dpy, AnyButton, AnyModifier, c->win, False, BUTTONMASK,
 56				GrabModeAsync, GrabModeSync, None, None);
 57
 58}
 59
 60static void
 61resizetitle(Client *c)
 62{
 63	c->tw = textw(c->name);
 64	if(c->tw > c->w)
 65		c->tw = c->w + 2;
 66	c->tx = c->x + c->w - c->tw + 2;
 67	c->ty = c->y;
 68	if(isvisible(c))
 69		XMoveResizeWindow(dpy, c->twin, c->tx, c->ty, c->tw, c->th);
 70	else
 71		XMoveResizeWindow(dpy, c->twin, c->tx + 2 * sw, c->ty, c->tw, c->th);
 72
 73}
 74
 75static int
 76xerrordummy(Display *dsply, XErrorEvent *ee)
 77{
 78	return 0;
 79}
 80
 81/* extern functions */
 82
 83void
 84ban(Client *c)
 85{
 86	XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y);
 87	XMoveWindow(dpy, c->twin, c->tx + 2 * sw, c->ty);
 88}
 89
 90void
 91focus(Client *c)
 92{
 93	Client *old;
 94
 95	if(!issel)
 96		return;
 97	if(!sel)
 98		sel = c;
 99	else if(sel != c) {
100		if(maximized)
101			togglemax(NULL);
102		old = sel;
103		sel = c;
104		if(old) {
105			grabbuttons(old, False);
106			drawtitle(old);
107		}
108	}
109	if(c) {
110		detachstack(c);
111		c->snext = stack;
112		stack = c;
113		grabbuttons(c, True);
114		drawtitle(c);
115		XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime);
116	}
117	else
118		XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
119}
120
121Client *
122getclient(Window w)
123{
124	Client *c;
125
126	for(c = clients; c; c = c->next)
127		if(c->win == w)
128			return c;
129	return NULL;
130}
131
132Client *
133getctitle(Window w)
134{
135	Client *c;
136
137	for(c = clients; c; c = c->next)
138		if(c->twin == w)
139			return c;
140	return NULL;
141}
142
143void
144gravitate(Client *c, Bool invert)
145{
146	int dx = 0, dy = 0;
147
148	switch(c->grav) {
149	default:
150		break;
151	case StaticGravity:
152	case NorthWestGravity:
153	case NorthGravity:
154	case NorthEastGravity:
155		dy = c->border;
156		break;
157	case EastGravity:
158	case CenterGravity:
159	case WestGravity:
160		dy = -(c->h / 2) + c->border;
161		break;
162	case SouthEastGravity:
163	case SouthGravity:
164	case SouthWestGravity:
165		dy = -(c->h);
166		break;
167	}
168
169	switch (c->grav) {
170	default:
171		break;
172	case StaticGravity:
173	case NorthWestGravity:
174	case WestGravity:
175	case SouthWestGravity:
176		dx = c->border;
177		break;
178	case NorthGravity:
179	case CenterGravity:
180	case SouthGravity:
181		dx = -(c->w / 2) + c->border;
182		break;
183	case NorthEastGravity:
184	case EastGravity:
185	case SouthEastGravity:
186		dx = -(c->w + c->border);
187		break;
188	}
189
190	if(invert) {
191		dx = -dx;
192		dy = -dy;
193	}
194	c->x += dx;
195	c->y += dy;
196}
197
198void
199killclient(Arg *arg)
200{
201	if(!sel)
202		return;
203	if(sel->proto & PROTODELWIN)
204		sendevent(sel->win, wmatom[WMProtocols], wmatom[WMDelete]);
205	else
206		XKillClient(dpy, sel->win);
207}
208
209void
210manage(Window w, XWindowAttributes *wa)
211{
212	Client *c;
213	Window trans;
214	XSetWindowAttributes twa;
215
216	c = emallocz(sizeof(Client));
217	c->tags = emallocz(ntags * sizeof(Bool));
218	c->win = w;
219	c->x = c->tx = wa->x;
220	c->y = c->ty = wa->y;
221	c->w = c->tw = wa->width;
222	c->h = wa->height;
223	c->th = bh;
224
225	c->border = 0;
226	updatesize(c);
227
228	if(c->x + c->w + 2 > sw)
229		c->x = sw - c->w - 2;
230	if(c->x < 0)
231		c->x = 0;
232	if(c->y + c->h + 2 > sh)
233		c->y = sh - c->h - 2;
234	if(c->h != sh && c->y < bh)
235		c->y = bh;
236
237	c->proto = getproto(c->win);
238	XSelectInput(dpy, c->win,
239		StructureNotifyMask | PropertyChangeMask | EnterWindowMask);
240	XGetTransientForHint(dpy, c->win, &trans);
241	twa.override_redirect = 1;
242	twa.background_pixmap = ParentRelative;
243	twa.event_mask = ExposureMask | EnterWindowMask;
244
245	c->twin = XCreateWindow(dpy, root, c->tx, c->ty, c->tw, c->th,
246			0, DefaultDepth(dpy, screen), CopyFromParent,
247			DefaultVisual(dpy, screen),
248			CWOverrideRedirect | CWBackPixmap | CWEventMask, &twa);
249
250	grabbuttons(c, False);
251	settags(c, getclient(trans));
252	if(!c->isfloat)
253		c->isfloat = trans
254			|| (c->maxw && c->minw &&
255				c->maxw == c->minw && c->maxh == c->minh);
256
257	if(clients)
258		clients->prev = c;
259	c->next = clients;
260	c->snext = stack;
261	stack = clients = c;
262
263	updatetitle(c);
264	ban(c);
265	XMapWindow(dpy, c->win);
266	XMapWindow(dpy, c->twin);
267	if(isvisible(c))
268		focus(c);
269	arrange(NULL);
270}
271
272void
273resize(Client *c, Bool sizehints, Corner sticky)
274{
275	int bottom = c->y + c->h;
276	int right = c->x + c->w;
277	XWindowChanges wc;
278
279	if(sizehints) {
280		if(c->incw)
281			c->w -= (c->w - c->basew) % c->incw;
282		if(c->inch)
283			c->h -= (c->h - c->baseh) % c->inch;
284		if(c->minw && c->w < c->minw)
285			c->w = c->minw;
286		if(c->minh && c->h < c->minh)
287			c->h = c->minh;
288		if(c->maxw && c->w > c->maxw)
289			c->w = c->maxw;
290		if(c->maxh && c->h > c->maxh)
291			c->h = c->maxh;
292	}
293	if(sticky == TopRight || sticky == BotRight)
294		c->x = right - c->w;
295	if(sticky == BotLeft || sticky == BotRight)
296		c->y = bottom - c->h;
297
298	resizetitle(c);
299	wc.x = c->x;
300	wc.y = c->y;
301	wc.width = c->w;
302	wc.height = c->h;
303	if(c->w == sw && c->h == sh)
304		wc.border_width = 0;
305	else
306		wc.border_width = 1;
307	XConfigureWindow(dpy, c->win, CWX | CWY | CWWidth | CWHeight | CWBorderWidth, &wc);
308	XSync(dpy, False);
309}
310
311void
312updatesize(Client *c)
313{
314	long msize;
315	XSizeHints size;
316
317	if(!XGetWMNormalHints(dpy, c->win, &size, &msize) || !size.flags)
318		size.flags = PSize;
319	c->flags = size.flags;
320	if(c->flags & PBaseSize) {
321		c->basew = size.base_width;
322		c->baseh = size.base_height;
323	}
324	else
325		c->basew = c->baseh = 0;
326	if(c->flags & PResizeInc) {
327		c->incw = size.width_inc;
328		c->inch = size.height_inc;
329	}
330	else
331		c->incw = c->inch = 0;
332	if(c->flags & PMaxSize) {
333		c->maxw = size.max_width;
334		c->maxh = size.max_height;
335	}
336	else
337		c->maxw = c->maxh = 0;
338	if(c->flags & PMinSize) {
339		c->minw = size.min_width;
340		c->minh = size.min_height;
341	}
342	else
343		c->minw = c->minh = 0;
344	if(c->flags & PWinGravity)
345		c->grav = size.win_gravity;
346	else
347		c->grav = NorthWestGravity;
348}
349
350void
351updatetitle(Client *c)
352{
353	char **list = NULL;
354	int n;
355	XTextProperty name;
356
357	name.nitems = 0;
358	c->name[0] = 0;
359	XGetTextProperty(dpy, c->win, &name, netatom[NetWMName]);
360	if(!name.nitems)
361		XGetWMName(dpy, c->win, &name);
362	if(!name.nitems)
363		return;
364	if(name.encoding == XA_STRING)
365		strncpy(c->name, (char *)name.value, sizeof(c->name));
366	else {
367		if(XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success
368				&& n > 0 && *list)
369		{
370			strncpy(c->name, *list, sizeof(c->name));
371			XFreeStringList(list);
372		}
373	}
374	XFree(name.value);
375	resizetitle(c);
376}
377
378void
379togglemax(Arg *arg)
380{
381	int ox, oy, ow, oh;
382	Client *c;
383	XEvent ev;
384
385	if(!sel)
386		return;
387
388	if((maximized = !maximized)) {
389		ox = sel->x;
390		oy = sel->y;
391		ow = sel->w;
392		oh = sel->h;
393		sel->x = sx;
394		sel->y = sy + bh;
395		sel->w = sw - 2;
396		sel->h = sh - 2 - bh;
397
398		restack();
399		for(c = getnext(clients); c; c = getnext(c->next))
400			if(c != sel)
401				ban(c);
402		resize(sel, arrange == dofloat, TopLeft);
403
404		sel->x = ox;
405		sel->y = oy;
406		sel->w = ow;
407		sel->h = oh;
408	}
409	else
410		arrange(NULL);
411	while(XCheckMaskEvent(dpy, EnterWindowMask, &ev));
412}
413
414void
415unmanage(Client *c)
416{
417	Client *nc;
418
419	XGrabServer(dpy);
420	XSetErrorHandler(xerrordummy);
421
422	detach(c);
423	detachstack(c);
424	if(sel == c) {
425		for(nc = stack; nc && !isvisible(nc); nc = nc->snext);
426		focus(nc);
427	}
428
429	XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
430	XDestroyWindow(dpy, c->twin);
431
432	free(c->tags);
433	free(c);
434
435	XSync(dpy, False);
436	XSetErrorHandler(xerror);
437	XUngrabServer(dpy);
438	arrange(NULL);
439}