git: initial import (.gitignore)
[evilwm.git] / events.c
blob601da98a88061e4f5aa2fb34484c131062c56632
1 /* evilwm - Minimalist Window Manager for X
2 * Copyright (C) 1999-2009 Ciaran Anscomb <evilwm@6809.org.uk>
3 * see README for license and other details. */
5 #include <stdlib.h>
6 #include <string.h>
7 #include <errno.h>
8 #include <sys/select.h>
9 #include "evilwm.h"
10 #include "log.h"
12 static int interruptibleXNextEvent(XEvent *event);
14 #ifdef DEBUG
15 const char *debug_atom_name(Atom a);
16 const char *debug_atom_name(Atom a) {
17 static char buf[48];
18 char *atom_name = XGetAtomName(dpy, a);
19 strncpy(buf, atom_name, sizeof(buf));
20 buf[sizeof(buf)-1] = 0;
21 return buf;
23 #endif
25 static void handle_key_event(XKeyEvent *e) {
26 KeySym key = XKeycodeToKeysym(dpy,e->keycode,0);
27 Client *c;
28 int width_inc, height_inc;
29 ScreenInfo *current_screen = find_current_screen();
31 switch(key) {
32 case KEY_NEW:
33 spawn(opt_term);
34 break;
35 case KEY_NEXT:
36 next();
37 if (XGrabKeyboard(dpy, e->root, False, GrabModeAsync, GrabModeAsync, CurrentTime) == GrabSuccess) {
38 XEvent ev;
39 do {
40 XMaskEvent(dpy, KeyPressMask|KeyReleaseMask, &ev);
41 if (ev.type == KeyPress && XKeycodeToKeysym(dpy,ev.xkey.keycode,0) == KEY_NEXT)
42 next();
43 } while (ev.type == KeyPress || XKeycodeToKeysym(dpy,ev.xkey.keycode,0) == KEY_NEXT);
44 XUngrabKeyboard(dpy, CurrentTime);
46 ewmh_select_client(current);
47 break;
48 case KEY_DOCK_TOGGLE:
49 set_docks_visible(current_screen, !current_screen->docks_visible);
50 break;
51 #ifdef VWM
52 case XK_1: case XK_2: case XK_3: case XK_4:
53 case XK_5: case XK_6: case XK_7: case XK_8:
54 switch_vdesk(current_screen, KEY_TO_VDESK(key));
55 break;
56 case KEY_PREVDESK:
57 if (current_screen->vdesk > 0) {
58 switch_vdesk(current_screen,
59 current_screen->vdesk - 1);
61 break;
62 case KEY_NEXTDESK:
63 if (current_screen->vdesk < VDESK_MAX) {
64 switch_vdesk(current_screen,
65 current_screen->vdesk + 1);
67 break;
68 #endif
69 default: break;
71 c = current;
72 if (c == NULL) return;
73 width_inc = (c->width_inc > 1) ? c->width_inc : 16;
74 height_inc = (c->height_inc > 1) ? c->height_inc : 16;
75 switch (key) {
76 case KEY_LEFT:
77 if (e->state & altmask) {
78 if ((c->width - width_inc) >= c->min_width)
79 c->width -= width_inc;
80 } else {
81 c->x -= 16;
83 goto move_client;
84 case KEY_DOWN:
85 if (e->state & altmask) {
86 if (!c->max_height || (c->height + height_inc) <= c->max_height)
87 c->height += height_inc;
88 } else {
89 c->y += 16;
91 goto move_client;
92 case KEY_UP:
93 if (e->state & altmask) {
94 if ((c->height - height_inc) >= c->min_height)
95 c->height -= height_inc;
96 } else {
97 c->y -= 16;
99 goto move_client;
100 case KEY_RIGHT:
101 if (e->state & altmask) {
102 if (!c->max_width || (c->width + width_inc) <= c->max_width)
103 c->width += width_inc;
104 } else {
105 c->x += 16;
107 goto move_client;
108 case KEY_TOPLEFT:
109 c->x = c->border;
110 c->y = c->border;
111 goto move_client;
112 case KEY_TOPRIGHT:
113 c->x = DisplayWidth(dpy, c->screen->screen)
114 - c->width-c->border;
115 c->y = c->border;
116 goto move_client;
117 case KEY_BOTTOMLEFT:
118 c->x = c->border;
119 c->y = DisplayHeight(dpy, c->screen->screen)
120 - c->height-c->border;
121 goto move_client;
122 case KEY_BOTTOMRIGHT:
123 c->x = DisplayWidth(dpy, c->screen->screen)
124 - c->width-c->border;
125 c->y = DisplayHeight(dpy, c->screen->screen)
126 - c->height-c->border;
127 goto move_client;
128 case KEY_KILL:
129 send_wm_delete(c, e->state & altmask);
130 break;
131 case KEY_LOWER: case KEY_ALTLOWER:
132 client_lower(c);
133 break;
134 case KEY_INFO:
135 show_info(c, e->keycode);
136 break;
137 case KEY_MAX:
138 maximise_client(c, NET_WM_STATE_TOGGLE, MAXIMISE_HORZ|MAXIMISE_VERT);
139 break;
140 case KEY_MAXVERT:
141 maximise_client(c, NET_WM_STATE_TOGGLE, MAXIMISE_VERT);
142 break;
143 #ifdef VWM
144 case KEY_FIX:
145 if (is_fixed(c)) {
146 client_to_vdesk(c, current_screen->vdesk);
147 } else {
148 client_to_vdesk(c, VDESK_FIXED);
150 break;
151 #endif
152 default: break;
154 return;
155 move_client:
156 if (abs(c->x) == c->border && c->oldw != 0)
157 c->x = 0;
158 if (abs(c->y) == c->border && c->oldh != 0)
159 c->y = 0;
160 moveresize(c);
161 #ifdef WARP_POINTER
162 setmouse(c->window, c->width + c->border - 1,
163 c->height + c->border - 1);
164 #endif
165 discard_enter_events();
166 return;
169 #ifdef MOUSE
170 static void handle_button_event(XButtonEvent *e) {
171 Client *c = find_client(e->window);
173 if (c) {
174 switch (e->button) {
175 case Button1:
176 drag(c); break;
177 case Button2:
178 sweep(c); break;
179 case Button3:
180 client_lower(c); break;
181 default: break;
185 #endif
187 static void do_window_changes(int value_mask, XWindowChanges *wc, Client *c,
188 int gravity) {
189 ungravitate(c);
190 if (value_mask & CWX) c->x = wc->x;
191 if (value_mask & CWY) c->y = wc->y;
192 if (value_mask & CWWidth) {
193 c->width = wc->width;
194 if (c->width < c->min_width)
195 c->width = c->min_width;
196 if (c->max_width && c->width > c->max_width)
197 c->width = c->max_width;
199 if (value_mask & CWHeight) {
200 c->height = wc->height;
201 if (c->height < c->min_height)
202 c->height = c->min_height;
203 if (c->max_height && c->height > c->max_height)
204 c->height = c->max_height;
206 if (c->x == 0 && c->width >= DisplayWidth(dpy, c->screen->screen)) {
207 c->x -= c->border;
209 if (c->y == 0 && c->height >= DisplayHeight(dpy, c->screen->screen)) {
210 c->y -= c->border;
212 gravitate(c, gravity);
213 wc->x = c->x - c->border;
214 wc->y = c->y - c->border;
215 wc->border_width = c->border;
216 XConfigureWindow(dpy, c->parent, value_mask, wc);
217 XMoveResizeWindow(dpy, c->window, 0, 0, c->width, c->height);
218 if ((value_mask & (CWX|CWY)) && !(value_mask & (CWWidth|CWHeight))) {
219 send_config(c);
223 static void handle_configure_request(XConfigureRequestEvent *e) {
224 Client *c = find_client(e->window);
225 XWindowChanges wc;
227 wc.x = e->x;
228 wc.y = e->y;
229 wc.width = e->width;
230 wc.height = e->height;
231 wc.border_width = 0;
232 wc.sibling = e->above;
233 wc.stack_mode = e->detail;
234 if (c) {
235 if (e->value_mask & CWStackMode && e->value_mask & CWSibling) {
236 Client *sibling = find_client(e->above);
237 if (sibling) {
238 wc.sibling = sibling->parent;
241 do_window_changes(e->value_mask, &wc, c, 0);
242 } else {
243 LOG_XENTER("XConfigureWindow(window=%lx, value_mask=%lx)", (unsigned int)e->window, e->value_mask);
244 XConfigureWindow(dpy, e->window, e->value_mask, &wc);
245 LOG_XLEAVE();
249 static void handle_map_request(XMapRequestEvent *e) {
250 Client *c = find_client(e->window);
252 LOG_ENTER("handle_map_request(window=%lx)", e->window);
253 if (c) {
254 #ifdef VWM
255 if (!is_fixed(c) && c->vdesk != c->screen->vdesk)
256 switch_vdesk(c->screen, c->vdesk);
257 #endif
258 client_show(c);
259 client_raise(c);
260 } else {
261 XWindowAttributes attr;
262 XGetWindowAttributes(dpy, e->window, &attr);
263 make_new_client(e->window, find_screen(attr.root));
265 LOG_LEAVE();
268 static void handle_unmap_event(XUnmapEvent *e) {
269 Client *c = find_client(e->window);
271 LOG_ENTER("handle_unmap_event(window=%lx)", e->window);
272 if (c) {
273 if (c->ignore_unmap) {
274 c->ignore_unmap--;
275 LOG_DEBUG("ignored (%d ignores remaining)\n", c->ignore_unmap);
276 } else {
277 LOG_DEBUG("flagging client for removal\n");
278 c->remove = 1;
279 need_client_tidy = 1;
281 } else {
282 LOG_DEBUG("unknown client!\n");
284 LOG_LEAVE();
287 static void handle_colormap_change(XColormapEvent *e) {
288 Client *c = find_client(e->window);
290 if (c && e->new) {
291 c->cmap = e->colormap;
292 XInstallColormap(dpy, c->cmap);
296 static void handle_property_change(XPropertyEvent *e) {
297 Client *c = find_client(e->window);
299 if (c) {
300 LOG_ENTER("handle_property_change(window=%lx, atom=%s)", e->window, debug_atom_name(e->atom));
301 if (e->atom == XA_WM_NORMAL_HINTS) {
302 get_wm_normal_hints(c);
303 LOG_DEBUG("geometry=%dx%d+%d+%d\n", c->width, c->height, c->x, c->y);
304 } else if (e->atom == xa_net_wm_window_type) {
305 get_window_type(c);
306 if (!c->is_dock
307 #ifdef VWM
308 && (is_fixed(c) || (c->vdesk == c->screen->vdesk))
309 #endif
311 client_show(c);
314 LOG_LEAVE();
318 static void handle_enter_event(XCrossingEvent *e) {
319 Client *c;
321 if ((c = find_client(e->window))) {
322 #ifdef VWM
323 if (!is_fixed(c) && c->vdesk != c->screen->vdesk)
324 return;
325 #endif
326 select_client(c);
327 ewmh_select_client(c);
331 static void handle_mappingnotify_event(XMappingEvent *e) {
332 XRefreshKeyboardMapping(e);
333 if (e->request == MappingKeyboard) {
334 int i;
335 for (i = 0; i < num_screens; i++) {
336 grab_keys_for_screen(&screens[i]);
341 #ifdef SHAPE
342 static void handle_shape_event(XShapeEvent *e) {
343 Client *c = find_client(e->window);
344 if (c)
345 set_shape(c);
347 #endif
349 static void handle_client_message(XClientMessageEvent *e) {
350 #ifdef VWM
351 ScreenInfo *s = find_current_screen();
352 #endif
353 Client *c;
355 LOG_ENTER("handle_client_message(window=%lx, format=%d, type=%s)", e->window, e->format, debug_atom_name(e->message_type));
357 #ifdef VWM
358 if (e->message_type == xa_net_current_desktop) {
359 switch_vdesk(s, e->data.l[0]);
360 LOG_LEAVE();
361 return;
363 #endif
364 c = find_client(e->window);
365 if (!c && e->message_type == xa_net_request_frame_extents) {
366 ewmh_set_net_frame_extents(e->window);
367 LOG_LEAVE();
368 return;
370 if (!c) {
371 LOG_LEAVE();
372 return;
374 if (e->message_type == xa_net_active_window) {
375 /* Only do this if it came from direct user action */
376 if (e->data.l[0] == 2) {
377 #ifdef VWM
378 if (c->screen == s)
379 #endif
380 select_client(c);
382 LOG_LEAVE();
383 return;
385 if (e->message_type == xa_net_close_window) {
386 /* Only do this if it came from direct user action */
387 if (e->data.l[1] == 2) {
388 send_wm_delete(c, 0);
390 LOG_LEAVE();
391 return;
393 if (e->message_type == xa_net_moveresize_window) {
394 /* Only do this if it came from direct user action */
395 int source_indication = (e->data.l[0] >> 12) & 3;
396 if (source_indication == 2) {
397 int value_mask = (e->data.l[0] >> 8) & 0x0f;
398 int gravity = e->data.l[0] & 0xff;
399 XWindowChanges wc;
401 wc.x = e->data.l[1];
402 wc.y = e->data.l[2];
403 wc.width = e->data.l[3];
404 wc.height = e->data.l[4];
405 do_window_changes(value_mask, &wc, c, gravity);
407 LOG_LEAVE();
408 return;
410 if (e->message_type == xa_net_restack_window) {
411 /* Only do this if it came from direct user action */
412 if (e->data.l[0] == 2) {
413 XWindowChanges wc;
415 wc.sibling = e->data.l[1];
416 wc.stack_mode = e->data.l[2];
417 do_window_changes(CWSibling | CWStackMode, &wc, c, c->win_gravity);
419 LOG_LEAVE();
420 return;
422 #ifdef VWM
423 if (e->message_type == xa_net_wm_desktop) {
424 /* Only do this if it came from direct user action */
425 if (e->data.l[1] == 2) {
426 client_to_vdesk(c, e->data.l[0]);
428 LOG_LEAVE();
429 return;
431 #endif
432 if (e->message_type == xa_net_wm_state) {
433 int i, maximise_hv = 0;
434 /* Message can contain up to two state changes: */
435 for (i = 1; i <= 2; i++) {
436 if ((Atom)e->data.l[i] == xa_net_wm_state_maximized_vert) {
437 maximise_hv |= MAXIMISE_VERT;
438 } else if ((Atom)e->data.l[i] == xa_net_wm_state_maximized_horz) {
439 maximise_hv |= MAXIMISE_HORZ;
440 } else if ((Atom)e->data.l[i] == xa_net_wm_state_fullscreen) {
441 maximise_hv |= MAXIMISE_VERT|MAXIMISE_HORZ;
444 if (maximise_hv) {
445 maximise_client(c, e->data.l[0], maximise_hv);
447 LOG_LEAVE();
448 return;
450 LOG_LEAVE();
453 void event_main_loop(void) {
454 XEvent ev;
455 /* main event loop here */
456 while (!wm_exit) {
457 if (interruptibleXNextEvent(&ev)) {
458 switch (ev.type) {
459 case KeyPress:
460 handle_key_event(&ev.xkey); break;
461 #ifdef MOUSE
462 case ButtonPress:
463 handle_button_event(&ev.xbutton); break;
464 #endif
465 case ConfigureRequest:
466 handle_configure_request(&ev.xconfigurerequest); break;
467 case MapRequest:
468 handle_map_request(&ev.xmaprequest); break;
469 case ColormapNotify:
470 handle_colormap_change(&ev.xcolormap); break;
471 case EnterNotify:
472 handle_enter_event(&ev.xcrossing); break;
473 case PropertyNotify:
474 handle_property_change(&ev.xproperty); break;
475 case UnmapNotify:
476 handle_unmap_event(&ev.xunmap); break;
477 case MappingNotify:
478 handle_mappingnotify_event(&ev.xmapping); break;
479 case ClientMessage:
480 handle_client_message(&ev.xclient); break;
481 default:
482 #ifdef SHAPE
483 if (have_shape && ev.type == shape_event) {
484 handle_shape_event((XShapeEvent *)&ev);
486 #endif
487 #ifdef RANDR
488 if (have_randr && ev.type == randr_event_base + RRScreenChangeNotify) {
489 XRRUpdateConfiguration(&ev);
491 #endif
492 break;
495 if (need_client_tidy) {
496 struct list *iter, *niter;
497 need_client_tidy = 0;
498 for (iter = clients_tab_order; iter; iter = niter) {
499 Client *c = iter->data;
500 niter = iter->next;
501 if (c->remove)
502 remove_client(c);
508 /* interruptibleXNextEvent() is taken from the Blender source and comes with
509 * the following copyright notice: */
511 /* Copyright (c) Mark J. Kilgard, 1994, 1995, 1996. */
513 /* This program is freely distributable without licensing fees
514 * and is provided without guarantee or warrantee expressed or
515 * implied. This program is -not- in the public domain. */
517 /* Unlike XNextEvent, if a signal arrives, interruptibleXNextEvent will
518 * return zero. */
520 static int interruptibleXNextEvent(XEvent *event) {
521 fd_set fds;
522 int rc;
523 int dpy_fd = ConnectionNumber(dpy);
524 for (;;) {
525 if (XPending(dpy)) {
526 XNextEvent(dpy, event);
527 return 1;
529 FD_ZERO(&fds);
530 FD_SET(dpy_fd, &fds);
531 rc = select(dpy_fd + 1, &fds, NULL, NULL, NULL);
532 if (rc < 0) {
533 if (errno == EINTR) {
534 return 0;
535 } else {
536 LOG_ERROR("interruptibleXNextEvent(): select()\n");