all repos — dwm @ 838a1ff950eb42e4137cfd7039a0e78227b2ef70

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