build: set minimal cmake version to 2.4.7
[awesome.git] / event.c
blobf9d650405c14d0f9c512a937a67881b4974b299a
1 /*
2 * event.c - event handlers
4 * Copyright © 2007-2008 Julien Danjou <julien@danjou.info>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include <xcb/xcb_keysyms.h>
23 #include <xcb/xcb_atom.h>
24 #include <xcb/xcb_aux.h>
26 #include "screen.h"
27 #include "event.h"
28 #include "tag.h"
29 #include "statusbar.h"
30 #include "window.h"
31 #include "mouse.h"
32 #include "ewmh.h"
33 #include "client.h"
34 #include "widget.h"
35 #include "titlebar.h"
36 #include "keygrabber.h"
37 #include "lua.h"
38 #include "systray.h"
39 #include "layouts/floating.h"
40 #include "common/xscreen.h"
41 #include "common/xutil.h"
43 extern awesome_t globalconf;
45 /** Handle mouse button press.
46 * \param c The client on which the event happened or NULL.
47 * \param button Button number
48 * \param state Modkeys state
49 * \param buttons Buttons list to check for
51 static void
52 event_handle_mouse_button_press(client_t *c,
53 unsigned int button,
54 unsigned int state,
55 button_t *buttons)
57 button_t *b;
59 for(b = buttons; b; b = b->next)
60 if(button == b->button && CLEANMASK(state) == b->mod && b->fct)
62 if(c)
64 luaA_client_userdata_new(globalconf.L, c);
65 luaA_dofunction(globalconf.L, b->fct, 1);
67 else
68 luaA_dofunction(globalconf.L, b->fct, 0);
72 /** The button press event handler.
73 * \param data currently unused.
74 * \param connection The connection to the X server.
75 * \param ev The event.
77 int
78 event_handle_buttonpress(void *data __attribute__ ((unused)),
79 xcb_connection_t *connection, xcb_button_press_event_t *ev)
81 int screen, tmp;
82 const int nb_screen = xcb_setup_roots_length(xcb_get_setup(connection));
83 client_t *c;
84 widget_node_t *w;
85 statusbar_t *statusbar;
87 for(screen = 0; screen < globalconf.screens_info->nscreen; screen++)
88 for(statusbar = globalconf.screens[screen].statusbar; statusbar; statusbar = statusbar->next)
89 if(statusbar->sw
90 && (statusbar->sw->window == ev->event || statusbar->sw->window == ev->child))
92 /* If the statusbar is child, then x,y are
93 * relative to root window */
94 if(statusbar->sw->window == ev->child)
96 ev->event_x -= statusbar->sw->geometry.x;
97 ev->event_y -= statusbar->sw->geometry.y;
99 /* Need to transform coordinates like it was
100 * top/bottom */
101 switch(statusbar->position)
103 case Right:
104 tmp = ev->event_y;
105 ev->event_y = statusbar->height - ev->event_x;
106 ev->event_x = tmp;
107 break;
108 case Left:
109 tmp = ev->event_y;
110 ev->event_y = ev->event_x;
111 ev->event_x = statusbar->width - tmp;
112 break;
113 default:
114 break;
116 for(w = statusbar->widgets; w; w = w->next)
117 if(ev->event_x >= w->area.x && ev->event_x < w->area.x + w->area.width
118 && ev->event_y >= w->area.y && ev->event_y < w->area.y + w->area.height)
120 w->widget->button_press(w, ev, statusbar->screen,
121 statusbar, AWESOME_TYPE_STATUSBAR);
122 return 0;
124 /* return if no widget match */
125 return 0;
128 if((c = client_getbytitlebarwin(ev->event)))
130 /* Need to transform coordinates like it was
131 * top/bottom */
132 switch(c->titlebar->position)
134 case Right:
135 tmp = ev->event_y;
136 ev->event_y = c->titlebar->height - ev->event_x;
137 ev->event_x = tmp;
138 break;
139 case Left:
140 tmp = ev->event_y;
141 ev->event_y = ev->event_x;
142 ev->event_x = c->titlebar->width - tmp;
143 break;
144 default:
145 break;
147 for(w = c->titlebar->widgets; w; w = w->next)
148 if(ev->event_x >= w->area.x && ev->event_x < w->area.x + w->area.width
149 && ev->event_y >= w->area.y && ev->event_y < w->area.y + w->area.height)
151 w->widget->button_press(w, ev, c->screen,
152 c->titlebar, AWESOME_TYPE_TITLEBAR);
153 return 0;
155 /* return if no widget match */
156 return 0;
159 if((c = client_getbywin(ev->event)))
161 event_handle_mouse_button_press(c, ev->detail, ev->state, c->buttons);
162 xcb_allow_events(globalconf.connection, XCB_ALLOW_REPLAY_POINTER, XCB_CURRENT_TIME);
164 else
165 for(screen = 0; screen < nb_screen; screen++)
166 if(xutil_screen_get(connection, screen)->root == ev->event)
168 event_handle_mouse_button_press(NULL, ev->detail, ev->state,
169 globalconf.buttons.root);
170 return 0;
173 return 0;
176 /** The configure event handler.
177 * \param data currently unused.
178 * \param connection The connection to the X server.
179 * \param ev The event.
182 event_handle_configurerequest(void *data __attribute__ ((unused)),
183 xcb_connection_t *connection, xcb_configure_request_event_t *ev)
185 client_t *c;
186 area_t geometry;
188 if((c = client_getbywin(ev->window)))
190 geometry = c->geometry;
192 if(ev->value_mask & XCB_CONFIG_WINDOW_X)
193 geometry.x = ev->x;
194 if(ev->value_mask & XCB_CONFIG_WINDOW_Y)
195 geometry.y = ev->y;
196 if(ev->value_mask & XCB_CONFIG_WINDOW_WIDTH)
197 geometry.width = ev->width;
198 if(ev->value_mask & XCB_CONFIG_WINDOW_HEIGHT)
199 geometry.height = ev->height;
201 if(geometry.x != c->geometry.x || geometry.y != c->geometry.y
202 || geometry.width != c->geometry.width || geometry.height != c->geometry.height)
204 if(c->isfloating || layout_get_current(c->screen) == layout_floating)
205 client_resize(c, geometry, false);
206 else
208 globalconf.screens[c->screen].need_arrange = true;
209 /* If we do not resize the client, at least tell it that it
210 * has its new configuration. That fixes at least
211 * gnome-terminal */
212 window_configure(c->win, c->geometry, c->border);
215 else
217 titlebar_update_geometry_floating(c);
218 window_configure(c->win, geometry, c->border);
221 else
223 uint16_t config_win_mask = 0;
224 uint32_t config_win_vals[7];
225 unsigned short i = 0;
227 if(ev->value_mask & XCB_CONFIG_WINDOW_X)
229 config_win_mask |= XCB_CONFIG_WINDOW_X;
230 config_win_vals[i++] = ev->x;
232 if(ev->value_mask & XCB_CONFIG_WINDOW_Y)
234 config_win_mask |= XCB_CONFIG_WINDOW_Y;
235 config_win_vals[i++] = ev->y;
237 if(ev->value_mask & XCB_CONFIG_WINDOW_WIDTH)
239 config_win_mask |= XCB_CONFIG_WINDOW_WIDTH;
240 config_win_vals[i++] = ev->width;
242 if(ev->value_mask & XCB_CONFIG_WINDOW_HEIGHT)
244 config_win_mask |= XCB_CONFIG_WINDOW_HEIGHT;
245 config_win_vals[i++] = ev->height;
247 if(ev->value_mask & XCB_CONFIG_WINDOW_BORDER_WIDTH)
249 config_win_mask |= XCB_CONFIG_WINDOW_BORDER_WIDTH;
250 config_win_vals[i++] = ev->border_width;
252 if(ev->value_mask & XCB_CONFIG_WINDOW_SIBLING)
254 config_win_mask |= XCB_CONFIG_WINDOW_SIBLING;
255 config_win_vals[i++] = ev->sibling;
257 if(ev->value_mask & XCB_CONFIG_WINDOW_STACK_MODE)
259 config_win_mask |= XCB_CONFIG_WINDOW_STACK_MODE;
260 config_win_vals[i++] = ev->stack_mode;
263 xcb_configure_window(connection, ev->window, config_win_mask, config_win_vals);
266 return 0;
269 /** The configure notify event handler.
270 * \param data currently unused.
271 * \param connection The connection to the X server.
272 * \param ev The event.
275 event_handle_configurenotify(void *data __attribute__ ((unused)),
276 xcb_connection_t *connection, xcb_configure_notify_event_t *ev)
278 int screen_nbr;
279 const xcb_screen_t *screen;
281 for(screen_nbr = 0; screen_nbr < xcb_setup_roots_length(xcb_get_setup (connection)); screen_nbr++)
282 if((screen = xutil_screen_get(connection, screen_nbr)) != NULL
283 && ev->window == screen->root
284 && (ev->width != screen->width_in_pixels
285 || ev->height != screen->height_in_pixels))
286 /* it's not that we panic, but restart */
287 a_exec(globalconf.argv);
289 return 0;
292 /** The destroy notify event handler.
293 * \param data currently unused.
294 * \param connection The connection to the X server.
295 * \param ev The event.
298 event_handle_destroynotify(void *data __attribute__ ((unused)),
299 xcb_connection_t *connection __attribute__ ((unused)),
300 xcb_destroy_notify_event_t *ev)
302 client_t *c;
304 if((c = client_getbywin(ev->window)))
305 client_unmanage(c);
307 return 0;
310 /** The enter notify event handler.
311 * \param data currently unused.
312 * \param connection The connection to the X server.
313 * \param ev The event.
316 event_handle_enternotify(void *data __attribute__ ((unused)),
317 xcb_connection_t *connection,
318 xcb_enter_notify_event_t *ev)
320 client_t *c;
321 xembed_window_t *emwin;
323 if(ev->mode != XCB_NOTIFY_MODE_NORMAL
324 || (ev->root_x == globalconf.pointer_x
325 && ev->root_y == globalconf.pointer_y))
326 return 0;
328 if((c = client_getbytitlebarwin(ev->event))
329 || (c = client_getbywin(ev->event)))
331 window_grabbuttons(c->win, c->phys_screen, c->buttons);
332 /* The idea behind saving pointer_x and pointer_y is Bob Marley powered.
333 * this will allow us top drop some EnterNotify events and thus not giving
334 * focus to windows appering under the cursor without a cursor move */
335 globalconf.pointer_x = ev->root_x;
336 globalconf.pointer_y = ev->root_y;
338 luaA_client_userdata_new(globalconf.L, c);
339 luaA_dofunction(globalconf.L, globalconf.hooks.mouseover, 1);
341 else if((emwin = xembed_getbywin(globalconf.embedded, ev->event)))
342 xcb_ungrab_button(globalconf.connection, XCB_BUTTON_INDEX_ANY,
343 xutil_screen_get(connection, emwin->phys_screen)->root,
344 ANY_MODIFIER);
345 else
346 window_root_grabbuttons(ev->root);
348 return 0;
351 /** The expose event handler.
352 * \param data currently unused.
353 * \param connection The connection to the X server.
354 * \param ev The event.
357 event_handle_expose(void *data __attribute__ ((unused)),
358 xcb_connection_t *connection __attribute__ ((unused)),
359 xcb_expose_event_t *ev)
361 int screen;
362 statusbar_t *statusbar;
363 client_t *c;
365 if(!ev->count)
367 for(screen = 0; screen < globalconf.screens_info->nscreen; screen++)
368 for(statusbar = globalconf.screens[screen].statusbar; statusbar; statusbar = statusbar->next)
369 if(statusbar->sw
370 && statusbar->sw->window == ev->window)
372 simplewindow_refresh_pixmap(statusbar->sw);
373 return 0;
376 if((c = client_getbytitlebarwin(ev->window)))
377 simplewindow_refresh_pixmap(c->titlebar->sw);
380 return 0;
383 /** The key press event handler.
384 * \param data currently unused.
385 * \param connection The connection to the X server.
386 * \param ev The event.
389 event_handle_keypress(void *data __attribute__ ((unused)),
390 xcb_connection_t *connection __attribute__ ((unused)),
391 xcb_key_press_event_t *ev)
393 xcb_keysym_t keysym;
394 keybinding_t *k;
396 if(globalconf.keygrabber != LUA_REFNIL)
398 lua_rawgeti(globalconf.L, LUA_REGISTRYINDEX, globalconf.keygrabber);
399 if(keygrabber_handlekpress(globalconf.L, ev))
401 if(lua_pcall(globalconf.L, 2, 1, 0))
403 warn("error running function: %s", lua_tostring(globalconf.L, -1));
404 keygrabber_ungrab();
406 else if(!lua_isboolean(globalconf.L, -1) || !lua_toboolean(globalconf.L, -1))
407 keygrabber_ungrab();
409 lua_pop(globalconf.L, 1); /* pop returned value or function if not called */
411 else
413 keysym = xcb_key_symbols_get_keysym(globalconf.keysyms, ev->detail, 0);
415 for(k = globalconf.keys; k; k = k->next)
416 if(((k->keycode && ev->detail == k->keycode) || (k->keysym && keysym == k->keysym))
417 && k->fct && CLEANMASK(k->mod) == CLEANMASK(ev->state))
418 luaA_dofunction(globalconf.L, k->fct, 0);
421 return 0;
424 /** The map request event handler.
425 * \param data currently unused.
426 * \param connection The connection to the X server.
427 * \param ev The event.
430 event_handle_maprequest(void *data __attribute__ ((unused)),
431 xcb_connection_t *connection, xcb_map_request_event_t *ev)
433 int screen_nbr = 0;
434 client_t *c;
435 xcb_get_window_attributes_cookie_t wa_c;
436 xcb_get_window_attributes_reply_t *wa_r;
437 xcb_query_pointer_cookie_t qp_c = { 0 };
438 xcb_query_pointer_reply_t *qp_r = NULL;
439 xcb_get_geometry_cookie_t geom_c;
440 xcb_get_geometry_reply_t *geom_r;
441 xcb_screen_iterator_t iter;
443 wa_c = xcb_get_window_attributes_unchecked(connection, ev->window);
445 if(!(wa_r = xcb_get_window_attributes_reply(connection, wa_c, NULL)))
446 return -1;
448 if(wa_r->override_redirect)
449 goto bailout;
451 if(xembed_getbywin(globalconf.embedded, ev->window))
453 xcb_map_window(connection, ev->window);
454 xembed_window_activate(connection, ev->window);
456 else if(!(c = client_getbywin(ev->window)))
458 geom_c = xcb_get_geometry_unchecked(connection, ev->window);
460 if(globalconf.screens_info->xinerama_is_active)
461 qp_c = xcb_query_pointer_unchecked(connection, xutil_screen_get(globalconf.connection,
462 screen_nbr)->root);
464 if(!(geom_r = xcb_get_geometry_reply(connection, geom_c, NULL)))
466 if(globalconf.screens_info->xinerama_is_active)
468 qp_r = xcb_query_pointer_reply(connection, qp_c, NULL);
469 p_delete(&qp_r);
471 return -1;
474 if(globalconf.screens_info->xinerama_is_active
475 && (qp_r = xcb_query_pointer_reply(connection, qp_c, NULL)))
477 screen_nbr = screen_get_bycoord(globalconf.screens_info, screen_nbr,
478 qp_r->root_x, qp_r->root_y);
479 p_delete(&qp_r);
481 else
482 for(iter = xcb_setup_roots_iterator(xcb_get_setup(connection)), screen_nbr = 0;
483 iter.rem && iter.data->root != geom_r->root; xcb_screen_next (&iter), ++screen_nbr);
485 client_manage(ev->window, geom_r, screen_nbr);
486 p_delete(&geom_r);
489 bailout:
490 p_delete(&wa_r);
491 return 0;
494 /** The property notify event handler.
495 * \param data currently unused.
496 * \param connection The connection to the X server.
497 * \param ev The event.
500 event_handle_propertynotify(void *data __attribute__ ((unused)),
501 xcb_connection_t *connection, xcb_property_notify_event_t *ev)
503 client_t *c;
504 xcb_window_t trans;
505 xembed_window_t *emwin;
507 if(ev->state == XCB_PROPERTY_DELETE)
508 return 0; /* ignore */
509 if((emwin = xembed_getbywin(globalconf.embedded, ev->window)))
510 xembed_property_update(connection, emwin);
511 else if((c = client_getbywin(ev->window)))
513 if(ev->atom == WM_TRANSIENT_FOR)
515 xutil_get_transient_for_hint(connection, c->win, &trans);
516 if(!c->isfloating
517 && (c->isfloating = (client_getbywin(trans) != NULL)))
518 globalconf.screens[c->screen].need_arrange = true;
520 else if (ev->atom == WM_NORMAL_HINTS)
521 client_updatesizehints(c);
522 else if (ev->atom == WM_HINTS)
523 client_updatewmhints(c);
525 if(ev->atom == WM_NAME
526 || ev->atom == xutil_intern_atom_reply(globalconf.connection, &globalconf.atoms,
527 xutil_intern_atom(globalconf.connection,
528 &globalconf.atoms,
529 "_NET_WM_NAME")))
530 client_updatetitle(c);
533 return 0;
536 /** The unmap notify event handler.
537 * \param data currently unused.
538 * \param connection The connection to the X server.
539 * \param ev The event.
542 event_handle_unmapnotify(void *data __attribute__ ((unused)),
543 xcb_connection_t *connection, xcb_unmap_notify_event_t *ev)
545 client_t *c;
546 xembed_window_t *em;
547 int i;
549 /* event->send_event (Xlib) is quivalent to (ev->response_type &
550 * 0x80) in XCB because the SendEvent bit is available in the
551 * response_type field */
552 bool send_event = ((ev->response_type & 0x80) >> 7);
554 if((c = client_getbywin(ev->window))
555 && ev->event == xutil_screen_get(connection, c->phys_screen)->root
556 && send_event && window_getstate(c->win) == XCB_WM_NORMAL_STATE)
557 client_unmanage(c);
559 /** \todo invalidate for all screen might be too much */
560 if((em = xembed_getbywin(globalconf.embedded, ev->window)))
562 xembed_window_list_detach(&globalconf.embedded, em);
563 for(i = 0; i < globalconf.screens_info->nscreen; i++)
564 widget_invalidate_cache(i, WIDGET_CACHE_EMBEDDED);
567 return 0;
570 /** The randr screen change notify event handler.
571 * \param data currently unused.
572 * \param connection The connection to the X server.
573 * \param ev The event.
576 event_handle_randr_screen_change_notify(void *data __attribute__ ((unused)),
577 xcb_connection_t *connection __attribute__ ((unused)),
578 xcb_randr_screen_change_notify_event_t *ev)
580 if(!globalconf.have_randr)
581 return -1;
583 /* Code of XRRUpdateConfiguration Xlib function ported to XCB
584 * (only the code relevant to RRScreenChangeNotify) as the latter
585 * doesn't provide this kind of function */
586 if(ev->rotation & (XCB_RANDR_ROTATION_ROTATE_90 | XCB_RANDR_ROTATION_ROTATE_270))
587 xcb_randr_set_screen_size(connection, ev->root, ev->height, ev->width,
588 ev->mheight, ev->mwidth);
589 else
590 xcb_randr_set_screen_size(connection, ev->root, ev->width, ev->height,
591 ev->mwidth, ev->mheight);
593 /* XRRUpdateConfiguration also executes the following instruction
594 * but it's not useful because SubpixelOrder is not used at all at
595 * the moment
597 * XRenderSetSubpixelOrder(dpy, snum, scevent->subpixel_order);
600 a_exec(globalconf.argv);
601 return 0;
604 /** The client message event handler.
605 * \param data currently unused.
606 * \param connection The connection to the X server.
607 * \param ev The event.
610 event_handle_clientmessage(void *data __attribute__ ((unused)),
611 xcb_connection_t *connection,
612 xcb_client_message_event_t *ev)
614 xutil_intern_atom_request_t atom_xem_q, atom_systray_q;
615 xcb_atom_t atom_xem, atom_systray;
617 atom_xem_q = xutil_intern_atom(connection, &globalconf.atoms, "_XEMBED");
618 atom_systray_q = xutil_intern_atom(connection, &globalconf.atoms, "_NET_SYSTEM_TRAY_OPCODE");
620 atom_xem = xutil_intern_atom_reply(globalconf.connection, &globalconf.atoms, atom_xem_q);
621 atom_systray = xutil_intern_atom_reply(globalconf.connection, &globalconf.atoms, atom_systray_q);
623 if(ev->type == atom_xem)
624 return xembed_process_client_message(ev);
625 else if(ev->type == atom_systray)
626 return systray_process_client_message(ev);
627 return ewmh_process_client_message(ev);
630 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80