all repos — dwm @ 26157e6973f240a9b5ee407b9d2d5eca9358844f

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