JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
ordered variables in structs and source files alphabetically
[dwm.git] / event.c
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 <X11/keysym.h>
9 #include <X11/Xatom.h>
10
11 #define ButtonMask      (ButtonPressMask | ButtonReleaseMask)
12 #define MouseMask       (ButtonMask | PointerMotionMask)
13
14 /* CUSTOMIZE */
15 const char *browse[] = { "firefox", NULL };
16 const char *gimp[] = { "gimp", NULL };
17 const char *term[] = { 
18         "urxvtc", "-tr", "+sb", "-bg", "black", "-fg", "white", "-cr", "white",
19         "-fn", "-*-terminus-medium-*-*-*-13-*-*-*-*-*-iso10646-*", NULL
20 };
21 const char *xlock[] = { "xlock", NULL };
22
23 Key key[] = {
24         /* modifier                             key                     function        arguments */
25         { ControlMask,                  XK_0,           appendtag,      { .i = Tscratch } }, 
26         { ControlMask,                  XK_1,           appendtag,      { .i = Tdev } }, 
27         { ControlMask,                  XK_2,           appendtag,      { .i = Twww } }, 
28         { ControlMask,                  XK_3,           appendtag,      { .i = Twork } }, 
29         { Mod1Mask,                             XK_0,           view,           { .i = Tscratch } }, 
30         { Mod1Mask,                             XK_1,           view,           { .i = Tdev } }, 
31         { Mod1Mask,                             XK_2,           view,           { .i = Twww } }, 
32         { Mod1Mask,                             XK_3,           view,           { .i = Twork } }, 
33         { Mod1Mask,                             XK_j,           focusnext,              { 0 } }, 
34         { Mod1Mask,                             XK_k,           focusprev,              { 0 } },
35         { Mod1Mask,                             XK_m,           maximize,               { 0 } }, 
36         { Mod1Mask,                             XK_space,       dotile,         { 0 } }, 
37         { Mod1Mask,                             XK_Return,      zoom,           { 0 } },
38         { Mod1Mask|ShiftMask,   XK_0,           replacetag,             { .i = Tscratch } }, 
39         { Mod1Mask|ShiftMask,   XK_1,           replacetag,             { .i = Tdev } }, 
40         { Mod1Mask|ShiftMask,   XK_2,           replacetag,             { .i = Twww } }, 
41         { Mod1Mask|ShiftMask,   XK_3,           replacetag,             { .i = Twork } }, 
42         { Mod1Mask|ShiftMask,   XK_c,           killclient,             { 0 } }, 
43         { Mod1Mask|ShiftMask,   XK_g,           spawn,          { .argv = gimp } },
44         { Mod1Mask|ShiftMask,   XK_l,           spawn,          { .argv = xlock } },
45         { Mod1Mask|ShiftMask,   XK_q,           quit,           { 0 } },
46         { Mod1Mask|ShiftMask,   XK_space,       dofloat,        { 0 } }, 
47         { Mod1Mask|ShiftMask,   XK_w,           spawn,          { .argv = browse } },
48         { Mod1Mask|ShiftMask,   XK_Return,      spawn,          { .argv = term } },
49 };
50
51 /* static */
52
53 static void
54 movemouse(Client *c)
55 {
56         XEvent ev;
57         int x1, y1, ocx, ocy, di;
58         unsigned int dui;
59         Window dummy;
60
61         ocx = c->x;
62         ocy = c->y;
63         if(XGrabPointer(dpy, root, False, MouseMask, GrabModeAsync, GrabModeAsync,
64                                 None, cursor[CurMove], CurrentTime) != GrabSuccess)
65                 return;
66         XQueryPointer(dpy, root, &dummy, &dummy, &x1, &y1, &di, &di, &dui);
67         for(;;) {
68                 XMaskEvent(dpy, MouseMask | ExposureMask, &ev);
69                 switch (ev.type) {
70                 default: break;
71                 case Expose:
72                         handler[Expose](&ev);
73                         break;
74                 case MotionNotify:
75                         XSync(dpy, False);
76                         c->x = ocx + (ev.xmotion.x - x1);
77                         c->y = ocy + (ev.xmotion.y - y1);
78                         resize(c, False);
79                         break;
80                 case ButtonRelease:
81                         XUngrabPointer(dpy, CurrentTime);
82                         return;
83                 }
84         }
85 }
86
87 static void
88 resizemouse(Client *c)
89 {
90         XEvent ev;
91         int ocx, ocy;
92
93         ocx = c->x;
94         ocy = c->y;
95         if(XGrabPointer(dpy, root, False, MouseMask, GrabModeAsync, GrabModeAsync,
96                                 None, cursor[CurResize], CurrentTime) != GrabSuccess)
97                 return;
98         XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w, c->h);
99         for(;;) {
100                 XMaskEvent(dpy, MouseMask | ExposureMask, &ev);
101                 switch(ev.type) {
102                 default: break;
103                 case Expose:
104                         handler[Expose](&ev);
105                         break;
106                 case MotionNotify:
107                         XSync(dpy, False);
108                         c->w = abs(ocx - ev.xmotion.x);
109                         c->h = abs(ocy - ev.xmotion.y);
110                         c->x = (ocx <= ev.xmotion.x) ? ocx : ocx - c->w;
111                         c->y = (ocy <= ev.xmotion.y) ? ocy : ocy - c->h;
112                         resize(c, True);
113                         break;
114                 case ButtonRelease:
115                         XUngrabPointer(dpy, CurrentTime);
116                         return;
117                 }
118         }
119 }
120
121 static void
122 buttonpress(XEvent *e)
123 {
124         int x;
125         Arg a;
126         XButtonPressedEvent *ev = &e->xbutton;
127         Client *c;
128
129         if(barwin == ev->window) {
130                 switch(ev->button) {
131                 default:
132                         x = 0;
133                         for(a.i = 0; a.i < TLast; a.i++) {
134                                 x += textw(tags[a.i]);
135                                 if(ev->x < x) {
136                                         view(&a);
137                                         break;
138                                 }
139                         }
140                         break;
141                 case Button4:
142                         a.i = (tsel + 1 < TLast) ? tsel + 1 : 0;
143                         view(&a);
144                         break;
145                 case Button5:
146                         a.i = (tsel - 1 >= 0) ? tsel - 1 : TLast - 1;
147                         view(&a);
148                         break;
149                 }
150         }
151         else if((c = getclient(ev->window))) {
152                 if(arrange == dotile && !c->isfloat) {
153                         if((ev->state & ControlMask) && (ev->button == Button1))
154                                 zoom(NULL);
155                         return;
156                 }
157                 /* floating windows */
158                 higher(c);
159                 switch(ev->button) {
160                 default:
161                         break;
162                 case Button1:
163                         movemouse(c);
164                         break;
165                 case Button2:
166                         lower(c);
167                         break;
168                 case Button3:
169                         resizemouse(c);
170                         break;
171                 }
172         }
173 }
174
175 static void
176 configurerequest(XEvent *e)
177 {
178         XConfigureRequestEvent *ev = &e->xconfigurerequest;
179         XWindowChanges wc;
180         Client *c;
181
182         ev->value_mask &= ~CWSibling;
183         if((c = getclient(ev->window))) {
184                 gravitate(c, True);
185                 if(ev->value_mask & CWX)
186                         c->x = ev->x;
187                 if(ev->value_mask & CWY)
188                         c->y = ev->y;
189                 if(ev->value_mask & CWWidth)
190                         c->w = ev->width;
191                 if(ev->value_mask & CWHeight)
192                         c->h = ev->height;
193                 if(ev->value_mask & CWBorderWidth)
194                         c->border = 1;
195                 gravitate(c, False);
196                 resize(c, True);
197         }
198
199         wc.x = ev->x;
200         wc.y = ev->y;
201         wc.width = ev->width;
202         wc.height = ev->height;
203         wc.border_width = 1;
204         wc.sibling = None;
205         wc.stack_mode = Above;
206         ev->value_mask &= ~CWStackMode;
207         ev->value_mask |= CWBorderWidth;
208         XConfigureWindow(dpy, ev->window, ev->value_mask, &wc);
209         XSync(dpy, False);
210 }
211
212 static void
213 destroynotify(XEvent *e)
214 {
215         Client *c;
216         XDestroyWindowEvent *ev = &e->xdestroywindow;
217
218         if((c = getclient(ev->window)))
219                 unmanage(c);
220 }
221
222 static void
223 enternotify(XEvent *e)
224 {
225         XCrossingEvent *ev = &e->xcrossing;
226         Client *c;
227
228         if(ev->mode != NotifyNormal || ev->detail == NotifyInferior)
229                 return;
230
231         if((c = getclient(ev->window)))
232                 focus(c);
233         else if(ev->window == root)
234                 issel = True;
235 }
236
237 static void
238 expose(XEvent *e)
239 {
240         XExposeEvent *ev = &e->xexpose;
241         Client *c;
242
243         if(ev->count == 0) {
244                 if(barwin == ev->window)
245                         drawstatus();
246                 else if((c = getctitle(ev->window)))
247                         drawtitle(c);
248         }
249 }
250
251 static void
252 keypress(XEvent *e)
253 {
254         XKeyEvent *ev = &e->xkey;
255         static unsigned int len = key ? sizeof(key) / sizeof(key[0]) : 0;
256         unsigned int i;
257         KeySym keysym;
258
259         keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0);
260         for(i = 0; i < len; i++)
261                 if((keysym == key[i].keysym) && (key[i].mod == ev->state)) {
262                         if(key[i].func)
263                                 key[i].func(&key[i].arg);
264                         return;
265                 }
266 }
267
268 static void
269 leavenotify(XEvent *e)
270 {
271         XCrossingEvent *ev = &e->xcrossing;
272
273         if((ev->window == root) && !ev->same_screen)
274                 issel = True;
275 }
276
277 static void
278 maprequest(XEvent *e)
279 {
280         XMapRequestEvent *ev = &e->xmaprequest;
281         static XWindowAttributes wa;
282
283         if(!XGetWindowAttributes(dpy, ev->window, &wa))
284                 return;
285
286         if(wa.override_redirect) {
287                 XSelectInput(dpy, ev->window,
288                                 (StructureNotifyMask | PropertyChangeMask));
289                 return;
290         }
291
292         if(!getclient(ev->window))
293                 manage(ev->window, &wa);
294 }
295
296 static void
297 propertynotify(XEvent *e)
298 {
299         XPropertyEvent *ev = &e->xproperty;
300         Window trans;
301         Client *c;
302
303         if(ev->state == PropertyDelete)
304                 return; /* ignore */
305
306         if((c = getclient(ev->window))) {
307                 if(ev->atom == wmatom[WMProtocols]) {
308                         c->proto = getproto(c->win);
309                         return;
310                 }
311                 switch (ev->atom) {
312                         default: break;
313                         case XA_WM_TRANSIENT_FOR:
314                                 XGetTransientForHint(dpy, c->win, &trans);
315                                 if(!c->isfloat && (c->isfloat = (trans != 0)))
316                                         arrange(NULL);
317                                 break;
318                         case XA_WM_NORMAL_HINTS:
319                                 setsize(c);
320                                 break;
321                 }
322                 if(ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) {
323                         settitle(c);
324                         drawtitle(c);
325                 }
326         }
327 }
328
329 static void
330 unmapnotify(XEvent *e)
331 {
332         Client *c;
333         XUnmapEvent *ev = &e->xunmap;
334
335         if((c = getclient(ev->window)))
336                 unmanage(c);
337 }
338
339 /* extern */
340
341 void (*handler[LASTEvent]) (XEvent *) = {
342         [ButtonPress] = buttonpress,
343         [ConfigureRequest] = configurerequest,
344         [DestroyNotify] = destroynotify,
345         [EnterNotify] = enternotify,
346         [LeaveNotify] = leavenotify,
347         [Expose] = expose,
348         [KeyPress] = keypress,
349         [MapRequest] = maprequest,
350         [PropertyNotify] = propertynotify,
351         [UnmapNotify] = unmapnotify
352 };
353
354 void
355 grabkeys()
356 {
357         static unsigned int len = key ? sizeof(key) / sizeof(key[0]) : 0;
358         unsigned int i;
359         KeyCode code;
360
361         for(i = 0; i < len; i++) {
362                 code = XKeysymToKeycode(dpy, key[i].keysym);
363                 XUngrabKey(dpy, code, key[i].mod, root);
364                 XGrabKey(dpy, code, key[i].mod, root, True,
365                                 GrabModeAsync, GrabModeAsync);
366         }
367 }