all repos — dwm @ 1.4

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
247	if(clients)
248		clients->prev = c;
249	c->next = clients;
250	clients = c;
251
252	settitle(c);
253	ban(c);
254	XMapWindow(dpy, c->win);
255	XMapWindow(dpy, c->twin);
256	if(isvisible(c))
257		focus(c);
258	arrange(NULL);
259}
260
261void
262resize(Client *c, Bool sizehints, Corner sticky)
263{
264	int bottom = c->y + c->h;
265	int right = c->x + c->w;
266	XWindowChanges wc;
267
268	if(sizehints) {
269		if(c->incw)
270			c->w -= (c->w - c->basew) % c->incw;
271		if(c->inch)
272			c->h -= (c->h - c->baseh) % c->inch;
273		if(c->minw && c->w < c->minw)
274			c->w = c->minw;
275		if(c->minh && c->h < c->minh)
276			c->h = c->minh;
277		if(c->maxw && c->w > c->maxw)
278			c->w = c->maxw;
279		if(c->maxh && c->h > c->maxh)
280			c->h = c->maxh;
281	}
282	if(sticky == TopRight || sticky == BotRight)
283		c->x = right - c->w;
284	if(sticky == BotLeft || sticky == BotRight)
285		c->y = bottom - c->h;
286
287	resizetitle(c);
288	wc.x = c->x;
289	wc.y = c->y;
290	wc.width = c->w;
291	wc.height = c->h;
292	if(c->w == sw && c->h == sh)
293		wc.border_width = 0;
294	else
295		wc.border_width = 1;
296	XConfigureWindow(dpy, c->win, CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc);
297	XSync(dpy, False);
298}
299
300void
301setsize(Client *c)
302{
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
314		c->basew = c->baseh = 0;
315	if(c->flags & PResizeInc) {
316		c->incw = size.width_inc;
317		c->inch = size.height_inc;
318	}
319	else
320		c->incw = c->inch = 0;
321	if(c->flags & PMaxSize) {
322		c->maxw = size.max_width;
323		c->maxh = size.max_height;
324	}
325	else
326		c->maxw = c->maxh = 0;
327	if(c->flags & PMinSize) {
328		c->minw = size.min_width;
329		c->minh = size.min_height;
330	}
331	else
332		c->minw = c->minh = 0;
333	if(c->flags & PWinGravity)
334		c->grav = size.win_gravity;
335	else
336		c->grav = NorthWestGravity;
337}
338
339void
340settitle(Client *c)
341{
342	char **list = NULL;
343	int n;
344	XTextProperty name;
345
346	name.nitems = 0;
347	c->name[0] = 0;
348	XGetTextProperty(dpy, c->win, &name, netatom[NetWMName]);
349	if(!name.nitems)
350		XGetWMName(dpy, c->win, &name);
351	if(!name.nitems)
352		return;
353	if(name.encoding == XA_STRING)
354		strncpy(c->name, (char *)name.value, sizeof(c->name));
355	else {
356		if(XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success
357				&& n > 0 && *list)
358		{
359			strncpy(c->name, *list, sizeof(c->name));
360			XFreeStringList(list);
361		}
362	}
363	XFree(name.value);
364	resizetitle(c);
365}
366
367void
368togglemax(Arg *arg)
369{
370	int ox, oy, ow, oh;
371	Client *c;
372	XEvent ev;
373
374	if(!sel)
375		return;
376
377	if((maximized = !maximized)) {
378		ox = sel->x;
379		oy = sel->y;
380		ow = sel->w;
381		oh = sel->h;
382		sel->x = sx;
383		sel->y = sy + bh;
384		sel->w = sw - 2;
385		sel->h = sh - 2 - bh;
386
387		restack();
388		for(c = getnext(clients); c; c = getnext(c->next))
389			if(c != sel)
390				ban(c);
391		resize(sel, arrange == dofloat, TopLeft);
392
393		sel->x = ox;
394		sel->y = oy;
395		sel->w = ow;
396		sel->h = oh;
397	}
398	else
399		arrange(NULL);
400	while(XCheckMaskEvent(dpy, EnterWindowMask, &ev));
401}
402
403void
404unmanage(Client *c)
405{
406	Client *tc, *fc;
407	Window trans;
408	XGrabServer(dpy);
409	XSetErrorHandler(xerrordummy);
410
411	detach(c);
412	if(sel == c) {
413		XGetTransientForHint(dpy, c->win, &trans);
414		if(trans && (tc = getclient(trans)) && isvisible(tc))
415			fc = tc;
416		else
417			fc = getnext(clients);
418		focus(fc);
419	}
420
421	XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
422	XDestroyWindow(dpy, c->twin);
423
424	free(c->tags);
425	free(c);
426
427	XSync(dpy, False);
428	XSetErrorHandler(xerror);
429	XUngrabServer(dpy);
430	arrange(NULL);
431}