all repos — dwm @ 4.2

fork of suckless dynamic window manager

client.c (view raw)

  1/* See LICENSE file for copyright and license details. */
  2#include "dwm.h"
  3#include <stdlib.h>
  4#include <string.h>
  5#include <X11/Xatom.h>
  6#include <X11/Xutil.h>
  7
  8/* static */
  9
 10static void
 11attachstack(Client *c) {
 12	c->snext = stack;
 13	stack = c;
 14}
 15
 16static void
 17detachstack(Client *c) {
 18	Client **tc;
 19
 20	for(tc=&stack; *tc && *tc != c; tc=&(*tc)->snext);
 21	*tc = c->snext;
 22}
 23
 24static void
 25grabbuttons(Client *c, Bool focused) {
 26	XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
 27
 28	if(focused) {
 29		XGrabButton(dpy, Button1, MODKEY, c->win, False, BUTTONMASK,
 30				GrabModeAsync, GrabModeSync, None, None);
 31		XGrabButton(dpy, Button1, MODKEY | LockMask, c->win, False, BUTTONMASK,
 32				GrabModeAsync, GrabModeSync, None, None);
 33		XGrabButton(dpy, Button1, MODKEY | numlockmask, c->win, False, BUTTONMASK,
 34				GrabModeAsync, GrabModeSync, None, None);
 35		XGrabButton(dpy, Button1, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK,
 36				GrabModeAsync, GrabModeSync, None, None);
 37
 38		XGrabButton(dpy, Button2, MODKEY, c->win, False, BUTTONMASK,
 39				GrabModeAsync, GrabModeSync, None, None);
 40		XGrabButton(dpy, Button2, MODKEY | LockMask, c->win, False, BUTTONMASK,
 41				GrabModeAsync, GrabModeSync, None, None);
 42		XGrabButton(dpy, Button2, MODKEY | numlockmask, c->win, False, BUTTONMASK,
 43				GrabModeAsync, GrabModeSync, None, None);
 44		XGrabButton(dpy, Button2, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK,
 45				GrabModeAsync, GrabModeSync, None, None);
 46
 47		XGrabButton(dpy, Button3, MODKEY, c->win, False, BUTTONMASK,
 48				GrabModeAsync, GrabModeSync, None, None);
 49		XGrabButton(dpy, Button3, MODKEY | LockMask, c->win, False, BUTTONMASK,
 50				GrabModeAsync, GrabModeSync, None, None);
 51		XGrabButton(dpy, Button3, MODKEY | numlockmask, c->win, False, BUTTONMASK,
 52				GrabModeAsync, GrabModeSync, None, None);
 53		XGrabButton(dpy, Button3, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK,
 54				GrabModeAsync, GrabModeSync, None, None);
 55	}
 56	else
 57		XGrabButton(dpy, AnyButton, AnyModifier, c->win, False, BUTTONMASK,
 58				GrabModeAsync, GrabModeSync, None, None);
 59}
 60
 61static Bool
 62isprotodel(Client *c) {
 63	int i, n;
 64	Atom *protocols;
 65	Bool ret = False;
 66
 67	if(XGetWMProtocols(dpy, c->win, &protocols, &n)) {
 68		for(i = 0; !ret && i < n; i++)
 69			if(protocols[i] == wmatom[WMDelete])
 70				ret = True;
 71		XFree(protocols);
 72	}
 73	return ret;
 74}
 75
 76static void
 77setclientstate(Client *c, long state) {
 78	long data[] = {state, None};
 79
 80	XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32,
 81			PropModeReplace, (unsigned char *)data, 2);
 82}
 83
 84static int
 85xerrordummy(Display *dsply, XErrorEvent *ee) {
 86	return 0;
 87}
 88
 89/* extern */
 90
 91void
 92attach(Client *c) {
 93	if(clients)
 94		clients->prev = c;
 95	c->next = clients;
 96	clients = c;
 97}
 98
 99void
100configure(Client *c) {
101	XConfigureEvent ce;
102
103	ce.type = ConfigureNotify;
104	ce.display = dpy;
105	ce.event = c->win;
106	ce.window = c->win;
107	ce.x = c->x;
108	ce.y = c->y;
109	ce.width = c->w;
110	ce.height = c->h;
111	ce.border_width = c->border;
112	ce.above = None;
113	ce.override_redirect = False;
114	XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce);
115}
116
117void
118detach(Client *c) {
119	if(c->prev)
120		c->prev->next = c->next;
121	if(c->next)
122		c->next->prev = c->prev;
123	if(c == clients)
124		clients = c->next;
125	c->next = c->prev = NULL;
126}
127
128void
129focus(Client *c) {
130	if((!c && selscreen)|| (c && !isvisible(c)))
131		for(c = stack; c && !isvisible(c); c = c->snext);
132	if(sel && sel != c) {
133		grabbuttons(sel, False);
134		XSetWindowBorder(dpy, sel->win, dc.norm[ColBorder]);
135	}
136	if(c) {
137		detachstack(c);
138		attachstack(c);
139		grabbuttons(c, True);
140	}
141	sel = c;
142	drawstatus();
143	if(!selscreen)
144		return;
145	if(c) {
146		XSetWindowBorder(dpy, c->win, dc.sel[ColBorder]);
147		XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime);
148	}
149	else
150		XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
151}
152
153void
154killclient(const char *arg) {
155	XEvent ev;
156
157	if(!sel)
158		return;
159	if(isprotodel(sel)) {
160		ev.type = ClientMessage;
161		ev.xclient.window = sel->win;
162		ev.xclient.message_type = wmatom[WMProtocols];
163		ev.xclient.format = 32;
164		ev.xclient.data.l[0] = wmatom[WMDelete];
165		ev.xclient.data.l[1] = CurrentTime;
166		XSendEvent(dpy, sel->win, False, NoEventMask, &ev);
167	}
168	else
169		XKillClient(dpy, sel->win);
170}
171
172void
173manage(Window w, XWindowAttributes *wa) {
174	Client *c, *t = NULL;
175	Window trans;
176	Status rettrans;
177	XWindowChanges wc;
178
179	c = emallocz(sizeof(Client));
180	c->tags = emallocz(ntags * sizeof(Bool));
181	c->win = w;
182	c->x = wa->x;
183	c->y = wa->y;
184	c->w = wa->width;
185	c->h = wa->height;
186	c->oldborder = wa->border_width;
187	if(c->w == sw && c->h == sh) {
188		c->x = sx;
189		c->y = sy;
190		c->border = wa->border_width;
191	}
192	else {
193		if(c->x + c->w + 2 * c->border > wax + waw)
194			c->x = wax + waw - c->w - 2 * c->border;
195		if(c->y + c->h + 2 * c->border > way + wah)
196			c->y = way + wah - c->h - 2 * c->border;
197		if(c->x < wax)
198			c->x = wax;
199		if(c->y < way)
200			c->y = way;
201		c->border = BORDERPX;
202	}
203	wc.border_width = c->border;
204	XConfigureWindow(dpy, w, CWBorderWidth, &wc);
205	XSetWindowBorder(dpy, w, dc.norm[ColBorder]);
206	configure(c); /* propagates border_width, if size doesn't change */
207	updatesizehints(c);
208	XSelectInput(dpy, w,
209		StructureNotifyMask | PropertyChangeMask | EnterWindowMask);
210	grabbuttons(c, False);
211	updatetitle(c);
212	if((rettrans = XGetTransientForHint(dpy, w, &trans) == Success))
213		for(t = clients; t && t->win != trans; t = t->next);
214	settags(c, t);
215	if(!c->isfloating)
216		c->isfloating = (rettrans == Success) || c->isfixed;
217	attach(c);
218	attachstack(c);
219	c->isbanned = True;
220	XMoveWindow(dpy, w, c->x + 2 * sw, c->y);
221	XMapWindow(dpy, w);
222	setclientstate(c, NormalState);
223	focus(c);
224	lt->arrange();
225}
226
227void
228resize(Client *c, int x, int y, int w, int h, Bool sizehints) {
229	float dx, dy, max, min, ratio;
230	XWindowChanges wc;
231
232	if(w <= 0 || h <= 0)
233		return;
234	if(sizehints) {
235		if(c->minay > 0 && c->maxay > 0 && (h - c->baseh) > 0) {
236			dx = (float)(w - c->basew);
237			dy = (float)(h - c->baseh);
238			min = (float)(c->minax) / (float)(c->minay);
239			max = (float)(c->maxax) / (float)(c->maxay);
240			ratio = dx / dy;
241			if(max > 0 && min > 0 && ratio > 0) {
242				if(ratio < min) {
243					dy = (dx * min + dy) / (min * min + 1);
244					dx = dy * min;
245					w = (int)dx + c->basew;
246					h = (int)dy + c->baseh;
247				}
248				else if(ratio > max) {
249					dy = (dx * min + dy) / (max * max + 1);
250					dx = dy * min;
251					w = (int)dx + c->basew;
252					h = (int)dy + c->baseh;
253				}
254			}
255		}
256		if(c->minw && w < c->minw)
257			w = c->minw;
258		if(c->minh && h < c->minh)
259			h = c->minh;
260		if(c->maxw && w > c->maxw)
261			w = c->maxw;
262		if(c->maxh && h > c->maxh)
263			h = c->maxh;
264		if(c->incw)
265			w -= (w - c->basew) % c->incw;
266		if(c->inch)
267			h -= (h - c->baseh) % c->inch;
268	}
269	if(w <= 0 || h <= 0)
270		return;
271	/* offscreen appearance fixes */
272	if(x > sw)
273		x = sw - w - 2 * c->border;
274	if(y > sh)
275		y = sh - h - 2 * c->border;
276	if(x + w + 2 * c->border < sx)
277		x = sx;
278	if(y + h + 2 * c->border < sy)
279		y = sy;
280	if(c->x != x || c->y != y || c->w != w || c->h != h) {
281		c->x = wc.x = x;
282		c->y = wc.y = y;
283		c->w = wc.width = w;
284		c->h = wc.height = h;
285		wc.border_width = c->border;
286		XConfigureWindow(dpy, c->win, CWX | CWY | CWWidth | CWHeight | CWBorderWidth, &wc);
287		configure(c);
288		XSync(dpy, False);
289	}
290}
291
292void
293togglefloating(const char *arg) {
294	if(!sel || lt->arrange == floating)
295		return;
296	sel->isfloating = !sel->isfloating;
297	if(sel->isfloating)
298		resize(sel, sel->x, sel->y, sel->w, sel->h, True);
299	lt->arrange();
300}
301
302void
303updatesizehints(Client *c) {
304	long msize;
305	XSizeHints size;
306
307	if(!XGetWMNormalHints(dpy, c->win, &size, &msize) || !size.flags)
308		size.flags = PSize;
309	c->flags = size.flags;
310	if(c->flags & PBaseSize) {
311		c->basew = size.base_width;
312		c->baseh = size.base_height;
313	}
314	else if(c->flags & PMinSize) {
315		c->basew = size.min_width;
316		c->baseh = size.min_height;
317	}
318	else
319		c->basew = c->baseh = 0;
320	if(c->flags & PResizeInc) {
321		c->incw = size.width_inc;
322		c->inch = size.height_inc;
323	}
324	else
325		c->incw = c->inch = 0;
326	if(c->flags & PMaxSize) {
327		c->maxw = size.max_width;
328		c->maxh = size.max_height;
329	}
330	else
331		c->maxw = c->maxh = 0;
332	if(c->flags & PMinSize) {
333		c->minw = size.min_width;
334		c->minh = size.min_height;
335	}
336	else if(c->flags & PBaseSize) {
337		c->minw = size.base_width;
338		c->minh = size.base_height;
339	}
340	else
341		c->minw = c->minh = 0;
342	if(c->flags & PAspect) {
343		c->minax = size.min_aspect.x;
344		c->maxax = size.max_aspect.x;
345		c->minay = size.min_aspect.y;
346		c->maxay = size.max_aspect.y;
347	}
348	else
349		c->minax = c->maxax = c->minay = c->maxay = 0;
350	c->isfixed = (c->maxw && c->minw && c->maxh && c->minh
351			&& c->maxw == c->minw && c->maxh == c->minh);
352}
353
354void
355updatetitle(Client *c) {
356	char **list = NULL;
357	int n;
358	XTextProperty name;
359
360	name.nitems = 0;
361	c->name[0] = 0;
362	XGetTextProperty(dpy, c->win, &name, netatom[NetWMName]);
363	if(!name.nitems)
364		XGetWMName(dpy, c->win, &name);
365	if(!name.nitems)
366		return;
367	if(name.encoding == XA_STRING)
368		strncpy(c->name, (char *)name.value, sizeof c->name - 1);
369	else {
370		if(XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success
371		&& n > 0 && *list)
372		{
373			strncpy(c->name, *list, sizeof c->name - 1);
374			XFreeStringList(list);
375		}
376	}
377	c->name[sizeof c->name - 1] = '\0';
378	XFree(name.value);
379}
380
381void
382unmanage(Client *c) {
383	XWindowChanges wc;
384
385	wc.border_width = c->oldborder;
386	/* The server grab construct avoids race conditions. */
387	XGrabServer(dpy);
388	XSetErrorHandler(xerrordummy);
389	XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */
390	detach(c);
391	detachstack(c);
392	if(sel == c)
393		focus(NULL);
394	XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
395	setclientstate(c, WithdrawnState);
396	free(c->tags);
397	free(c);
398	XSync(dpy, False);
399	XSetErrorHandler(xerror);
400	XUngrabServer(dpy);
401	lt->arrange();
402}