all repos — dwm @ b39d0c521ae07117dfb1f2602ece053d25a1ea55

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