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>
29 #include "statusbar.h"
36 #include "keygrabber.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
52 event_handle_mouse_button_press(client_t
*c
,
59 for(b
= buttons
; b
; b
= b
->next
)
60 if(button
== b
->button
&& CLEANMASK(state
) == b
->mod
&& b
->fct
)
64 luaA_client_userdata_new(globalconf
.L
, c
);
65 luaA_dofunction(globalconf
.L
, b
->fct
, 1);
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.
78 event_handle_buttonpress(void *data
__attribute__ ((unused
)),
79 xcb_connection_t
*connection
, xcb_button_press_event_t
*ev
)
82 const int nb_screen
= xcb_setup_roots_length(xcb_get_setup(connection
));
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
)
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
101 switch(statusbar
->position
)
105 ev
->event_y
= statusbar
->height
- ev
->event_x
;
110 ev
->event_y
= ev
->event_x
;
111 ev
->event_x
= statusbar
->width
- tmp
;
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
);
124 /* return if no widget match */
128 if((c
= client_getbytitlebarwin(ev
->event
)))
130 /* Need to transform coordinates like it was
132 switch(c
->titlebar
->position
)
136 ev
->event_y
= c
->titlebar
->height
- ev
->event_x
;
141 ev
->event_y
= ev
->event_x
;
142 ev
->event_x
= c
->titlebar
->width
- tmp
;
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
);
155 /* return if no widget match */
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
);
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
);
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
)
188 if((c
= client_getbywin(ev
->window
)))
190 geometry
= c
->geometry
;
192 if(ev
->value_mask
& XCB_CONFIG_WINDOW_X
)
194 if(ev
->value_mask
& XCB_CONFIG_WINDOW_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);
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
212 window_configure(c
->win
, c
->geometry
, c
->border
);
217 titlebar_update_geometry_floating(c
);
218 window_configure(c
->win
, geometry
, c
->border
);
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
);
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
)
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
);
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
)
304 if((c
= client_getbywin(ev
->window
)))
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
)
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
))
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
,
346 window_root_grabbuttons(ev
->root
);
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
)
362 statusbar_t
*statusbar
;
367 for(screen
= 0; screen
< globalconf
.screens_info
->nscreen
; screen
++)
368 for(statusbar
= globalconf
.screens
[screen
].statusbar
; statusbar
; statusbar
= statusbar
->next
)
370 && statusbar
->sw
->window
== ev
->window
)
372 simplewindow_refresh_pixmap(statusbar
->sw
);
376 if((c
= client_getbytitlebarwin(ev
->window
)))
377 simplewindow_refresh_pixmap(c
->titlebar
->sw
);
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
)
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));
406 else if(!lua_isboolean(globalconf
.L
, -1) || !lua_toboolean(globalconf
.L
, -1))
409 lua_pop(globalconf
.L
, 1); /* pop returned value or function if not called */
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);
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
)
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
)))
448 if(wa_r
->override_redirect
)
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
,
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
);
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
);
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
);
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
)
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
);
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
,
530 client_updatetitle(c
);
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
)
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
)
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
);
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
)
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
);
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
597 * XRenderSetSubpixelOrder(dpy, snum, scevent->subpixel_order);
600 a_exec(globalconf
.argv
);
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