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.
23 #include <xcb/xcb_atom.h>
24 #include <xcb/randr.h>
25 #include <xcb/xcb_icccm.h>
34 #include "keybinding.h"
35 #include "keygrabber.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
50 event_handle_mouse_button_press(client_t
*c
,
57 for(b
= buttons
; b
; b
= b
->next
)
58 if(button
== b
->button
&& XUTIL_MASK_CLEAN(state
) == b
->mod
&& b
->fct
)
62 luaA_client_userdata_new(globalconf
.L
, c
);
63 luaA_dofunction(globalconf
.L
, b
->fct
, 1, 0);
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.
76 event_handle_buttonpress(void *data
__attribute__ ((unused
)),
77 xcb_connection_t
*connection
, xcb_button_press_event_t
*ev
)
80 const int nb_screen
= xcb_setup_roots_length(xcb_get_setup(connection
));
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
)
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
99 switch(statusbar
->position
)
103 ev
->event_y
= statusbar
->height
- ev
->event_x
;
108 ev
->event_y
= ev
->event_x
;
109 ev
->event_x
= statusbar
->width
- tmp
;
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
);
122 /* return if no widget match */
126 if((c
= client_getbytitlebarwin(ev
->event
)))
128 /* Need to transform coordinates like it was
130 switch(c
->titlebar
->position
)
134 ev
->event_y
= c
->titlebar
->height
- ev
->event_x
;
139 ev
->event_y
= ev
->event_x
;
140 ev
->event_x
= c
->titlebar
->width
- tmp
;
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
);
153 /* return if no widget match */
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
);
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
);
174 /** The configure event handler.
175 * \param data currently unused.
176 * \param connection The connection to the X server.
177 * \param ev The event.
180 event_handle_configurerequest(void *data
__attribute__ ((unused
)),
181 xcb_connection_t
*connection
, xcb_configure_request_event_t
*ev
)
186 if((c
= client_getbywin(ev
->window
)))
188 geometry
= c
->geometry
;
190 if(ev
->value_mask
& XCB_CONFIG_WINDOW_X
)
192 if(ev
->value_mask
& XCB_CONFIG_WINDOW_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);
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
213 window_configure(c
->win
, c
->geometry
, c
->border
);
218 titlebar_update_geometry_floating(c
);
219 window_configure(c
->win
, geometry
, c
->border
);
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
);
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.
276 event_handle_configurenotify(void *data
__attribute__ ((unused
)),
277 xcb_connection_t
*connection
, xcb_configure_notify_event_t
*ev
)
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 */
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.
299 event_handle_destroynotify(void *data
__attribute__ ((unused
)),
300 xcb_connection_t
*connection
__attribute__ ((unused
)),
301 xcb_destroy_notify_event_t
*ev
)
304 xembed_window_t
*emwin
;
307 if((c
= client_getbywin(ev
->window
)))
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
);
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.
325 event_handle_enternotify(void *data
__attribute__ ((unused
)),
326 xcb_connection_t
*connection
,
327 xcb_enter_notify_event_t
*ev
)
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
))
337 if((c
= client_getbytitlebarwin(ev
->event
))
338 || (c
= client_getbywin(ev
->event
)))
340 window_buttons_grab(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
,
355 window_root_buttons_grab(ev
->root
);
360 /** The expose event handler.
361 * \param data currently unused.
362 * \param connection The connection to the X server.
363 * \param ev The event.
366 event_handle_expose(void *data
__attribute__ ((unused
)),
367 xcb_connection_t
*connection
__attribute__ ((unused
)),
368 xcb_expose_event_t
*ev
)
371 statusbar_t
*statusbar
;
376 for(screen
= 0; screen
< globalconf
.screens_info
->nscreen
; screen
++)
377 for(statusbar
= globalconf
.screens
[screen
].statusbar
; statusbar
; statusbar
= statusbar
->next
)
379 && statusbar
->sw
->window
== ev
->window
)
381 simplewindow_refresh_pixmap(statusbar
->sw
);
385 if((c
= client_getbytitlebarwin(ev
->window
)))
386 simplewindow_refresh_pixmap(c
->titlebar
->sw
);
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.
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));
412 else if(!lua_isboolean(globalconf
.L
, -1) || !lua_toboolean(globalconf
.L
, -1))
415 lua_pop(globalconf
.L
, 1); /* pop returned value or function if not called */
419 keybinding_t
*k
= keybinding_find(ev
);
420 if (k
&& k
->fct
!= LUA_REFNIL
)
421 luaA_dofunction(globalconf
.L
, k
->fct
, 0, 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.
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;
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
)))
451 if(wa_r
->override_redirect
)
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
))
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 */
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
,
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
);
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
);
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
);
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.
515 event_handle_propertynotify(void *data
__attribute__ ((unused
)),
516 xcb_connection_t
*connection
, xcb_property_notify_event_t
*ev
)
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
);
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 xcb_get_property_cookie_t icon_q
= ewmh_window_icon_get_unchecked(c
->win
);
544 netwm_icon_delete(&c
->icon
);
545 widget_invalidate_cache(c
->screen
, WIDGET_CACHE_CLIENTS
);
546 c
->icon
= ewmh_window_icon_get_reply(icon_q
);
553 /** The unmap notify event handler.
554 * \param data currently unused.
555 * \param connection The connection to the X server.
556 * \param ev The event.
559 event_handle_unmapnotify(void *data
__attribute__ ((unused
)),
560 xcb_connection_t
*connection
, xcb_unmap_notify_event_t
*ev
)
566 /* event->send_event (Xlib) is quivalent to (ev->response_type &
567 * 0x80) in XCB because the SendEvent bit is available in the
568 * response_type field */
569 bool send_event
= ((ev
->response_type
& 0x80) >> 7);
571 if((c
= client_getbywin(ev
->window
)))
573 if(ev
->event
== xutil_screen_get(connection
, c
->phys_screen
)->root
575 && window_state_get_reply(window_state_get_unchecked(c
->win
)) == XCB_WM_NORMAL_STATE
)
578 else if((em
= xembed_getbywin(globalconf
.embedded
, ev
->window
)))
580 xembed_window_list_detach(&globalconf
.embedded
, em
);
581 for(i
= 0; i
< globalconf
.screens_info
->nscreen
; i
++)
582 widget_invalidate_cache(i
, WIDGET_CACHE_EMBEDDED
);
588 /** The randr screen change notify event handler.
589 * \param data currently unused.
590 * \param connection The connection to the X server.
591 * \param ev The event.
594 event_handle_randr_screen_change_notify(void *data
__attribute__ ((unused
)),
595 xcb_connection_t
*connection
__attribute__ ((unused
)),
596 xcb_randr_screen_change_notify_event_t
*ev
)
598 if(!globalconf
.have_randr
)
601 /* Code of XRRUpdateConfiguration Xlib function ported to XCB
602 * (only the code relevant to RRScreenChangeNotify) as the latter
603 * doesn't provide this kind of function */
604 if(ev
->rotation
& (XCB_RANDR_ROTATION_ROTATE_90
| XCB_RANDR_ROTATION_ROTATE_270
))
605 xcb_randr_set_screen_size(connection
, ev
->root
, ev
->height
, ev
->width
,
606 ev
->mheight
, ev
->mwidth
);
608 xcb_randr_set_screen_size(connection
, ev
->root
, ev
->width
, ev
->height
,
609 ev
->mwidth
, ev
->mheight
);
611 /* XRRUpdateConfiguration also executes the following instruction
612 * but it's not useful because SubpixelOrder is not used at all at
615 * XRenderSetSubpixelOrder(dpy, snum, scevent->subpixel_order);
623 /** The client message event handler.
624 * \param data currently unused.
625 * \param connection The connection to the X server.
626 * \param ev The event.
629 event_handle_clientmessage(void *data
__attribute__ ((unused
)),
630 xcb_connection_t
*connection
,
631 xcb_client_message_event_t
*ev
)
635 if(ev
->type
== WM_CHANGE_STATE
)
637 if((c
= client_getbywin(ev
->window
))
639 && ev
->data
.data32
[0] == XCB_WM_ICONIC_STATE
)
642 globalconf
.screens
[c
->screen
].need_arrange
= true;
645 else if(ev
->type
== _XEMBED
)
646 return xembed_process_client_message(ev
);
647 else if(ev
->type
== _NET_SYSTEM_TRAY_OPCODE
)
648 return systray_process_client_message(ev
);
649 return ewmh_process_client_message(ev
);
652 void a_xcb_set_event_handlers(void)
654 const xcb_query_extension_reply_t
*randr_query
;
656 set_button_press_event_handler(globalconf
.evenths
, event_handle_buttonpress
, NULL
);
657 set_configure_request_event_handler(globalconf
.evenths
, event_handle_configurerequest
, NULL
);
658 set_configure_notify_event_handler(globalconf
.evenths
, event_handle_configurenotify
, NULL
);
659 set_destroy_notify_event_handler(globalconf
.evenths
, event_handle_destroynotify
, NULL
);
660 set_enter_notify_event_handler(globalconf
.evenths
, event_handle_enternotify
, NULL
);
661 set_expose_event_handler(globalconf
.evenths
, event_handle_expose
, NULL
);
662 set_key_press_event_handler(globalconf
.evenths
, event_handle_keypress
, NULL
);
663 set_map_request_event_handler(globalconf
.evenths
, event_handle_maprequest
, NULL
);
664 set_property_notify_event_handler(globalconf
.evenths
, event_handle_propertynotify
, NULL
);
665 set_unmap_notify_event_handler(globalconf
.evenths
, event_handle_unmapnotify
, NULL
);
666 set_client_message_event_handler(globalconf
.evenths
, event_handle_clientmessage
, NULL
);
668 /* check for randr extension */
669 randr_query
= xcb_get_extension_data(globalconf
.connection
, &xcb_randr_id
);
670 if((globalconf
.have_randr
= randr_query
->present
))
671 xcb_set_event_handler(globalconf
.evenths
,
672 (randr_query
->first_event
+ XCB_RANDR_SCREEN_CHANGE_NOTIFY
),
673 (xcb_generic_event_handler_t
) event_handle_randr_screen_change_notify
,
678 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80