all repos — dwm @ 0d0e8bde134b999dd22c891d227d886ca6c9ba2c

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