all repos — dwm @ fee8df6ccf3ab1494421d422af252f7b3da3a811

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