all repos — dwm @ b18e6840152b300f2db8ff8dca16aee0839f864b

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