all repos — dwm @ da0b2a2f31d5a849464215a86a968ec3a1af2f73

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