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