all repos — dwm @ 2b5553b1ebf7c8e59b5ecf7128b932115ff2b313

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
  7#include <stdlib.h>
  8#include <string.h>
  9#include <X11/Xatom.h>
 10#include <X11/Xutil.h>
 11
 12/* static functions */
 13
 14static void
 15resizetitle(Client *c)
 16{
 17	int i;
 18
 19	c->tw = 0;
 20	for(i = 0; i < TLast; i++)
 21		if(c->tags[i])
 22			c->tw += textw(c->tags[i]);
 23	c->tw += textw(c->name);
 24	if(c->tw > c->w)
 25		c->tw = c->w + 2;
 26	c->tx = c->x + c->w - c->tw + 2;
 27	c->ty = c->y;
 28	if(c->tags[tsel])
 29		XMoveResizeWindow(dpy, c->title, c->tx, c->ty, c->tw, c->th);
 30	else
 31		XMoveResizeWindow(dpy, c->title, c->tx + 2 * sw, c->ty, c->tw, c->th);
 32
 33}
 34
 35static int
 36xerrordummy(Display *dsply, XErrorEvent *ee)
 37{
 38	return 0;
 39}
 40
 41/* extern functions */
 42
 43void
 44ban(Client *c)
 45{
 46	XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y);
 47	XMoveWindow(dpy, c->title, c->tx + 2 * sw, c->ty);
 48}
 49
 50void
 51focus(Client *c)
 52{
 53	Client *old = sel;
 54	XEvent ev;
 55
 56	sel = c;
 57	if(old && old != c)
 58		drawtitle(old);
 59	drawtitle(c);
 60	XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime);
 61	XSync(dpy, False);
 62	while(XCheckMaskEvent(dpy, EnterWindowMask, &ev));
 63}
 64
 65void
 66focusnext(Arg *arg)
 67{
 68	Client *c;
 69   
 70	if(!sel)
 71		return;
 72
 73	if(sel->ismax)
 74		togglemax(NULL);
 75
 76	if(!(c = getnext(sel->next, tsel)))
 77		c = getnext(clients, tsel);
 78	if(c) {
 79		higher(c);
 80		c->revert = sel;
 81		focus(c);
 82	}
 83}
 84
 85void
 86focusprev(Arg *arg)
 87{
 88	Client *c;
 89
 90	if(!sel)
 91		return;
 92
 93	if(sel->ismax)
 94		togglemax(NULL);
 95
 96	if((c = sel->revert && sel->revert->tags[tsel] ? sel->revert : NULL)) {
 97		higher(c);
 98		focus(c);
 99	}
100}
101
102Client *
103getclient(Window w)
104{
105	Client *c;
106
107	for(c = clients; c; c = c->next)
108		if(c->win == w)
109			return c;
110	return NULL;
111}
112
113Client *
114getctitle(Window w)
115{
116	Client *c;
117
118	for(c = clients; c; c = c->next)
119		if(c->title == w)
120			return c;
121	return NULL;
122}
123
124void
125gravitate(Client *c, Bool invert)
126{
127	int dx = 0, dy = 0;
128
129	switch(c->grav) {
130	case StaticGravity:
131	case NorthWestGravity:
132	case NorthGravity:
133	case NorthEastGravity:
134		dy = c->border;
135		break;
136	case EastGravity:
137	case CenterGravity:
138	case WestGravity:
139		dy = -(c->h / 2) + c->border;
140		break;
141	case SouthEastGravity:
142	case SouthGravity:
143	case SouthWestGravity:
144		dy = -(c->h);
145		break;
146	default:
147		break;
148	}
149
150	switch (c->grav) {
151	case StaticGravity:
152	case NorthWestGravity:
153	case WestGravity:
154	case SouthWestGravity:
155		dx = c->border;
156		break;
157	case NorthGravity:
158	case CenterGravity:
159	case SouthGravity:
160		dx = -(c->w / 2) + c->border;
161		break;
162	case NorthEastGravity:
163	case EastGravity:
164	case SouthEastGravity:
165		dx = -(c->w + c->border);
166		break;
167	default:
168		break;
169	}
170
171	if(invert) {
172		dx = -dx;
173		dy = -dy;
174	}
175	c->x += dx;
176	c->y += dy;
177}
178
179void
180higher(Client *c)
181{
182	XRaiseWindow(dpy, c->win);
183	XRaiseWindow(dpy, c->title);
184}
185
186void
187killclient(Arg *arg)
188{
189	if(!sel)
190		return;
191	if(sel->proto & WM_PROTOCOL_DELWIN)
192		sendevent(sel->win, wmatom[WMProtocols], wmatom[WMDelete]);
193	else
194		XKillClient(dpy, sel->win);
195}
196
197void
198lower(Client *c)
199{
200	XLowerWindow(dpy, c->title);
201	XLowerWindow(dpy, c->win);
202}
203
204void
205manage(Window w, XWindowAttributes *wa)
206{
207	int diff;
208	Client *c;
209	Window trans;
210	XSetWindowAttributes twa;
211
212	c = emallocz(sizeof(Client));
213	c->win = w;
214	c->x = c->tx = wa->x;
215	c->y = c->ty = wa->y;
216	c->w = c->tw = wa->width;
217	c->h = wa->height;
218	c->th = bh;
219
220	if(c->y < bh)
221		c->y = c->ty = bh;
222
223	c->border = 1;
224	c->proto = getproto(c->win);
225	setsize(c);
226	XSelectInput(dpy, c->win,
227			StructureNotifyMask | PropertyChangeMask | EnterWindowMask);
228	XGetTransientForHint(dpy, c->win, &trans);
229	twa.override_redirect = 1;
230	twa.background_pixmap = ParentRelative;
231	twa.event_mask = ExposureMask;
232
233	c->title = XCreateWindow(dpy, root, c->tx, c->ty, c->tw, c->th,
234			0, DefaultDepth(dpy, screen), CopyFromParent,
235			DefaultVisual(dpy, screen),
236			CWOverrideRedirect | CWBackPixmap | CWEventMask, &twa);
237
238	settags(c);
239
240	c->next = clients;
241	clients = c;
242
243	XGrabButton(dpy, Button1, MODKEY, c->win, False, ButtonPressMask,
244			GrabModeAsync, GrabModeSync, None, None);
245	XGrabButton(dpy, Button2, MODKEY, c->win, False, ButtonPressMask,
246			GrabModeAsync, GrabModeSync, None, None);
247	XGrabButton(dpy, Button3, MODKEY, c->win, False, ButtonPressMask,
248			GrabModeAsync, GrabModeSync, None, None);
249
250	if(!c->isfloat)
251		c->isfloat = trans || (c->maxw && c->minw &&
252				(c->maxw == c->minw) && (c->maxh == c->minh));
253
254
255	settitle(c);
256	arrange(NULL);
257
258	/* mapping the window now prevents flicker */
259	if(c->tags[tsel]) {
260		XMapRaised(dpy, c->win);
261		XMapRaised(dpy, c->title);
262		focus(c);
263	}
264	else {
265		XMapRaised(dpy, c->win);
266		XMapRaised(dpy, c->title);
267	}
268}
269
270void
271pop(Client *c)
272{
273	Client **l;
274
275	for(l = &clients; *l && *l != c; l = &(*l)->next);
276	*l = c->next;
277
278	c->next = clients; /* pop */
279	clients = c;
280	arrange(NULL);
281}
282
283void
284resize(Client *c, Bool inc, Corner sticky)
285{
286	int bottom = c->y + c->h;
287	int right = c->x + c->w;
288	XConfigureEvent e;
289
290	if(inc) {
291		if(c->incw)
292			c->w -= (c->w - c->basew) % c->incw;
293		if(c->inch)
294			c->h -= (c->h - c->baseh) % c->inch;
295	}
296	if(c->x > sw) /* might happen on restart */
297		c->x = sw - c->w;
298	if(c->y > sh)
299		c->y = sh - c->h;
300	if(c->minw && c->w < c->minw)
301		c->w = c->minw;
302	if(c->minh && c->h < c->minh)
303		c->h = c->minh;
304	if(c->maxw && c->w > c->maxw)
305		c->w = c->maxw;
306	if(c->maxh && c->h > c->maxh)
307		c->h = c->maxh;
308	if(sticky == TopRight || sticky == BotRight)
309		c->x = right - c->w;
310	if(sticky == BotLeft || sticky == BotRight)
311		c->y = bottom - c->h;
312
313	resizetitle(c);
314	XSetWindowBorderWidth(dpy, c->win, 1);
315	XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h);
316
317	e.type = ConfigureNotify;
318	e.event = c->win;
319	e.window = c->win;
320	e.x = c->x;
321	e.y = c->y;
322	e.width = c->w;
323	e.height = c->h;
324	e.border_width = c->border;
325	e.above = None;
326	e.override_redirect = False;
327	XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&e);
328	XSync(dpy, False);
329}
330
331void
332setsize(Client *c)
333{
334	long msize;
335	XSizeHints size;
336
337	if(!XGetWMNormalHints(dpy, c->win, &size, &msize) || !size.flags)
338		size.flags = PSize;
339	c->flags = size.flags;
340	if(c->flags & PBaseSize) {
341		c->basew = size.base_width;
342		c->baseh = size.base_height;
343	}
344	else
345		c->basew = c->baseh = 0;
346	if(c->flags & PResizeInc) {
347		c->incw = size.width_inc;
348		c->inch = size.height_inc;
349	}
350	else
351		c->incw = c->inch = 0;
352	if(c->flags & PMaxSize) {
353		c->maxw = size.max_width;
354		c->maxh = size.max_height;
355	}
356	else
357		c->maxw = c->maxh = 0;
358	if(c->flags & PMinSize) {
359		c->minw = size.min_width;
360		c->minh = size.min_height;
361	}
362	else
363		c->minw = c->minh = 0;
364	if(c->flags & PWinGravity)
365		c->grav = size.win_gravity;
366	else
367		c->grav = NorthWestGravity;
368}
369
370void
371settitle(Client *c)
372{
373	char **list = NULL;
374	int n;
375	XTextProperty name;
376
377	name.nitems = 0;
378	c->name[0] = 0;
379	XGetTextProperty(dpy, c->win, &name, netatom[NetWMName]);
380	if(!name.nitems)
381		XGetWMName(dpy, c->win, &name);
382	if(!name.nitems)
383		return;
384	if(name.encoding == XA_STRING)
385		strncpy(c->name, (char *)name.value, sizeof(c->name));
386	else {
387		if(XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success
388				&& n > 0 && *list)
389		{
390			strncpy(c->name, *list, sizeof(c->name));
391			XFreeStringList(list);
392		}
393	}
394	XFree(name.value);
395	resizetitle(c);
396}
397
398void
399togglemax(Arg *arg)
400{
401	int ox, oy, ow, oh;
402	XEvent ev;
403
404	if(!sel)
405		return;
406
407	if((sel->ismax = !sel->ismax)) {
408		ox = sel->x;
409		oy = sel->y;
410		ow = sel->w;
411		oh = sel->h;
412		sel->x = sx;
413		sel->y = sy + bh;
414		sel->w = sw - 2 * sel->border;
415		sel->h = sh - 2 * sel->border - bh;
416
417		higher(sel);
418		resize(sel, False, TopLeft);
419
420		sel->x = ox;
421		sel->y = oy;
422		sel->w = ow;
423		sel->h = oh;
424	}
425	else
426		resize(sel, False, TopLeft);
427	while(XCheckMaskEvent(dpy, EnterWindowMask, &ev));
428}
429
430void
431unmanage(Client *c)
432{
433	Client **l;
434
435	XGrabServer(dpy);
436	XSetErrorHandler(xerrordummy);
437
438	XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
439	XDestroyWindow(dpy, c->title);
440
441	for(l = &clients; *l && *l != c; l = &(*l)->next);
442	*l = c->next;
443	for(l = &clients; *l; l = &(*l)->next)
444		if((*l)->revert == c)
445			(*l)->revert = NULL;
446	if(sel == c)
447		sel = sel->revert ? sel->revert : clients;
448
449	free(c);
450
451	XSync(dpy, False);
452	XSetErrorHandler(xerror);
453	XUngrabServer(dpy);
454	arrange(NULL);
455	if(sel)
456		focus(sel);
457}
458
459void
460zoom(Arg *arg)
461{
462	Client *c;
463
464	if(!sel)
465		return;
466
467	if(sel == getnext(clients, tsel) && sel->next)  {
468		if((c = getnext(sel->next, tsel)))
469			sel = c;
470	}
471
472	pop(sel);
473	focus(sel);
474}