all repos — dwm @ d934296476be7345842fec1a2630d1752c704078

fork of suckless dynamic window manager

event.c (view raw)

  1/* © 2006-2007 Anselm R. Garbe <garbeam at gmail dot com>
  2 * © 2006-2007 Sander van Dijk <a dot h dot vandijk at gmail dot com>
  3 * © 2006-2007 Jukka Salmi <jukka at salmi dot ch>
  4 * © 2007 Premysl Hruby <dfenze at gmail dot com>
  5 * © 2007 Szabolcs Nagy <nszabolcs at gmail dot com>
  6 * See LICENSE file for license details. */
  7#include "dwm.h"
  8#include <stdio.h>
  9#include <stdlib.h>
 10#include <X11/keysym.h>
 11#include <X11/Xatom.h>
 12
 13/* static */
 14
 15typedef struct {
 16	unsigned long mod;
 17	KeySym keysym;
 18	void (*func)(const char *arg);
 19	const char *arg;
 20} Key;
 21
 22KEYS
 23
 24#define CLEANMASK(mask) (mask & ~(numlockmask | LockMask))
 25#define MOUSEMASK		(BUTTONMASK | PointerMotionMask)
 26
 27static Client *
 28getclient(Window w) {
 29	Client *c;
 30
 31	for(c = clients; c && c->win != w; c = c->next);
 32	return c;
 33}
 34
 35static void
 36movemouse(Client *c) {
 37	int x1, y1, ocx, ocy, di, nx, ny;
 38	unsigned int dui;
 39	Window dummy;
 40	XEvent ev;
 41
 42	ocx = nx = c->x;
 43	ocy = ny = c->y;
 44	if(XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync,
 45			None, cursor[CurMove], CurrentTime) != GrabSuccess)
 46		return;
 47	c->ismax = False;
 48	XQueryPointer(dpy, root, &dummy, &dummy, &x1, &y1, &di, &di, &dui);
 49	for(;;) {
 50		XMaskEvent(dpy, MOUSEMASK | ExposureMask | SubstructureRedirectMask, &ev);
 51		switch (ev.type) {
 52		case ButtonRelease:
 53			XUngrabPointer(dpy, CurrentTime);
 54			return;
 55		case ConfigureRequest:
 56		case Expose:
 57		case MapRequest:
 58			handler[ev.type](&ev);
 59			break;
 60		case MotionNotify:
 61			XSync(dpy, False);
 62			nx = ocx + (ev.xmotion.x - x1);
 63			ny = ocy + (ev.xmotion.y - y1);
 64			if(abs(wax + nx) < SNAP)
 65				nx = wax;
 66			else if(abs((wax + waw) - (nx + c->w + 2 * c->border)) < SNAP)
 67				nx = wax + waw - c->w - 2 * c->border;
 68			if(abs(way - ny) < SNAP)
 69				ny = way;
 70			else if(abs((way + wah) - (ny + c->h + 2 * c->border)) < SNAP)
 71				ny = way + wah - c->h - 2 * c->border;
 72			resize(c, nx, ny, c->w, c->h, False);
 73			break;
 74		}
 75	}
 76}
 77
 78static void
 79resizemouse(Client *c) {
 80	int ocx, ocy;
 81	int nw, nh;
 82	XEvent ev;
 83
 84	ocx = c->x;
 85	ocy = c->y;
 86	if(XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync,
 87			None, cursor[CurResize], CurrentTime) != GrabSuccess)
 88		return;
 89	c->ismax = False;
 90	XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->border - 1, c->h + c->border - 1);
 91	for(;;) {
 92		XMaskEvent(dpy, MOUSEMASK | ExposureMask | SubstructureRedirectMask , &ev);
 93		switch(ev.type) {
 94		case ButtonRelease:
 95			XWarpPointer(dpy, None, c->win, 0, 0, 0, 0,
 96					c->w + c->border - 1, c->h + c->border - 1);
 97			XUngrabPointer(dpy, CurrentTime);
 98			while(XCheckMaskEvent(dpy, EnterWindowMask, &ev));
 99			return;
100		case ConfigureRequest:
101		case Expose:
102		case MapRequest:
103			handler[ev.type](&ev);
104			break;
105		case MotionNotify:
106			XSync(dpy, False);
107			if((nw = ev.xmotion.x - ocx - 2 * c->border + 1) <= 0)
108				nw = 1;
109			if((nh = ev.xmotion.y - ocy - 2 * c->border + 1) <= 0)
110				nh = 1;
111			resize(c, c->x, c->y, nw, nh, True);
112			break;
113		}
114	}
115}
116
117static void
118buttonpress(XEvent *e) {
119	static char buf[32];
120	unsigned int i, x;
121	Client *c;
122	XButtonPressedEvent *ev = &e->xbutton;
123
124	buf[0] = 0;
125	if(barwin == ev->window) {
126		x = 0;
127		for(i = 0; i < ntags; i++) {
128			x += textw(tags[i]);
129			if(ev->x < x) {
130				snprintf(buf, sizeof buf, "%d", i);
131				if(ev->button == Button1) {
132					if(ev->state & MODKEY)
133						tag(buf);
134					else
135						view(buf);
136				}
137				else if(ev->button == Button3) {
138					if(ev->state & MODKEY)
139						toggletag(buf);
140					else
141						toggleview(buf);
142				}
143				return;
144			}
145		}
146		if(ev->x < x + blw)
147			switch(ev->button) {
148			case Button1:
149				setlayout(NULL);
150				break;
151			}
152	}
153	else if((c = getclient(ev->window))) {
154		focus(c);
155		if(CLEANMASK(ev->state) != MODKEY)
156			return;
157		if(ev->button == Button1 && (lt->arrange == floating || c->isfloating)) {
158			restack();
159			movemouse(c);
160		}
161		else if(ev->button == Button2)
162			zoom(NULL);
163		else if(ev->button == Button3
164		&& (lt->arrange == floating || c->isfloating) && !c->isfixed)
165		{
166			restack();
167			resizemouse(c);
168		}
169	}
170}
171
172static void
173configurerequest(XEvent *e) {
174	Client *c;
175	XConfigureRequestEvent *ev = &e->xconfigurerequest;
176	XWindowChanges wc;
177
178	if((c = getclient(ev->window))) {
179		c->ismax = False;
180		if(ev->value_mask & CWBorderWidth)
181			c->border = ev->border_width;
182		if(c->isfixed || c->isfloating || (lt->arrange == floating)) {
183			if(ev->value_mask & CWX)
184				c->x = ev->x;
185			if(ev->value_mask & CWY)
186				c->y = ev->y;
187			if(ev->value_mask & CWWidth)
188				c->w = ev->width;
189			if(ev->value_mask & CWHeight)
190				c->h = ev->height;
191			if((c->x + c->w) > sw && c->isfloating)
192				c->x = sw / 2 - c->w / 2; /* center in x direction */
193			if((c->y + c->h) > sh && c->isfloating)
194				c->y = sh / 2 - c->h / 2; /* center in y direction */
195			if((ev->value_mask & (CWX | CWY))
196			&& !(ev->value_mask & (CWWidth | CWHeight)))
197				configure(c);
198			if(isvisible(c))
199				XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h);
200		}
201		else
202			configure(c);
203	}
204	else {
205		wc.x = ev->x;
206		wc.y = ev->y;
207		wc.width = ev->width;
208		wc.height = ev->height;
209		wc.border_width = ev->border_width;
210		wc.sibling = ev->above;
211		wc.stack_mode = ev->detail;
212		XConfigureWindow(dpy, ev->window, ev->value_mask, &wc);
213	}
214	XSync(dpy, False);
215}
216
217static void
218configurenotify(XEvent *e) {
219	XConfigureEvent *ev = &e->xconfigure;
220
221	if (ev->window == root && (ev->width != sw || ev->height != sh)) {
222		sw = ev->width;
223		sh = ev->height;
224		XFreePixmap(dpy, dc.drawable);
225		dc.drawable = XCreatePixmap(dpy, root, sw, bh, DefaultDepth(dpy, screen));
226		XResizeWindow(dpy, barwin, sw, bh);
227		updatebarpos();
228		lt->arrange();
229	}
230}
231
232static void
233destroynotify(XEvent *e) {
234	Client *c;
235	XDestroyWindowEvent *ev = &e->xdestroywindow;
236
237	if((c = getclient(ev->window)))
238		unmanage(c);
239}
240
241static void
242enternotify(XEvent *e) {
243	Client *c;
244	XCrossingEvent *ev = &e->xcrossing;
245
246	if(ev->mode != NotifyNormal || ev->detail == NotifyInferior)
247		return;
248	if((c = getclient(ev->window)))
249		focus(c);
250	else if(ev->window == root) {
251		selscreen = True;
252		focus(NULL);
253	}
254}
255
256static void
257expose(XEvent *e) {
258	XExposeEvent *ev = &e->xexpose;
259
260	if(ev->count == 0) {
261		if(barwin == ev->window)
262			drawstatus();
263	}
264}
265
266static void
267keypress(XEvent *e) {
268	static unsigned int len = sizeof key / sizeof key[0];
269	unsigned int i;
270	KeySym keysym;
271	XKeyEvent *ev = &e->xkey;
272
273	keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0);
274	for(i = 0; i < len; i++)
275		if(keysym == key[i].keysym
276		&& CLEANMASK(key[i].mod) == CLEANMASK(ev->state))
277		{
278			if(key[i].func)
279				key[i].func(key[i].arg);
280		}
281}
282
283static void
284leavenotify(XEvent *e) {
285	XCrossingEvent *ev = &e->xcrossing;
286
287	if((ev->window == root) && !ev->same_screen) {
288		selscreen = False;
289		focus(NULL);
290	}
291}
292
293static void
294mappingnotify(XEvent *e) {
295	XMappingEvent *ev = &e->xmapping;
296
297	XRefreshKeyboardMapping(ev);
298	if(ev->request == MappingKeyboard)
299		grabkeys();
300}
301
302static void
303maprequest(XEvent *e) {
304	static XWindowAttributes wa;
305	XMapRequestEvent *ev = &e->xmaprequest;
306
307	if(!XGetWindowAttributes(dpy, ev->window, &wa))
308		return;
309	if(wa.override_redirect)
310		return;
311	if(!getclient(ev->window))
312		manage(ev->window, &wa);
313}
314
315static void
316propertynotify(XEvent *e) {
317	Client *c;
318	Window trans;
319	XPropertyEvent *ev = &e->xproperty;
320
321	if(ev->state == PropertyDelete)
322		return; /* ignore */
323	if((c = getclient(ev->window))) {
324		switch (ev->atom) {
325			default: break;
326			case XA_WM_TRANSIENT_FOR:
327				XGetTransientForHint(dpy, c->win, &trans);
328				if(!c->isfloating && (c->isfloating = (getclient(trans) != NULL)))
329					lt->arrange();
330				break;
331			case XA_WM_NORMAL_HINTS:
332				updatesizehints(c);
333				break;
334		}
335		if(ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) {
336			updatetitle(c);
337			if(c == sel)
338				drawstatus();
339		}
340	}
341}
342
343static void
344unmapnotify(XEvent *e) {
345	Client *c;
346	XUnmapEvent *ev = &e->xunmap;
347
348	if((c = getclient(ev->window)))
349		unmanage(c);
350}
351
352/* extern */
353
354void (*handler[LASTEvent]) (XEvent *) = {
355	[ButtonPress] = buttonpress,
356	[ConfigureRequest] = configurerequest,
357	[ConfigureNotify] = configurenotify,
358	[DestroyNotify] = destroynotify,
359	[EnterNotify] = enternotify,
360	[LeaveNotify] = leavenotify,
361	[Expose] = expose,
362	[KeyPress] = keypress,
363	[MappingNotify] = mappingnotify,
364	[MapRequest] = maprequest,
365	[PropertyNotify] = propertynotify,
366	[UnmapNotify] = unmapnotify
367};
368
369void
370grabkeys(void) {
371	static unsigned int len = sizeof key / sizeof key[0];
372	unsigned int i;
373	KeyCode code;
374
375	XUngrabKey(dpy, AnyKey, AnyModifier, root);
376	for(i = 0; i < len; i++) {
377		code = XKeysymToKeycode(dpy, key[i].keysym);
378		XGrabKey(dpy, code, key[i].mod, root, True,
379				GrabModeAsync, GrabModeAsync);
380		XGrabKey(dpy, code, key[i].mod | LockMask, root, True,
381				GrabModeAsync, GrabModeAsync);
382		XGrabKey(dpy, code, key[i].mod | numlockmask, root, True,
383				GrabModeAsync, GrabModeAsync);
384		XGrabKey(dpy, code, key[i].mod | numlockmask | LockMask, root, True,
385				GrabModeAsync, GrabModeAsync);
386	}
387}