all repos — dwm @ 5a1a2edf0e584e660e16d2e01094851e0f9161e2

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	ban(c);
220	XMapWindow(dpy, w);
221	setclientstate(c, NormalState);
222	focus(c);
223	lt->arrange();
224}
225
226void
227resize(Client *c, int x, int y, int w, int h, Bool sizehints) {
228	float dx, dy, max, min, ratio;
229	XWindowChanges wc;
230
231	if(w <= 0 || h <= 0)
232		return;
233	if(sizehints) {
234		if(c->minay > 0 && c->maxay > 0 && (h - c->baseh) > 0) {
235			dx = (float)(w - c->basew);
236			dy = (float)(h - c->baseh);
237			min = (float)(c->minax) / (float)(c->minay);
238			max = (float)(c->maxax) / (float)(c->maxay);
239			ratio = dx / dy;
240			if(max > 0 && min > 0 && ratio > 0) {
241				if(ratio < min) {
242					dy = (dx * min + dy) / (min * min + 1);
243					dx = dy * min;
244					w = (int)dx + c->basew;
245					h = (int)dy + c->baseh;
246				}
247				else if(ratio > max) {
248					dy = (dx * min + dy) / (max * max + 1);
249					dx = dy * min;
250					w = (int)dx + c->basew;
251					h = (int)dy + c->baseh;
252				}
253			}
254		}
255		if(c->minw && w < c->minw)
256			w = c->minw;
257		if(c->minh && h < c->minh)
258			h = c->minh;
259		if(c->maxw && w > c->maxw)
260			w = c->maxw;
261		if(c->maxh && h > c->maxh)
262			h = c->maxh;
263		if(c->incw)
264			w -= (w - c->basew) % c->incw;
265		if(c->inch)
266			h -= (h - c->baseh) % c->inch;
267	}
268	if(w <= 0 || h <= 0)
269		return;
270	/* offscreen appearance fixes */
271	if(x > sw)
272		x = sw - w - 2 * c->border;
273	if(y > sh)
274		y = sh - h - 2 * c->border;
275	if(x + w + 2 * c->border < sx)
276		x = sx;
277	if(y + h + 2 * c->border < sy)
278		y = sy;
279	if(c->x != x || c->y != y || c->w != w || c->h != h) {
280		c->x = wc.x = x;
281		c->y = wc.y = y;
282		c->w = wc.width = w;
283		c->h = wc.height = h;
284		wc.border_width = c->border;
285		XConfigureWindow(dpy, c->win, CWX | CWY | CWWidth | CWHeight | CWBorderWidth, &wc);
286		configure(c);
287		XSync(dpy, False);
288	}
289}
290
291void
292togglefloating(const char *arg) {
293	if(!sel || lt->arrange == floating)
294		return;
295	sel->isfloating = !sel->isfloating;
296	if(sel->isfloating)
297		resize(sel, sel->x, sel->y, sel->w, sel->h, True);
298	lt->arrange();
299}
300
301void
302updatesizehints(Client *c) {
303	long msize;
304	XSizeHints size;
305
306	if(!XGetWMNormalHints(dpy, c->win, &size, &msize) || !size.flags)
307		size.flags = PSize;
308	c->flags = size.flags;
309	if(c->flags & PBaseSize) {
310		c->basew = size.base_width;
311		c->baseh = size.base_height;
312	}
313	else if(c->flags & PMinSize) {
314		c->basew = size.min_width;
315		c->baseh = size.min_height;
316	}
317	else
318		c->basew = c->baseh = 0;
319	if(c->flags & PResizeInc) {
320		c->incw = size.width_inc;
321		c->inch = size.height_inc;
322	}
323	else
324		c->incw = c->inch = 0;
325	if(c->flags & PMaxSize) {
326		c->maxw = size.max_width;
327		c->maxh = size.max_height;
328	}
329	else
330		c->maxw = c->maxh = 0;
331	if(c->flags & PMinSize) {
332		c->minw = size.min_width;
333		c->minh = size.min_height;
334	}
335	else if(c->flags & PBaseSize) {
336		c->minw = size.base_width;
337		c->minh = size.base_height;
338	}
339	else
340		c->minw = c->minh = 0;
341	if(c->flags & PAspect) {
342		c->minax = size.min_aspect.x;
343		c->maxax = size.max_aspect.x;
344		c->minay = size.min_aspect.y;
345		c->maxay = size.max_aspect.y;
346	}
347	else
348		c->minax = c->maxax = c->minay = c->maxay = 0;
349	c->isfixed = (c->maxw && c->minw && c->maxh && c->minh
350			&& c->maxw == c->minw && c->maxh == c->minh);
351}
352
353void
354updatetitle(Client *c) {
355	char **list = NULL;
356	int n;
357	XTextProperty name;
358
359	name.nitems = 0;
360	c->name[0] = 0;
361	XGetTextProperty(dpy, c->win, &name, netatom[NetWMName]);
362	if(!name.nitems)
363		XGetWMName(dpy, c->win, &name);
364	if(!name.nitems)
365		return;
366	if(name.encoding == XA_STRING)
367		strncpy(c->name, (char *)name.value, sizeof c->name - 1);
368	else {
369		if(XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success
370		&& n > 0 && *list)
371		{
372			strncpy(c->name, *list, sizeof c->name - 1);
373			XFreeStringList(list);
374		}
375	}
376	c->name[sizeof c->name - 1] = '\0';
377	XFree(name.value);
378}
379
380void
381unmanage(Client *c) {
382	XWindowChanges wc;
383
384	wc.border_width = c->oldborder;
385	/* The server grab construct avoids race conditions. */
386	XGrabServer(dpy);
387	XSetErrorHandler(xerrordummy);
388	XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */
389	detach(c);
390	detachstack(c);
391	if(sel == c)
392		focus(NULL);
393	XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
394	setclientstate(c, WithdrawnState);
395	free(c->tags);
396	free(c);
397	XSync(dpy, False);
398	XSetErrorHandler(xerror);
399	XUngrabServer(dpy);
400	lt->arrange();
401}