all repos — dwm @ e8389a4cc0f1c35bcb7e7646102bd6d6a830207e

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