screen: make tags array overwritable
[awesome.git] / event.c
blob76df8a127a4b2e1753451fb2361b2b3698becb52
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.h>
23 #include <xcb/xcb_atom.h>
24 #include <xcb/randr.h>
25 #include <xcb/xcb_icccm.h>
27 #include "event.h"
28 #include "tag.h"
29 #include "window.h"
30 #include "ewmh.h"
31 #include "client.h"
32 #include "widget.h"
33 #include "titlebar.h"
34 #include "keybinding.h"
35 #include "keygrabber.h"
36 #include "lua.h"
37 #include "systray.h"
38 #include "layouts/floating.h"
39 #include "common/atoms.h"
41 extern awesome_t globalconf;
43 /** Handle mouse button press.
44 * \param c The client on which the event happened or NULL.
45 * \param button Button number
46 * \param state Modkeys state
47 * \param buttons Buttons list to check for
49 static void
50 event_handle_mouse_button_press(client_t *c,
51 unsigned int button,
52 unsigned int state,
53 button_t *buttons)
55 button_t *b;
57 for(b = buttons; b; b = b->next)
58 if(button == b->button && XUTIL_MASK_CLEAN(state) == b->mod && b->fct)
60 if(c)
62 luaA_client_userdata_new(globalconf.L, c);
63 luaA_dofunction(globalconf.L, b->fct, 1, 0);
65 else
66 luaA_dofunction(globalconf.L, b->fct, 0, 0);
70 /** The button press event handler.
71 * \param data currently unused.
72 * \param connection The connection to the X server.
73 * \param ev The event.
75 static int
76 event_handle_buttonpress(void *data __attribute__ ((unused)),
77 xcb_connection_t *connection, xcb_button_press_event_t *ev)
79 int screen, tmp;
80 const int nb_screen = xcb_setup_roots_length(xcb_get_setup(connection));
81 client_t *c;
82 widget_node_t *w;
83 statusbar_t *statusbar;
85 for(screen = 0; screen < globalconf.screens_info->nscreen; screen++)
86 for(statusbar = globalconf.screens[screen].statusbar; statusbar; statusbar = statusbar->next)
87 if(statusbar->sw
88 && (statusbar->sw->window == ev->event || statusbar->sw->window == ev->child))
90 /* If the statusbar is child, then x,y are
91 * relative to root window */
92 if(statusbar->sw->window == ev->child)
94 ev->event_x -= statusbar->sw->geometry.x;
95 ev->event_y -= statusbar->sw->geometry.y;
97 /* Need to transform coordinates like it was
98 * top/bottom */
99 switch(statusbar->position)
101 case Right:
102 tmp = ev->event_y;
103 ev->event_y = statusbar->height - ev->event_x;
104 ev->event_x = tmp;
105 break;
106 case Left:
107 tmp = ev->event_y;
108 ev->event_y = ev->event_x;
109 ev->event_x = statusbar->width - tmp;
110 break;
111 default:
112 break;
114 for(w = statusbar->widgets; w; w = w->next)
115 if(ev->event_x >= w->area.x && ev->event_x < w->area.x + w->area.width
116 && ev->event_y >= w->area.y && ev->event_y < w->area.y + w->area.height)
118 w->widget->button_press(w, ev, statusbar->screen,
119 statusbar, AWESOME_TYPE_STATUSBAR);
120 return 0;
122 /* return if no widget match */
123 return 0;
126 if((c = client_getbytitlebarwin(ev->event)))
128 /* Need to transform coordinates like it was
129 * top/bottom */
130 switch(c->titlebar->position)
132 case Right:
133 tmp = ev->event_y;
134 ev->event_y = c->titlebar->height - ev->event_x;
135 ev->event_x = tmp;
136 break;
137 case Left:
138 tmp = ev->event_y;
139 ev->event_y = ev->event_x;
140 ev->event_x = c->titlebar->width - tmp;
141 break;
142 default:
143 break;
145 for(w = c->titlebar->widgets; w; w = w->next)
146 if(ev->event_x >= w->area.x && ev->event_x < w->area.x + w->area.width
147 && ev->event_y >= w->area.y && ev->event_y < w->area.y + w->area.height)
149 w->widget->button_press(w, ev, c->screen,
150 c->titlebar, AWESOME_TYPE_TITLEBAR);
151 return 0;
153 /* return if no widget match */
154 return 0;
157 if((c = client_getbywin(ev->event)))
159 event_handle_mouse_button_press(c, ev->detail, ev->state, c->buttons);
160 xcb_allow_events(globalconf.connection, XCB_ALLOW_REPLAY_POINTER, XCB_CURRENT_TIME);
162 else
163 for(screen = 0; screen < nb_screen; screen++)
164 if(xutil_screen_get(connection, screen)->root == ev->event)
166 event_handle_mouse_button_press(NULL, ev->detail, ev->state,
167 globalconf.buttons.root);
168 return 0;
171 return 0;
174 /** The configure event handler.
175 * \param data currently unused.
176 * \param connection The connection to the X server.
177 * \param ev The event.
179 static int
180 event_handle_configurerequest(void *data __attribute__ ((unused)),
181 xcb_connection_t *connection, xcb_configure_request_event_t *ev)
183 client_t *c;
184 area_t geometry;
186 if((c = client_getbywin(ev->window)))
188 geometry = c->geometry;
190 if(ev->value_mask & XCB_CONFIG_WINDOW_X)
191 geometry.x = ev->x;
192 if(ev->value_mask & XCB_CONFIG_WINDOW_Y)
193 geometry.y = ev->y;
194 if(ev->value_mask & XCB_CONFIG_WINDOW_WIDTH)
195 geometry.width = ev->width;
196 if(ev->value_mask & XCB_CONFIG_WINDOW_HEIGHT)
197 geometry.height = ev->height;
199 if(geometry.x != c->geometry.x || geometry.y != c->geometry.y
200 || geometry.width != c->geometry.width || geometry.height != c->geometry.height)
202 if(c->isfloating || layout_get_current(c->screen) == layout_floating)
204 client_resize(c, geometry, false);
205 titlebar_draw(c);
207 else
209 globalconf.screens[c->screen].need_arrange = true;
210 /* If we do not resize the client, at least tell it that it
211 * has its new configuration. That fixes at least
212 * gnome-terminal */
213 window_configure(c->win, c->geometry, c->border);
216 else
218 titlebar_update_geometry_floating(c);
219 window_configure(c->win, geometry, c->border);
222 else
224 uint16_t config_win_mask = 0;
225 uint32_t config_win_vals[7];
226 unsigned short i = 0;
228 if(ev->value_mask & XCB_CONFIG_WINDOW_X)
230 config_win_mask |= XCB_CONFIG_WINDOW_X;
231 config_win_vals[i++] = ev->x;
233 if(ev->value_mask & XCB_CONFIG_WINDOW_Y)
235 config_win_mask |= XCB_CONFIG_WINDOW_Y;
236 config_win_vals[i++] = ev->y;
238 if(ev->value_mask & XCB_CONFIG_WINDOW_WIDTH)
240 config_win_mask |= XCB_CONFIG_WINDOW_WIDTH;
241 config_win_vals[i++] = ev->width;
243 if(ev->value_mask & XCB_CONFIG_WINDOW_HEIGHT)
245 config_win_mask |= XCB_CONFIG_WINDOW_HEIGHT;
246 config_win_vals[i++] = ev->height;
248 if(ev->value_mask & XCB_CONFIG_WINDOW_BORDER_WIDTH)
250 config_win_mask |= XCB_CONFIG_WINDOW_BORDER_WIDTH;
251 config_win_vals[i++] = ev->border_width;
253 if(ev->value_mask & XCB_CONFIG_WINDOW_SIBLING)
255 config_win_mask |= XCB_CONFIG_WINDOW_SIBLING;
256 config_win_vals[i++] = ev->sibling;
258 if(ev->value_mask & XCB_CONFIG_WINDOW_STACK_MODE)
260 config_win_mask |= XCB_CONFIG_WINDOW_STACK_MODE;
261 config_win_vals[i++] = ev->stack_mode;
264 xcb_configure_window(connection, ev->window, config_win_mask, config_win_vals);
267 return 0;
270 /** The configure notify event handler.
271 * \param data currently unused.
272 * \param connection The connection to the X server.
273 * \param ev The event.
275 static int
276 event_handle_configurenotify(void *data __attribute__ ((unused)),
277 xcb_connection_t *connection, xcb_configure_notify_event_t *ev)
279 int screen_nbr;
280 const xcb_screen_t *screen;
282 for(screen_nbr = 0; screen_nbr < xcb_setup_roots_length(xcb_get_setup (connection)); screen_nbr++)
283 if((screen = xutil_screen_get(connection, screen_nbr)) != NULL
284 && ev->window == screen->root
285 && (ev->width != screen->width_in_pixels
286 || ev->height != screen->height_in_pixels))
287 /* it's not that we panic, but restart */
288 ewmh_restart();
290 return 0;
293 /** The destroy notify event handler.
294 * \param data currently unused.
295 * \param connection The connection to the X server.
296 * \param ev The event.
298 static int
299 event_handle_destroynotify(void *data __attribute__ ((unused)),
300 xcb_connection_t *connection __attribute__ ((unused)),
301 xcb_destroy_notify_event_t *ev)
303 client_t *c;
304 xembed_window_t *emwin;
305 int i;
307 if((c = client_getbywin(ev->window)))
308 client_unmanage(c);
309 else if((emwin = xembed_getbywin(globalconf.embedded, ev->event)))
311 xembed_window_list_detach(&globalconf.embedded, emwin);
312 for(i = 0; i < globalconf.screens_info->nscreen; i++)
313 widget_invalidate_cache(i, WIDGET_CACHE_EMBEDDED);
316 return 0;
319 /** The enter notify event handler.
320 * \param data currently unused.
321 * \param connection The connection to the X server.
322 * \param ev The event.
324 static int
325 event_handle_enternotify(void *data __attribute__ ((unused)),
326 xcb_connection_t *connection,
327 xcb_enter_notify_event_t *ev)
329 client_t *c;
330 xembed_window_t *emwin;
332 if(ev->mode != XCB_NOTIFY_MODE_NORMAL
333 || (ev->root_x == globalconf.pointer_x
334 && ev->root_y == globalconf.pointer_y))
335 return 0;
337 if((c = client_getbytitlebarwin(ev->event))
338 || (c = client_getbywin(ev->event)))
340 window_grabbuttons(c->win, ev->root, c->buttons);
341 /* The idea behind saving pointer_x and pointer_y is Bob Marley powered.
342 * this will allow us top drop some EnterNotify events and thus not giving
343 * focus to windows appering under the cursor without a cursor move */
344 globalconf.pointer_x = ev->root_x;
345 globalconf.pointer_y = ev->root_y;
347 luaA_client_userdata_new(globalconf.L, c);
348 luaA_dofunction(globalconf.L, globalconf.hooks.mouseover, 1, 0);
350 else if((emwin = xembed_getbywin(globalconf.embedded, ev->event)))
351 xcb_ungrab_button(globalconf.connection, XCB_BUTTON_INDEX_ANY,
352 xutil_screen_get(connection, emwin->phys_screen)->root,
353 XUTIL_ANY_MODIFIER);
354 else
355 window_root_grabbuttons(ev->root);
357 return 0;
360 /** The expose event handler.
361 * \param data currently unused.
362 * \param connection The connection to the X server.
363 * \param ev The event.
365 static int
366 event_handle_expose(void *data __attribute__ ((unused)),
367 xcb_connection_t *connection __attribute__ ((unused)),
368 xcb_expose_event_t *ev)
370 int screen;
371 statusbar_t *statusbar;
372 client_t *c;
374 if(!ev->count)
376 for(screen = 0; screen < globalconf.screens_info->nscreen; screen++)
377 for(statusbar = globalconf.screens[screen].statusbar; statusbar; statusbar = statusbar->next)
378 if(statusbar->sw
379 && statusbar->sw->window == ev->window)
381 simplewindow_refresh_pixmap(statusbar->sw);
382 return 0;
385 if((c = client_getbytitlebarwin(ev->window)))
386 simplewindow_refresh_pixmap(c->titlebar->sw);
389 return 0;
392 /** The key press event handler.
393 * \param data currently unused.
394 * \param connection The connection to the X server.
395 * \param ev The event.
397 static int
398 event_handle_keypress(void *data __attribute__ ((unused)),
399 xcb_connection_t *connection __attribute__ ((unused)),
400 xcb_key_press_event_t *ev)
402 if(globalconf.keygrabber != LUA_REFNIL)
404 lua_rawgeti(globalconf.L, LUA_REGISTRYINDEX, globalconf.keygrabber);
405 if(keygrabber_handlekpress(globalconf.L, ev))
407 if(lua_pcall(globalconf.L, 2, 1, 0))
409 warn("error running function: %s", lua_tostring(globalconf.L, -1));
410 keygrabber_ungrab();
412 else if(!lua_isboolean(globalconf.L, -1) || !lua_toboolean(globalconf.L, -1))
413 keygrabber_ungrab();
415 lua_pop(globalconf.L, 1); /* pop returned value or function if not called */
417 else
419 keybinding_t *k = keybinding_find(ev);
420 if (k && k->fct != LUA_REFNIL)
421 luaA_dofunction(globalconf.L, k->fct, 0, 0);
424 return 0;
427 /** The map request event handler.
428 * \param data currently unused.
429 * \param connection The connection to the X server.
430 * \param ev The event.
432 static int
433 event_handle_maprequest(void *data __attribute__ ((unused)),
434 xcb_connection_t *connection, xcb_map_request_event_t *ev)
436 int screen_nbr = 0, ret = 0;
437 client_t *c;
438 xcb_get_window_attributes_cookie_t wa_c;
439 xcb_get_window_attributes_reply_t *wa_r;
440 xcb_query_pointer_cookie_t qp_c = { 0 };
441 xcb_query_pointer_reply_t *qp_r = NULL;
442 xcb_get_geometry_cookie_t geom_c;
443 xcb_get_geometry_reply_t *geom_r;
444 xcb_screen_iterator_t iter;
446 wa_c = xcb_get_window_attributes_unchecked(connection, ev->window);
448 if(!(wa_r = xcb_get_window_attributes_reply(connection, wa_c, NULL)))
449 return -1;
451 if(wa_r->override_redirect)
452 goto bailout;
454 if(xembed_getbywin(globalconf.embedded, ev->window))
456 xcb_map_window(connection, ev->window);
457 xembed_window_activate(connection, ev->window);
459 else if((c = client_getbywin(ev->window)))
461 if(client_maybevisible(c, c->screen))
463 c->ishidden = false;
464 globalconf.screens[c->screen].need_arrange = true;
465 xcb_map_window(globalconf.connection, ev->window);
466 /* it will be raised, so just update ourself */
467 client_raise(c);
470 else
472 geom_c = xcb_get_geometry_unchecked(connection, ev->window);
474 if(globalconf.screens_info->xinerama_is_active)
475 qp_c = xcb_query_pointer_unchecked(connection, xutil_screen_get(globalconf.connection,
476 screen_nbr)->root);
478 if(!(geom_r = xcb_get_geometry_reply(connection, geom_c, NULL)))
480 if(globalconf.screens_info->xinerama_is_active)
482 qp_r = xcb_query_pointer_reply(connection, qp_c, NULL);
483 p_delete(&qp_r);
485 ret = -1;
486 goto bailout;
489 if(globalconf.screens_info->xinerama_is_active
490 && (qp_r = xcb_query_pointer_reply(connection, qp_c, NULL)))
492 screen_nbr = screen_get_bycoord(globalconf.screens_info, screen_nbr,
493 qp_r->root_x, qp_r->root_y);
494 p_delete(&qp_r);
496 else
497 for(iter = xcb_setup_roots_iterator(xcb_get_setup(connection)), screen_nbr = 0;
498 iter.rem && iter.data->root != geom_r->root; xcb_screen_next (&iter), ++screen_nbr);
500 client_manage(ev->window, geom_r, screen_nbr);
501 p_delete(&geom_r);
504 bailout:
505 p_delete(&wa_r);
506 return ret;
509 /** The property notify event handler.
510 * \param data currently unused.
511 * \param connection The connection to the X server.
512 * \param ev The event.
514 static int
515 event_handle_propertynotify(void *data __attribute__ ((unused)),
516 xcb_connection_t *connection, xcb_property_notify_event_t *ev)
518 client_t *c;
519 xcb_window_t trans;
520 xembed_window_t *emwin;
522 if(ev->state == XCB_PROPERTY_DELETE)
523 return 0; /* ignore */
524 else if((emwin = xembed_getbywin(globalconf.embedded, ev->window)))
525 xembed_property_update(connection, emwin);
526 else if((c = client_getbywin(ev->window)))
528 if(ev->atom == WM_TRANSIENT_FOR)
530 xcb_get_wm_transient_for(connection, c->win, &trans);
531 if(!c->isfloating
532 && (c->isfloating = (client_getbywin(trans) != NULL)))
533 globalconf.screens[c->screen].need_arrange = true;
535 else if (ev->atom == WM_NORMAL_HINTS)
536 xcb_free_size_hints(client_updatesizehints(c));
537 else if (ev->atom == WM_HINTS)
538 client_updatewmhints(c);
539 else if(ev->atom == WM_NAME || ev->atom == _NET_WM_NAME)
540 client_updatetitle(c);
541 else if(ev->atom == _NET_WM_ICON)
543 netwm_icon_delete(&c->icon);
544 c->icon = ewmh_window_icon_get(c->win);
545 widget_invalidate_cache(c->screen, WIDGET_CACHE_CLIENTS);
549 return 0;
552 /** The unmap notify event handler.
553 * \param data currently unused.
554 * \param connection The connection to the X server.
555 * \param ev The event.
557 static int
558 event_handle_unmapnotify(void *data __attribute__ ((unused)),
559 xcb_connection_t *connection, xcb_unmap_notify_event_t *ev)
561 client_t *c;
562 xembed_window_t *em;
563 int i;
565 /* event->send_event (Xlib) is quivalent to (ev->response_type &
566 * 0x80) in XCB because the SendEvent bit is available in the
567 * response_type field */
568 bool send_event = ((ev->response_type & 0x80) >> 7);
570 if((c = client_getbywin(ev->window)))
572 if(ev->event == xutil_screen_get(connection, c->phys_screen)->root
573 && send_event
574 && window_getstate(c->win) == XCB_WM_NORMAL_STATE)
575 client_unmanage(c);
577 else if((em = xembed_getbywin(globalconf.embedded, ev->window)))
579 xembed_window_list_detach(&globalconf.embedded, em);
580 for(i = 0; i < globalconf.screens_info->nscreen; i++)
581 widget_invalidate_cache(i, WIDGET_CACHE_EMBEDDED);
584 return 0;
587 /** The randr screen change notify event handler.
588 * \param data currently unused.
589 * \param connection The connection to the X server.
590 * \param ev The event.
592 static int
593 event_handle_randr_screen_change_notify(void *data __attribute__ ((unused)),
594 xcb_connection_t *connection __attribute__ ((unused)),
595 xcb_randr_screen_change_notify_event_t *ev)
597 if(!globalconf.have_randr)
598 return -1;
600 /* Code of XRRUpdateConfiguration Xlib function ported to XCB
601 * (only the code relevant to RRScreenChangeNotify) as the latter
602 * doesn't provide this kind of function */
603 if(ev->rotation & (XCB_RANDR_ROTATION_ROTATE_90 | XCB_RANDR_ROTATION_ROTATE_270))
604 xcb_randr_set_screen_size(connection, ev->root, ev->height, ev->width,
605 ev->mheight, ev->mwidth);
606 else
607 xcb_randr_set_screen_size(connection, ev->root, ev->width, ev->height,
608 ev->mwidth, ev->mheight);
610 /* XRRUpdateConfiguration also executes the following instruction
611 * but it's not useful because SubpixelOrder is not used at all at
612 * the moment
614 * XRenderSetSubpixelOrder(dpy, snum, scevent->subpixel_order);
617 ewmh_restart();
619 return 0;
622 /** The client message event handler.
623 * \param data currently unused.
624 * \param connection The connection to the X server.
625 * \param ev The event.
627 static int
628 event_handle_clientmessage(void *data __attribute__ ((unused)),
629 xcb_connection_t *connection,
630 xcb_client_message_event_t *ev)
632 client_t *c;
634 if(ev->type == WM_CHANGE_STATE)
636 if((c = client_getbywin(ev->window))
637 && ev->format == 32
638 && ev->data.data32[0] == XCB_WM_ICONIC_STATE)
640 c->ishidden = true;
641 globalconf.screens[c->screen].need_arrange = true;
644 else if(ev->type == _XEMBED)
645 return xembed_process_client_message(ev);
646 else if(ev->type == _NET_SYSTEM_TRAY_OPCODE)
647 return systray_process_client_message(ev);
648 return ewmh_process_client_message(ev);
651 void a_xcb_set_event_handlers(void)
653 const xcb_query_extension_reply_t *randr_query;
655 set_button_press_event_handler(globalconf.evenths, event_handle_buttonpress, NULL);
656 set_configure_request_event_handler(globalconf.evenths, event_handle_configurerequest, NULL);
657 set_configure_notify_event_handler(globalconf.evenths, event_handle_configurenotify, NULL);
658 set_destroy_notify_event_handler(globalconf.evenths, event_handle_destroynotify, NULL);
659 set_enter_notify_event_handler(globalconf.evenths, event_handle_enternotify, NULL);
660 set_expose_event_handler(globalconf.evenths, event_handle_expose, NULL);
661 set_key_press_event_handler(globalconf.evenths, event_handle_keypress, NULL);
662 set_map_request_event_handler(globalconf.evenths, event_handle_maprequest, NULL);
663 set_property_notify_event_handler(globalconf.evenths, event_handle_propertynotify, NULL);
664 set_unmap_notify_event_handler(globalconf.evenths, event_handle_unmapnotify, NULL);
665 set_client_message_event_handler(globalconf.evenths, event_handle_clientmessage, NULL);
667 /* check for randr extension */
668 randr_query = xcb_get_extension_data(globalconf.connection, &xcb_randr_id);
669 if((globalconf.have_randr = randr_query->present))
670 xcb_set_event_handler(globalconf.evenths,
671 (randr_query->first_event + XCB_RANDR_SCREEN_CHANGE_NOTIFY),
672 (xcb_generic_event_handler_t) event_handle_randr_screen_change_notify,
673 NULL);
677 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80