Fix Alt+Tab to fixed windows
[evilwm.git] / events.c
blobdf348bc8360fe1ef2c9ca568f92cc6004abd4e92
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 XLowerWindow(dpy, c->parent);
133 ewmh_lower_client(c);
134 break;
135 case KEY_INFO:
136 show_info(c, e->keycode);
137 break;
138 case KEY_MAX:
139 maximise_client(c, NET_WM_STATE_TOGGLE, MAXIMISE_HORZ|MAXIMISE_VERT);
140 break;
141 case KEY_MAXVERT:
142 maximise_client(c, NET_WM_STATE_TOGGLE, MAXIMISE_VERT);
143 break;
144 #ifdef VWM
145 case KEY_FIX:
146 if (is_fixed(c)) {
147 client_to_vdesk(c, current_screen->vdesk);
148 } else {
149 client_to_vdesk(c, VDESK_FIXED);
151 break;
152 #endif
153 default: break;
155 return;
156 move_client:
157 if (abs(c->x) == c->border && c->oldw != 0)
158 c->x = 0;
159 if (abs(c->y) == c->border && c->oldh != 0)
160 c->y = 0;
161 moveresize(c);
162 discard_enter_events();
163 return;
166 #ifdef MOUSE
167 static void handle_button_event(XButtonEvent *e) {
168 Client *c = find_client(e->window);
170 if (c) {
171 switch (e->button) {
172 case Button1:
173 drag(c); break;
174 case Button2:
175 sweep(c); break;
176 case Button3:
177 XLowerWindow(dpy, c->parent);
178 ewmh_lower_client(c);
179 break;
180 default: break;
184 #endif
186 static void do_window_changes(int value_mask, XWindowChanges *wc, Client *c,
187 int gravity) {
188 ungravitate(c);
189 if (value_mask & CWX) c->x = wc->x;
190 if (value_mask & CWY) c->y = wc->y;
191 if (value_mask & CWWidth) {
192 c->width = wc->width;
193 if (c->width < c->min_width)
194 c->width = c->min_width;
195 if (c->max_width && c->width > c->max_width)
196 c->width = c->max_width;
198 if (value_mask & CWHeight) {
199 c->height = wc->height;
200 if (c->height < c->min_height)
201 c->height = c->min_height;
202 if (c->max_height && c->height > c->max_height)
203 c->height = c->max_height;
205 if (c->x == 0 && c->width >= DisplayWidth(dpy, c->screen->screen)) {
206 c->x -= c->border;
208 if (c->y == 0 && c->height >= DisplayHeight(dpy, c->screen->screen)) {
209 c->y -= c->border;
211 gravitate(c, gravity);
212 wc->x = c->x - c->border;
213 wc->y = c->y - c->border;
214 wc->border_width = c->border;
215 XConfigureWindow(dpy, c->parent, value_mask, wc);
216 XMoveResizeWindow(dpy, c->window, 0, 0, c->width, c->height);
217 if ((value_mask & (CWX|CWY)) && !(value_mask & (CWWidth|CWHeight))) {
218 send_config(c);
222 static void handle_configure_request(XConfigureRequestEvent *e) {
223 Client *c = find_client(e->window);
224 XWindowChanges wc;
226 wc.x = e->x;
227 wc.y = e->y;
228 wc.width = e->width;
229 wc.height = e->height;
230 wc.border_width = 0;
231 wc.sibling = e->above;
232 wc.stack_mode = e->detail;
233 if (c) {
234 if (e->value_mask & CWStackMode && e->value_mask & CWSibling) {
235 Client *sibling = find_client(e->above);
236 if (sibling) {
237 wc.sibling = sibling->parent;
240 do_window_changes(e->value_mask, &wc, c, 0);
241 } else {
242 LOG_XENTER("XConfigureWindow(window=%lx, value_mask=%lx)", (unsigned int)e->window, e->value_mask);
243 XConfigureWindow(dpy, e->window, e->value_mask, &wc);
244 LOG_XLEAVE();
248 static void handle_map_request(XMapRequestEvent *e) {
249 Client *c = find_client(e->window);
251 LOG_ENTER("handle_map_request(window=%lx)", e->window);
252 if (c) {
253 #ifdef VWM
254 if (!is_fixed(c) && c->vdesk != c->screen->vdesk)
255 switch_vdesk(c->screen, c->vdesk);
256 #endif
257 unhide(c, RAISE);
258 } else {
259 XWindowAttributes attr;
260 XGetWindowAttributes(dpy, e->window, &attr);
261 make_new_client(e->window, find_screen(attr.root));
263 LOG_LEAVE();
266 static void handle_unmap_event(XUnmapEvent *e) {
267 Client *c = find_client(e->window);
269 LOG_ENTER("handle_unmap_event(window=%lx)", e->window);
270 if (c) {
271 if (c->ignore_unmap) {
272 c->ignore_unmap--;
273 LOG_DEBUG("ignored (%d ignores remaining)\n", c->ignore_unmap);
274 } else {
275 LOG_DEBUG("flagging client for removal\n");
276 c->remove = 1;
277 need_client_tidy = 1;
279 } else {
280 LOG_DEBUG("unknown client!\n");
282 LOG_LEAVE();
285 static void handle_colormap_change(XColormapEvent *e) {
286 Client *c = find_client(e->window);
288 if (c && e->new) {
289 c->cmap = e->colormap;
290 XInstallColormap(dpy, c->cmap);
294 static void handle_property_change(XPropertyEvent *e) {
295 Client *c = find_client(e->window);
297 if (c) {
298 LOG_ENTER("handle_property_change(window=%lx, atom=%s)", e->window, debug_atom_name(e->atom));
299 if (e->atom == XA_WM_NORMAL_HINTS) {
300 get_wm_normal_hints(c);
301 LOG_DEBUG("geometry=%dx%d+%d+%d\n", c->width, c->height, c->x, c->y);
302 } else if (e->atom == xa_net_wm_window_type) {
303 get_window_type(c);
304 if (!c->is_dock
305 #ifdef VWM
306 && (is_fixed(c) || (c->vdesk == c->screen->vdesk))
307 #endif
309 unhide(c, NO_RAISE);
312 LOG_LEAVE();
316 static void handle_enter_event(XCrossingEvent *e) {
317 Client *c;
319 if ((c = find_client(e->window))) {
320 #ifdef VWM
321 if (!is_fixed(c) && c->vdesk != c->screen->vdesk)
322 return;
323 #endif
324 select_client(c);
325 ewmh_select_client(c);
329 static void handle_mappingnotify_event(XMappingEvent *e) {
330 XRefreshKeyboardMapping(e);
331 if (e->request == MappingKeyboard) {
332 int i;
333 for (i = 0; i < num_screens; i++) {
334 grab_keys_for_screen(&screens[i]);
339 #ifdef SHAPE
340 static void handle_shape_event(XShapeEvent *e) {
341 Client *c = find_client(e->window);
342 if (c)
343 set_shape(c);
345 #endif
347 static void handle_client_message(XClientMessageEvent *e) {
348 #ifdef VWM
349 ScreenInfo *s = find_current_screen();
350 #endif
351 Client *c;
353 LOG_ENTER("handle_client_message(window=%lx, format=%d, type=%s)", e->window, e->format, debug_atom_name(e->message_type));
355 #ifdef VWM
356 if (e->message_type == xa_net_current_desktop) {
357 switch_vdesk(s, e->data.l[0]);
358 LOG_LEAVE();
359 return;
361 #endif
362 c = find_client(e->window);
363 if (!c && e->message_type == xa_net_request_frame_extents) {
364 ewmh_set_net_frame_extents(e->window);
365 LOG_LEAVE();
366 return;
368 if (!c) {
369 LOG_LEAVE();
370 return;
372 if (e->message_type == xa_net_active_window) {
373 /* Only do this if it came from direct user action */
374 if (e->data.l[0] == 2) {
375 #ifdef VWM
376 if (c->screen == s)
377 #endif
378 select_client(c);
380 LOG_LEAVE();
381 return;
383 if (e->message_type == xa_net_close_window) {
384 /* Only do this if it came from direct user action */
385 if (e->data.l[1] == 2) {
386 send_wm_delete(c, 0);
388 LOG_LEAVE();
389 return;
391 if (e->message_type == xa_net_moveresize_window) {
392 /* Only do this if it came from direct user action */
393 int source_indication = (e->data.l[0] >> 12) & 3;
394 if (source_indication == 2) {
395 int value_mask = (e->data.l[0] >> 8) & 0x0f;
396 int gravity = e->data.l[0] & 0xff;
397 XWindowChanges wc;
399 wc.x = e->data.l[1];
400 wc.y = e->data.l[2];
401 wc.width = e->data.l[3];
402 wc.height = e->data.l[4];
403 do_window_changes(value_mask, &wc, c, gravity);
405 LOG_LEAVE();
406 return;
408 if (e->message_type == xa_net_restack_window) {
409 /* Only do this if it came from direct user action */
410 if (e->data.l[0] == 2) {
411 XWindowChanges wc;
413 wc.sibling = e->data.l[1];
414 wc.stack_mode = e->data.l[2];
415 do_window_changes(CWSibling | CWStackMode, &wc, c, c->win_gravity);
417 LOG_LEAVE();
418 return;
420 #ifdef VWM
421 if (e->message_type == xa_net_wm_desktop) {
422 /* Only do this if it came from direct user action */
423 if (e->data.l[1] == 2) {
424 client_to_vdesk(c, e->data.l[0]);
426 LOG_LEAVE();
427 return;
429 #endif
430 if (e->message_type == xa_net_wm_state) {
431 int i, maximise_hv = 0;
432 /* Message can contain up to two state changes: */
433 for (i = 1; i <= 2; i++) {
434 if ((Atom)e->data.l[i] == xa_net_wm_state_maximized_vert) {
435 maximise_hv |= MAXIMISE_VERT;
436 } else if ((Atom)e->data.l[i] == xa_net_wm_state_maximized_horz) {
437 maximise_hv |= MAXIMISE_HORZ;
438 } else if ((Atom)e->data.l[i] == xa_net_wm_state_fullscreen) {
439 maximise_hv |= MAXIMISE_VERT|MAXIMISE_HORZ;
442 if (maximise_hv) {
443 maximise_client(c, e->data.l[0], maximise_hv);
445 LOG_LEAVE();
446 return;
448 LOG_LEAVE();
451 void event_main_loop(void) {
452 XEvent ev;
453 /* main event loop here */
454 while (!wm_exit) {
455 if (interruptibleXNextEvent(&ev)) {
456 switch (ev.type) {
457 case KeyPress:
458 handle_key_event(&ev.xkey); break;
459 #ifdef MOUSE
460 case ButtonPress:
461 handle_button_event(&ev.xbutton); break;
462 #endif
463 case ConfigureRequest:
464 handle_configure_request(&ev.xconfigurerequest); break;
465 case MapRequest:
466 handle_map_request(&ev.xmaprequest); break;
467 case ColormapNotify:
468 handle_colormap_change(&ev.xcolormap); break;
469 case EnterNotify:
470 handle_enter_event(&ev.xcrossing); break;
471 case PropertyNotify:
472 handle_property_change(&ev.xproperty); break;
473 case UnmapNotify:
474 handle_unmap_event(&ev.xunmap); break;
475 case MappingNotify:
476 handle_mappingnotify_event(&ev.xmapping); break;
477 case ClientMessage:
478 handle_client_message(&ev.xclient); break;
479 default:
480 #ifdef SHAPE
481 if (have_shape && ev.type == shape_event) {
482 handle_shape_event((XShapeEvent *)&ev);
484 #endif
485 #ifdef RANDR
486 if (have_randr && ev.type == randr_event_base + RRScreenChangeNotify) {
487 XRRUpdateConfiguration(&ev);
489 #endif
490 break;
493 if (need_client_tidy) {
494 struct list *iter, *niter;
495 need_client_tidy = 0;
496 for (iter = clients_tab_order; iter; iter = niter) {
497 Client *c = iter->data;
498 niter = iter->next;
499 if (c->remove)
500 remove_client(c);
506 /* interruptibleXNextEvent() is taken from the Blender source and comes with
507 * the following copyright notice: */
509 /* Copyright (c) Mark J. Kilgard, 1994, 1995, 1996. */
511 /* This program is freely distributable without licensing fees
512 * and is provided without guarantee or warrantee expressed or
513 * implied. This program is -not- in the public domain. */
515 /* Unlike XNextEvent, if a signal arrives, interruptibleXNextEvent will
516 * return zero. */
518 static int interruptibleXNextEvent(XEvent *event) {
519 fd_set fds;
520 int rc;
521 int dpy_fd = ConnectionNumber(dpy);
522 for (;;) {
523 if (XPending(dpy)) {
524 XNextEvent(dpy, event);
525 return 1;
527 FD_ZERO(&fds);
528 FD_SET(dpy_fd, &fds);
529 rc = select(dpy_fd + 1, &fds, NULL, NULL, NULL);
530 if (rc < 0) {
531 if (errno == EINTR) {
532 return 0;
533 } else {
534 LOG_ERROR("interruptibleXNextEvent(): select()\n");