all repos — dwm @ fd00b3a18620c70721c4c4d9f0169e06b9ce4437

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