Mirror of Awesome WM window manager
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

event.c 38KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165
  1. /*
  2. * event.c - event handlers
  3. *
  4. * Copyright © 2007-2008 Julien Danjou <julien@danjou.info>
  5. *
  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.
  10. *
  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.
  15. *
  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.
  19. *
  20. */
  21. #include "event.h"
  22. #include "awesome.h"
  23. #include "property.h"
  24. #include "objects/tag.h"
  25. #include "objects/selection_getter.h"
  26. #include "objects/drawin.h"
  27. #include "objects/selection_acquire.h"
  28. #include "objects/selection_watcher.h"
  29. #include "xwindow.h"
  30. #include "ewmh.h"
  31. #include "objects/client.h"
  32. #include "keygrabber.h"
  33. #include "mousegrabber.h"
  34. #include "luaa.h"
  35. #include "systray.h"
  36. #include "xkb.h"
  37. #include "objects/screen.h"
  38. #include "common/atoms.h"
  39. #include "common/xutil.h"
  40. #include <xcb/xcb.h>
  41. #include <xcb/randr.h>
  42. #include <xcb/shape.h>
  43. #include <xcb/xcb_atom.h>
  44. #include <xcb/xcb_icccm.h>
  45. #include <xcb/xcb_event.h>
  46. #include <xcb/xkb.h>
  47. #include <xcb/xfixes.h>
  48. #define DO_EVENT_HOOK_CALLBACK(type, xcbtype, xcbeventprefix, arraytype, match) \
  49. static void \
  50. event_##xcbtype##_callback(xcb_##xcbtype##_press_event_t *ev, \
  51. arraytype *arr, \
  52. lua_State *L, \
  53. int oud, \
  54. int nargs, \
  55. void *data) \
  56. { \
  57. int abs_oud = oud < 0 ? ((lua_gettop(L) + 1) + oud) : oud; \
  58. int item_matching = 0; \
  59. foreach(item, *arr) \
  60. if(match(ev, *item, data)) \
  61. { \
  62. if(oud) \
  63. luaA_object_push_item(L, abs_oud, *item); \
  64. else \
  65. luaA_object_push(L, *item); \
  66. item_matching++; \
  67. } \
  68. for(; item_matching > 0; item_matching--) \
  69. { \
  70. switch(ev->response_type) \
  71. { \
  72. case xcbeventprefix##_PRESS: \
  73. for(int i = 0; i < nargs; i++) \
  74. lua_pushvalue(L, - nargs - item_matching); \
  75. luaA_object_emit_signal(L, - nargs - 1, "press", nargs); \
  76. break; \
  77. case xcbeventprefix##_RELEASE: \
  78. for(int i = 0; i < nargs; i++) \
  79. lua_pushvalue(L, - nargs - item_matching); \
  80. luaA_object_emit_signal(L, - nargs - 1, "release", nargs); \
  81. break; \
  82. } \
  83. lua_pop(L, 1); \
  84. } \
  85. lua_pop(L, nargs); \
  86. }
  87. static bool
  88. event_key_match(xcb_key_press_event_t *ev, keyb_t *k, void *data)
  89. {
  90. assert(data);
  91. xcb_keysym_t keysym = *(xcb_keysym_t *) data;
  92. return (((k->keycode && ev->detail == k->keycode)
  93. || (k->keysym && keysym == k->keysym))
  94. && (k->modifiers == XCB_BUTTON_MASK_ANY || k->modifiers == ev->state));
  95. }
  96. static bool
  97. event_button_match(xcb_button_press_event_t *ev, button_t *b, void *data)
  98. {
  99. return ((!b->button || ev->detail == b->button)
  100. && (b->modifiers == XCB_BUTTON_MASK_ANY || b->modifiers == ev->state));
  101. }
  102. DO_EVENT_HOOK_CALLBACK(button_t, button, XCB_BUTTON, button_array_t, event_button_match)
  103. DO_EVENT_HOOK_CALLBACK(keyb_t, key, XCB_KEY, key_array_t, event_key_match)
  104. /** Handle an event with mouse grabber if needed
  105. * \param x The x coordinate.
  106. * \param y The y coordinate.
  107. * \param mask The mask buttons.
  108. * \return True if the event was handled.
  109. */
  110. static bool
  111. event_handle_mousegrabber(int x, int y, uint16_t mask)
  112. {
  113. if(globalconf.mousegrabber != LUA_REFNIL)
  114. {
  115. lua_State *L = globalconf_get_lua_State();
  116. mousegrabber_handleevent(L, x, y, mask);
  117. lua_rawgeti(L, LUA_REGISTRYINDEX, globalconf.mousegrabber);
  118. if(!luaA_dofunction(L, 1, 1))
  119. {
  120. warn("Stopping mousegrabber.");
  121. luaA_mousegrabber_stop(L);
  122. } else {
  123. if(!lua_isboolean(L, -1) || !lua_toboolean(L, -1))
  124. luaA_mousegrabber_stop(L);
  125. lua_pop(L, 1); /* pop returned value */
  126. }
  127. return true;
  128. }
  129. return false;
  130. }
  131. /** Emit a button signal.
  132. * The top of the lua stack has to be the object on which to emit the event.
  133. * \param L The Lua VM state.
  134. * \param ev The event to handle.
  135. */
  136. static void
  137. event_emit_button(lua_State *L, xcb_button_press_event_t *ev)
  138. {
  139. const char *name;
  140. switch(XCB_EVENT_RESPONSE_TYPE(ev))
  141. {
  142. case XCB_BUTTON_PRESS:
  143. name = "button::press";
  144. break;
  145. case XCB_BUTTON_RELEASE:
  146. name = "button::release";
  147. break;
  148. default:
  149. fatal("Invalid event type");
  150. }
  151. /* Push the event's info */
  152. lua_pushinteger(L, ev->event_x);
  153. lua_pushinteger(L, ev->event_y);
  154. lua_pushinteger(L, ev->detail);
  155. luaA_pushmodifiers(L, ev->state);
  156. /* And emit the signal */
  157. luaA_object_emit_signal(L, -5, name, 4);
  158. }
  159. /** The button press event handler.
  160. * \param ev The event.
  161. */
  162. static void
  163. event_handle_button(xcb_button_press_event_t *ev)
  164. {
  165. lua_State *L = globalconf_get_lua_State();
  166. client_t *c;
  167. drawin_t *drawin;
  168. globalconf.timestamp = ev->time;
  169. {
  170. /* ev->state contains the state before the event. Compute the state
  171. * after the event for the mousegrabber.
  172. */
  173. uint16_t state = ev->state, change = 1 << (ev->detail - 1 + 8);
  174. if (XCB_EVENT_RESPONSE_TYPE(ev) == XCB_BUTTON_PRESS)
  175. state |= change;
  176. else
  177. state &= ~change;
  178. if(event_handle_mousegrabber(ev->root_x, ev->root_y, state))
  179. return;
  180. }
  181. /* ev->state is
  182. * button status (8 bits) + modifiers status (8 bits)
  183. * we don't care for button status that we get, especially on release, so
  184. * drop them */
  185. ev->state &= 0x00ff;
  186. if((drawin = drawin_getbywin(ev->event))
  187. || (drawin = drawin_getbywin(ev->child)))
  188. {
  189. /* If the drawin is child, then x,y are
  190. * relative to root window */
  191. if(drawin->window == ev->child)
  192. {
  193. ev->event_x -= drawin->geometry.x + drawin->border_width;
  194. ev->event_y -= drawin->geometry.y + drawin->border_width;
  195. }
  196. /* Push the drawable */
  197. luaA_object_push(L, drawin);
  198. luaA_object_push_item(L, -1, drawin->drawable);
  199. /* and handle the button raw button event */
  200. event_emit_button(L, ev);
  201. lua_pop(L, 1);
  202. /* check if any button object matches */
  203. event_button_callback(ev, &drawin->buttons, L, -1, 1, NULL);
  204. /* Either we are receiving this due to ButtonPress/Release on the root
  205. * window or because we grabbed the button on the window. In the later
  206. * case we have to call AllowEvents.
  207. * Use AsyncPointer instead of ReplayPointer so that the event is
  208. * "eaten" instead of being handled again on the root window.
  209. */
  210. if(ev->child == XCB_NONE)
  211. xcb_allow_events(globalconf.connection,
  212. XCB_ALLOW_ASYNC_POINTER,
  213. ev->time);
  214. }
  215. else if((c = client_getbyframewin(ev->event)) || (c = client_getbywin(ev->event)))
  216. {
  217. /* For clicks inside of c->window, we get two events. Once because of a
  218. * passive grab on c->window and then again for c->frame_window.
  219. * Ignore the second event (identifiable by ev->child != XCB_NONE).
  220. */
  221. if (ev->event != c->frame_window || ev->child == XCB_NONE)
  222. {
  223. luaA_object_push(L, c);
  224. if (c->window == ev->event)
  225. {
  226. /* Button event into the client itself (not titlebar), translate
  227. * into the frame window.
  228. */
  229. ev->event_x += c->titlebar[CLIENT_TITLEBAR_LEFT].size;
  230. ev->event_y += c->titlebar[CLIENT_TITLEBAR_TOP].size;
  231. }
  232. /* And handle the button raw button event */
  233. event_emit_button(L, ev);
  234. /* then check if a titlebar was "hit" */
  235. if (c->frame_window == ev->event)
  236. {
  237. int x = ev->event_x, y = ev->event_y;
  238. drawable_t *d = client_get_drawable_offset(c, &x, &y);
  239. if (d)
  240. {
  241. /* Copy the event so that we can fake x/y */
  242. xcb_button_press_event_t event = *ev;
  243. event.event_x = x;
  244. event.event_y = y;
  245. luaA_object_push_item(L, -1, d);
  246. event_emit_button(L, &event);
  247. lua_pop(L, 1);
  248. }
  249. }
  250. /* then check if any button objects match */
  251. event_button_callback(ev, &c->buttons, L, -1, 1, NULL);
  252. }
  253. xcb_allow_events(globalconf.connection,
  254. XCB_ALLOW_REPLAY_POINTER,
  255. ev->time);
  256. }
  257. else if(ev->child == XCB_NONE)
  258. if(globalconf.screen->root == ev->event)
  259. {
  260. event_button_callback(ev, &globalconf.buttons, L, 0, 0, NULL);
  261. return;
  262. }
  263. }
  264. static void
  265. event_handle_configurerequest_configure_window(xcb_configure_request_event_t *ev)
  266. {
  267. uint16_t config_win_mask = 0;
  268. uint32_t config_win_vals[7];
  269. unsigned short i = 0;
  270. if(ev->value_mask & XCB_CONFIG_WINDOW_X)
  271. {
  272. config_win_mask |= XCB_CONFIG_WINDOW_X;
  273. config_win_vals[i++] = ev->x;
  274. }
  275. if(ev->value_mask & XCB_CONFIG_WINDOW_Y)
  276. {
  277. config_win_mask |= XCB_CONFIG_WINDOW_Y;
  278. config_win_vals[i++] = ev->y;
  279. }
  280. if(ev->value_mask & XCB_CONFIG_WINDOW_WIDTH)
  281. {
  282. config_win_mask |= XCB_CONFIG_WINDOW_WIDTH;
  283. config_win_vals[i++] = ev->width;
  284. }
  285. if(ev->value_mask & XCB_CONFIG_WINDOW_HEIGHT)
  286. {
  287. config_win_mask |= XCB_CONFIG_WINDOW_HEIGHT;
  288. config_win_vals[i++] = ev->height;
  289. }
  290. if(ev->value_mask & XCB_CONFIG_WINDOW_BORDER_WIDTH)
  291. {
  292. config_win_mask |= XCB_CONFIG_WINDOW_BORDER_WIDTH;
  293. config_win_vals[i++] = ev->border_width;
  294. }
  295. if(ev->value_mask & XCB_CONFIG_WINDOW_SIBLING)
  296. {
  297. config_win_mask |= XCB_CONFIG_WINDOW_SIBLING;
  298. config_win_vals[i++] = ev->sibling;
  299. }
  300. if(ev->value_mask & XCB_CONFIG_WINDOW_STACK_MODE)
  301. {
  302. config_win_mask |= XCB_CONFIG_WINDOW_STACK_MODE;
  303. config_win_vals[i++] = ev->stack_mode;
  304. }
  305. xcb_configure_window(globalconf.connection, ev->window, config_win_mask, config_win_vals);
  306. }
  307. /** The configure event handler.
  308. * \param ev The event.
  309. */
  310. static void
  311. event_handle_configurerequest(xcb_configure_request_event_t *ev)
  312. {
  313. client_t *c;
  314. if((c = client_getbywin(ev->window)))
  315. {
  316. lua_State *L = globalconf_get_lua_State();
  317. if(ev->value_mask & XCB_CONFIG_WINDOW_BORDER_WIDTH)
  318. {
  319. luaA_object_push(L, c);
  320. window_set_border_width(L, -1, ev->border_width);
  321. lua_pop(L, 1);
  322. }
  323. area_t geometry = c->geometry;
  324. uint16_t bw = c->border_width;
  325. uint16_t tb_left = c->titlebar[CLIENT_TITLEBAR_LEFT].size;
  326. uint16_t tb_right = c->titlebar[CLIENT_TITLEBAR_RIGHT].size;
  327. uint16_t tb_top = c->titlebar[CLIENT_TITLEBAR_TOP].size;
  328. uint16_t tb_bottom = c->titlebar[CLIENT_TITLEBAR_BOTTOM].size;
  329. uint16_t deco_left = bw + tb_left;
  330. uint16_t deco_right = bw + tb_right;
  331. uint16_t deco_top = bw + tb_top;
  332. uint16_t deco_bottom = bw + tb_bottom;
  333. int16_t diff_w = 0, diff_h = 0;
  334. if(ev->value_mask & XCB_CONFIG_WINDOW_WIDTH)
  335. {
  336. uint16_t old_w = geometry.width;
  337. geometry.width = ev->width;
  338. /* The ConfigureRequest specifies the size of the client window, we want the frame */
  339. geometry.width += tb_left + tb_right;
  340. diff_w = geometry.width - old_w;
  341. }
  342. if(ev->value_mask & XCB_CONFIG_WINDOW_HEIGHT)
  343. {
  344. uint16_t old_h = geometry.height;
  345. geometry.height = ev->height;
  346. /* The ConfigureRequest specifies the size of the client window, we want the frame */
  347. geometry.height += tb_top + tb_bottom;
  348. diff_h = geometry.height - old_h;
  349. }
  350. /* If the client resizes without moving itself, apply window gravity */
  351. if(c->size_hints.flags & XCB_ICCCM_SIZE_HINT_P_WIN_GRAVITY)
  352. {
  353. xwindow_translate_for_gravity(c->size_hints.win_gravity, 0, 0, diff_w, diff_h, &geometry.x, &geometry.y);
  354. }
  355. if(ev->value_mask & XCB_CONFIG_WINDOW_X)
  356. {
  357. geometry.x = ev->x;
  358. if(c->size_hints.flags & XCB_ICCCM_SIZE_HINT_P_WIN_GRAVITY)
  359. xwindow_translate_for_gravity(c->size_hints.win_gravity, deco_left, 0, deco_right, 0, &geometry.x, NULL);
  360. }
  361. if(ev->value_mask & XCB_CONFIG_WINDOW_Y)
  362. {
  363. geometry.y = ev->y;
  364. if(c->size_hints.flags & XCB_ICCCM_SIZE_HINT_P_WIN_GRAVITY)
  365. xwindow_translate_for_gravity(c->size_hints.win_gravity, 0, deco_top, 0, deco_bottom, NULL, &geometry.y);
  366. }
  367. c->got_configure_request = true;
  368. /* Request the changes to be applied */
  369. luaA_object_push(L, c);
  370. lua_pushstring(L, "ewmh"); /* context */
  371. lua_newtable(L); /* props */
  372. /* area, it needs to be directly in the `hints` table to comply with
  373. the "protocol"
  374. */
  375. lua_pushstring(L, "x");
  376. lua_pushinteger(L, geometry.x);
  377. lua_rawset(L, -3);
  378. lua_pushstring(L, "y");
  379. lua_pushinteger(L, geometry.y);
  380. lua_rawset(L, -3);
  381. lua_pushstring(L, "width");
  382. lua_pushinteger(L, geometry.width);
  383. lua_rawset(L, -3);
  384. lua_pushstring(L, "height");
  385. lua_pushinteger(L, geometry.height);
  386. lua_rawset(L, -3);
  387. luaA_object_emit_signal(L, -3, "request::geometry", 2);
  388. lua_pop(L, 1);
  389. }
  390. else if (xembed_getbywin(&globalconf.embedded, ev->window))
  391. {
  392. /* Ignore this so that systray icons cannot resize themselves.
  393. * We decide their size!
  394. * However, Xembed says that we act like a WM to the embedded window and
  395. * thus we have to send a synthetic configure notify informing the
  396. * window that its configure request was denied.
  397. */
  398. xcb_get_geometry_cookie_t geom_cookie =
  399. xcb_get_geometry_unchecked(globalconf.connection, ev->window);
  400. xcb_translate_coordinates_cookie_t coords_cookie =
  401. xcb_translate_coordinates_unchecked(globalconf.connection,
  402. ev->window, globalconf.screen->root, 0, 0);
  403. xcb_get_geometry_reply_t *geom =
  404. xcb_get_geometry_reply(globalconf.connection, geom_cookie, NULL);
  405. xcb_translate_coordinates_reply_t *coords =
  406. xcb_translate_coordinates_reply(globalconf.connection, coords_cookie, NULL);
  407. if (geom && coords)
  408. {
  409. xwindow_configure(ev->window,
  410. (area_t) { .x = coords->dst_x,
  411. .y = coords->dst_y,
  412. .width = geom->width,
  413. .height = geom->height },
  414. 0);
  415. }
  416. p_delete(&geom);
  417. p_delete(&coords);
  418. }
  419. else
  420. event_handle_configurerequest_configure_window(ev);
  421. }
  422. /** The configure notify event handler.
  423. * \param ev The event.
  424. */
  425. static void
  426. event_handle_configurenotify(xcb_configure_notify_event_t *ev)
  427. {
  428. xcb_screen_t *screen = globalconf.screen;
  429. if(ev->window == screen->root)
  430. screen_schedule_refresh();
  431. /* Copy what XRRUpdateConfiguration() would do: Update the configuration */
  432. if(ev->window == screen->root) {
  433. screen->width_in_pixels = ev->width;
  434. screen->height_in_pixels = ev->height;
  435. }
  436. }
  437. /** The destroy notify event handler.
  438. * \param ev The event.
  439. */
  440. static void
  441. event_handle_destroynotify(xcb_destroy_notify_event_t *ev)
  442. {
  443. client_t *c;
  444. if((c = client_getbywin(ev->window)))
  445. client_unmanage(c, false);
  446. else
  447. for(int i = 0; i < globalconf.embedded.len; i++)
  448. if(globalconf.embedded.tab[i].win == ev->window)
  449. {
  450. xembed_window_array_take(&globalconf.embedded, i);
  451. luaA_systray_invalidate();
  452. }
  453. }
  454. /** Record that the given drawable contains the pointer.
  455. */
  456. void
  457. event_drawable_under_mouse(lua_State *L, int ud)
  458. {
  459. void *d;
  460. lua_pushvalue(L, ud);
  461. d = luaA_object_ref(L, -1);
  462. if (d == globalconf.drawable_under_mouse)
  463. {
  464. /* Nothing to do */
  465. luaA_object_unref(L, d);
  466. return;
  467. }
  468. if (globalconf.drawable_under_mouse != NULL)
  469. {
  470. /* Emit leave on previous drawable */
  471. luaA_object_push(L, globalconf.drawable_under_mouse);
  472. luaA_object_emit_signal(L, -1, "mouse::leave", 0);
  473. lua_pop(L, 1);
  474. /* Unref the previous drawable */
  475. luaA_object_unref(L, globalconf.drawable_under_mouse);
  476. globalconf.drawable_under_mouse = NULL;
  477. }
  478. if (d != NULL)
  479. {
  480. /* Reference the drawable for leave event later */
  481. globalconf.drawable_under_mouse = d;
  482. /* Emit enter */
  483. luaA_object_emit_signal(L, ud, "mouse::enter", 0);
  484. }
  485. }
  486. /** The motion notify event handler.
  487. * \param ev The event.
  488. */
  489. static void
  490. event_handle_motionnotify(xcb_motion_notify_event_t *ev)
  491. {
  492. lua_State *L = globalconf_get_lua_State();
  493. drawin_t *w;
  494. client_t *c;
  495. globalconf.timestamp = ev->time;
  496. if(event_handle_mousegrabber(ev->root_x, ev->root_y, ev->state))
  497. return;
  498. if((c = client_getbyframewin(ev->event)))
  499. {
  500. luaA_object_push(L, c);
  501. lua_pushinteger(L, ev->event_x);
  502. lua_pushinteger(L, ev->event_y);
  503. luaA_object_emit_signal(L, -3, "mouse::move", 2);
  504. /* now check if a titlebar was "hit" */
  505. int x = ev->event_x, y = ev->event_y;
  506. drawable_t *d = client_get_drawable_offset(c, &x, &y);
  507. if (d)
  508. {
  509. luaA_object_push_item(L, -1, d);
  510. event_drawable_under_mouse(L, -1);
  511. lua_pushinteger(L, x);
  512. lua_pushinteger(L, y);
  513. luaA_object_emit_signal(L, -3, "mouse::move", 2);
  514. lua_pop(L, 1);
  515. }
  516. lua_pop(L, 1);
  517. }
  518. if((w = drawin_getbywin(ev->event)))
  519. {
  520. luaA_object_push(L, w);
  521. luaA_object_push_item(L, -1, w->drawable);
  522. event_drawable_under_mouse(L, -1);
  523. lua_pushinteger(L, ev->event_x);
  524. lua_pushinteger(L, ev->event_y);
  525. luaA_object_emit_signal(L, -3, "mouse::move", 2);
  526. lua_pop(L, 2);
  527. }
  528. }
  529. /** The leave notify event handler.
  530. * \param ev The event.
  531. */
  532. static void
  533. event_handle_leavenotify(xcb_leave_notify_event_t *ev)
  534. {
  535. lua_State *L = globalconf_get_lua_State();
  536. client_t *c;
  537. globalconf.timestamp = ev->time;
  538. /*
  539. * Ignore events with non-normal modes. Those are because a grab
  540. * activated/deactivated. Everything will be "back to normal" after the
  541. * grab.
  542. */
  543. if(ev->mode != XCB_NOTIFY_MODE_NORMAL)
  544. return;
  545. if((c = client_getbyframewin(ev->event)))
  546. {
  547. /* The window was left in some way, so definitely no titlebar has the
  548. * mouse cursor.
  549. */
  550. lua_pushnil(L);
  551. event_drawable_under_mouse(L, -1);
  552. lua_pop(L, 1);
  553. /* If detail is inferior, it means that the cursor is now in some child
  554. * window of our window. Thus, the titlebar was left, but now the cursor
  555. * is in the actual child window. Thus, ignore detail=Inferior for
  556. * leaving client windows.
  557. */
  558. if(ev->detail != XCB_NOTIFY_DETAIL_INFERIOR) {
  559. luaA_object_push(L, c);
  560. luaA_object_emit_signal(L, -1, "mouse::leave", 0);
  561. lua_pop(L, 1);
  562. }
  563. } else if(ev->detail != XCB_NOTIFY_DETAIL_INFERIOR) {
  564. /* Some window was left. This must be a drawin. Ignore detail=Inferior,
  565. * because this means that some child window now contains the mouse
  566. * cursor, i.e. a systray window. Everything else is a real 'leave'.
  567. */
  568. lua_pushnil(L);
  569. event_drawable_under_mouse(L, -1);
  570. lua_pop(L, 1);
  571. }
  572. }
  573. /** The enter notify event handler.
  574. * \param ev The event.
  575. */
  576. static void
  577. event_handle_enternotify(xcb_enter_notify_event_t *ev)
  578. {
  579. lua_State *L = globalconf_get_lua_State();
  580. client_t *c;
  581. drawin_t *drawin;
  582. globalconf.timestamp = ev->time;
  583. /*
  584. * Ignore events with non-normal modes. Those are because a grab
  585. * activated/deactivated. Everything will be "back to normal" after the
  586. * grab.
  587. */
  588. if(ev->mode != XCB_NOTIFY_MODE_NORMAL)
  589. return;
  590. /*
  591. * We ignore events with detail "inferior". This detail means that the
  592. * cursor was previously inside of a child window and now left that child
  593. * window. For our purposes, the cursor was already inside our window
  594. * before.
  595. * One exception are titlebars: They are not their own window, but are
  596. * "outside of the actual client window".
  597. */
  598. if(ev->detail != XCB_NOTIFY_DETAIL_INFERIOR && (drawin = drawin_getbywin(ev->event)))
  599. {
  600. luaA_object_push(L, drawin);
  601. luaA_object_push_item(L, -1, drawin->drawable);
  602. event_drawable_under_mouse(L, -1);
  603. lua_pop(L, 2);
  604. }
  605. if((c = client_getbyframewin(ev->event)))
  606. {
  607. luaA_object_push(L, c);
  608. /* Detail=Inferior means that a child of the frame window now contains
  609. * the mouse cursor, i.e. the actual client now has the cursor. All
  610. * other details mean that the client itself was really left.
  611. */
  612. if(ev->detail != XCB_NOTIFY_DETAIL_INFERIOR) {
  613. luaA_object_emit_signal(L, -1, "mouse::enter", 0);
  614. }
  615. drawable_t *d = client_get_drawable(c, ev->event_x, ev->event_y);
  616. if (d)
  617. {
  618. luaA_object_push_item(L, -1, d);
  619. } else {
  620. lua_pushnil(L);
  621. }
  622. event_drawable_under_mouse(L, -1);
  623. lua_pop(L, 2);
  624. }
  625. else if (ev->detail != XCB_NOTIFY_DETAIL_INFERIOR && ev->event == globalconf.screen->root) {
  626. /* When there are multiple X screens with awesome running separate
  627. * instances, reset focus.
  628. */
  629. globalconf.focus.need_update = true;
  630. }
  631. }
  632. /** The focus in event handler.
  633. * \param ev The event.
  634. */
  635. static void
  636. event_handle_focusin(xcb_focus_in_event_t *ev)
  637. {
  638. if (ev->mode == XCB_NOTIFY_MODE_GRAB
  639. || ev->mode == XCB_NOTIFY_MODE_UNGRAB)
  640. /* Ignore focus changes due to keyboard grabs */
  641. return;
  642. /* Events that we are interested in: */
  643. switch(ev->detail)
  644. {
  645. /* These are events that jump between root windows.
  646. */
  647. case XCB_NOTIFY_DETAIL_ANCESTOR:
  648. case XCB_NOTIFY_DETAIL_INFERIOR:
  649. /* These are events that jump between clients.
  650. * Virtual events ensure we always get an event on our top-level window.
  651. */
  652. case XCB_NOTIFY_DETAIL_NONLINEAR_VIRTUAL:
  653. case XCB_NOTIFY_DETAIL_NONLINEAR:
  654. {
  655. client_t *c;
  656. if((c = client_getbywin(ev->event))) {
  657. /* If there is still a pending focus change, do it now. */
  658. client_focus_refresh();
  659. client_focus_update(c);
  660. }
  661. }
  662. /* all other events are ignored */
  663. default:
  664. break;
  665. }
  666. }
  667. /** The expose event handler.
  668. * \param ev The event.
  669. */
  670. static void
  671. event_handle_expose(xcb_expose_event_t *ev)
  672. {
  673. drawin_t *drawin;
  674. client_t *client;
  675. if((drawin = drawin_getbywin(ev->window)))
  676. drawin_refresh_pixmap_partial(drawin,
  677. ev->x, ev->y,
  678. ev->width, ev->height);
  679. if ((client = client_getbyframewin(ev->window)))
  680. client_refresh_partial(client, ev->x, ev->y, ev->width, ev->height);
  681. }
  682. /** The key press event handler.
  683. * \param ev The event.
  684. */
  685. static void
  686. event_handle_key(xcb_key_press_event_t *ev)
  687. {
  688. lua_State *L = globalconf_get_lua_State();
  689. globalconf.timestamp = ev->time;
  690. if(globalconf.keygrabber != LUA_REFNIL)
  691. {
  692. if(keygrabber_handlekpress(L, ev))
  693. {
  694. lua_rawgeti(L, LUA_REGISTRYINDEX, globalconf.keygrabber);
  695. if(!luaA_dofunction(L, 3, 0))
  696. {
  697. warn("Stopping keygrabber.");
  698. luaA_keygrabber_stop(L);
  699. }
  700. }
  701. }
  702. else
  703. {
  704. /* get keysym ignoring all modifiers */
  705. xcb_keysym_t keysym = xcb_key_symbols_get_keysym(globalconf.keysyms, ev->detail, 0);
  706. client_t *c;
  707. if((c = client_getbywin(ev->event)) || (c = client_getbynofocuswin(ev->event)))
  708. {
  709. luaA_object_push(L, c);
  710. event_key_callback(ev, &c->keys, L, -1, 1, &keysym);
  711. }
  712. else
  713. event_key_callback(ev, &globalconf.keys, L, 0, 0, &keysym);
  714. }
  715. }
  716. /** The map request event handler.
  717. * \param ev The event.
  718. */
  719. static void
  720. event_handle_maprequest(xcb_map_request_event_t *ev)
  721. {
  722. client_t *c;
  723. xembed_window_t *em;
  724. xcb_get_window_attributes_cookie_t wa_c;
  725. xcb_get_window_attributes_reply_t *wa_r;
  726. xcb_get_geometry_cookie_t geom_c;
  727. xcb_get_geometry_reply_t *geom_r;
  728. wa_c = xcb_get_window_attributes_unchecked(globalconf.connection, ev->window);
  729. if(!(wa_r = xcb_get_window_attributes_reply(globalconf.connection, wa_c, NULL)))
  730. return;
  731. if(wa_r->override_redirect)
  732. goto bailout;
  733. if((em = xembed_getbywin(&globalconf.embedded, ev->window)))
  734. {
  735. xcb_map_window(globalconf.connection, ev->window);
  736. xembed_window_activate(globalconf.connection, ev->window, globalconf.timestamp);
  737. /* The correct way to set this is via the _XEMBED_INFO property. Neither
  738. * of the XEMBED not the systray spec talk about mapping windows.
  739. * Apparently, Qt doesn't care and does not set an _XEMBED_INFO
  740. * property. Let's simulate the XEMBED_MAPPED bit.
  741. */
  742. em->info.flags |= XEMBED_MAPPED;
  743. luaA_systray_invalidate();
  744. }
  745. else if((c = client_getbywin(ev->window)))
  746. {
  747. /* Check that it may be visible, but not asked to be hidden */
  748. if(client_on_selected_tags(c) && !c->hidden)
  749. {
  750. lua_State *L = globalconf_get_lua_State();
  751. luaA_object_push(L, c);
  752. client_set_minimized(L, -1, false);
  753. lua_pop(L, 1);
  754. /* it will be raised, so just update ourself */
  755. client_raise(c);
  756. }
  757. }
  758. else
  759. {
  760. geom_c = xcb_get_geometry_unchecked(globalconf.connection, ev->window);
  761. if(!(geom_r = xcb_get_geometry_reply(globalconf.connection, geom_c, NULL)))
  762. {
  763. goto bailout;
  764. }
  765. client_manage(ev->window, geom_r, wa_r);
  766. p_delete(&geom_r);
  767. }
  768. bailout:
  769. p_delete(&wa_r);
  770. }
  771. /** The unmap notify event handler.
  772. * \param ev The event.
  773. */
  774. static void
  775. event_handle_unmapnotify(xcb_unmap_notify_event_t *ev)
  776. {
  777. client_t *c;
  778. if((c = client_getbywin(ev->window)))
  779. client_unmanage(c, true);
  780. }
  781. /** The randr screen change notify event handler.
  782. * \param ev The event.
  783. */
  784. static void
  785. event_handle_randr_screen_change_notify(xcb_randr_screen_change_notify_event_t *ev)
  786. {
  787. /* Ignore events for other roots (do we get them at all?) */
  788. if (ev->root != globalconf.screen->root)
  789. return;
  790. /* Do (part of) what XRRUpdateConfiguration() would do (update our state) */
  791. if (ev->rotation & (XCB_RANDR_ROTATION_ROTATE_90 | XCB_RANDR_ROTATION_ROTATE_270)) {
  792. globalconf.screen->width_in_pixels = ev->height;
  793. globalconf.screen->width_in_millimeters = ev->mheight;
  794. globalconf.screen->height_in_pixels = ev->width;
  795. globalconf.screen->height_in_millimeters = ev->mwidth;
  796. } else {
  797. globalconf.screen->width_in_pixels = ev->width;
  798. globalconf.screen->width_in_millimeters = ev->mwidth;
  799. globalconf.screen->height_in_pixels = ev->height;
  800. globalconf.screen->height_in_millimeters = ev->mheight;;
  801. }
  802. screen_schedule_refresh();
  803. }
  804. /** XRandR event handler for RRNotify subtype XRROutputChangeNotifyEvent
  805. */
  806. static void
  807. event_handle_randr_output_change_notify(xcb_randr_notify_event_t *ev)
  808. {
  809. if(ev->subCode == XCB_RANDR_NOTIFY_OUTPUT_CHANGE) {
  810. xcb_randr_output_t output = ev->u.oc.output;
  811. uint8_t connection = ev->u.oc.connection;
  812. const char *connection_str = NULL;
  813. xcb_randr_get_output_info_reply_t *info;
  814. lua_State *L = globalconf_get_lua_State();
  815. /* The following explicitly uses XCB_CURRENT_TIME since we want to know
  816. * the final state of the connection. There could be more notification
  817. * events underway and using some "old" timestamp causes problems.
  818. */
  819. info = xcb_randr_get_output_info_reply(globalconf.connection,
  820. xcb_randr_get_output_info_unchecked(globalconf.connection,
  821. output,
  822. XCB_CURRENT_TIME),
  823. NULL);
  824. if(!info)
  825. return;
  826. switch(connection) {
  827. case XCB_RANDR_CONNECTION_CONNECTED:
  828. connection_str = "Connected";
  829. break;
  830. case XCB_RANDR_CONNECTION_DISCONNECTED:
  831. connection_str = "Disconnected";
  832. break;
  833. default:
  834. connection_str = "Unknown";
  835. break;
  836. }
  837. lua_pushlstring(L, (char *)xcb_randr_get_output_info_name(info), xcb_randr_get_output_info_name_length(info));
  838. lua_pushstring(L, connection_str);
  839. signal_object_emit(L, &global_signals, "screen::change", 2);
  840. p_delete(&info);
  841. /* The docs for RRSetOutputPrimary say we get this signal */
  842. screen_update_primary();
  843. }
  844. }
  845. /** The shape notify event handler.
  846. * \param ev The event.
  847. */
  848. static void
  849. event_handle_shape_notify(xcb_shape_notify_event_t *ev)
  850. {
  851. client_t *c = client_getbywin(ev->affected_window);
  852. if (c)
  853. {
  854. lua_State *L = globalconf_get_lua_State();
  855. luaA_object_push(L, c);
  856. if (ev->shape_kind == XCB_SHAPE_SK_BOUNDING)
  857. luaA_object_emit_signal(L, -1, "property::shape_client_bounding", 0);
  858. if (ev->shape_kind == XCB_SHAPE_SK_CLIP)
  859. luaA_object_emit_signal(L, -1, "property::shape_client_clip", 0);
  860. lua_pop(L, 1);
  861. }
  862. }
  863. /** The client message event handler.
  864. * \param ev The event.
  865. */
  866. static void
  867. event_handle_clientmessage(xcb_client_message_event_t *ev)
  868. {
  869. /* check for startup notification messages */
  870. if(sn_xcb_display_process_event(globalconf.sndisplay, (xcb_generic_event_t *) ev))
  871. return;
  872. if(ev->type == WM_CHANGE_STATE)
  873. {
  874. client_t *c;
  875. if((c = client_getbywin(ev->window))
  876. && ev->format == 32
  877. && ev->data.data32[0] == XCB_ICCCM_WM_STATE_ICONIC)
  878. {
  879. lua_State *L = globalconf_get_lua_State();
  880. luaA_object_push(L, c);
  881. client_set_minimized(L, -1, true);
  882. lua_pop(L, 1);
  883. }
  884. }
  885. else if(ev->type == _XEMBED)
  886. xembed_process_client_message(ev);
  887. else if(ev->type == _NET_SYSTEM_TRAY_OPCODE)
  888. systray_process_client_message(ev);
  889. else
  890. ewmh_process_client_message(ev);
  891. }
  892. static void
  893. event_handle_reparentnotify(xcb_reparent_notify_event_t *ev)
  894. {
  895. client_t *c;
  896. if((c = client_getbywin(ev->window)) && c->frame_window != ev->parent)
  897. {
  898. /* Ignore reparents to the root window, they *might* be caused by
  899. * ourselves if a client quickly unmaps and maps itself again. */
  900. if (ev->parent != globalconf.screen->root)
  901. client_unmanage(c, true);
  902. }
  903. else if (ev->parent != globalconf.systray.window) {
  904. /* Embedded window moved elsewhere, end of embedding */
  905. for(int i = 0; i < globalconf.embedded.len; i++)
  906. if(globalconf.embedded.tab[i].win == ev->window)
  907. {
  908. xembed_window_array_take(&globalconf.embedded, i);
  909. xcb_change_save_set(globalconf.connection, XCB_SET_MODE_DELETE, ev->window);
  910. luaA_systray_invalidate();
  911. }
  912. }
  913. }
  914. static void
  915. event_handle_selectionclear(xcb_selection_clear_event_t *ev)
  916. {
  917. if(ev->selection == globalconf.selection_atom)
  918. {
  919. warn("Lost WM_Sn selection, exiting...");
  920. g_main_loop_quit(globalconf.loop);
  921. } else
  922. selection_handle_selectionclear(ev);
  923. }
  924. /** \brief awesome xerror function.
  925. * There's no way to check accesses to destroyed windows, thus those cases are
  926. * ignored (especially on UnmapNotify's).
  927. * \param e The error event.
  928. */
  929. static void
  930. xerror(xcb_generic_error_t *e)
  931. {
  932. /* ignore this */
  933. if(e->error_code == XCB_WINDOW
  934. || (e->error_code == XCB_MATCH
  935. && e->major_code == XCB_SET_INPUT_FOCUS)
  936. || (e->error_code == XCB_VALUE
  937. && e->major_code == XCB_KILL_CLIENT)
  938. || (e->error_code == XCB_MATCH
  939. && e->major_code == XCB_CONFIGURE_WINDOW))
  940. return;
  941. #ifdef WITH_XCB_ERRORS
  942. const char *major = xcb_errors_get_name_for_major_code(
  943. globalconf.errors_ctx, e->major_code);
  944. const char *minor = xcb_errors_get_name_for_minor_code(
  945. globalconf.errors_ctx, e->major_code, e->minor_code);
  946. const char *extension = NULL;
  947. const char *error = xcb_errors_get_name_for_error(
  948. globalconf.errors_ctx, e->error_code, &extension);
  949. #else
  950. const char *major = xcb_event_get_request_label(e->major_code);
  951. const char *minor = NULL;
  952. const char *extension = NULL;
  953. const char *error = xcb_event_get_error_label(e->error_code);
  954. #endif
  955. warn("X error: request=%s%s%s (major %d, minor %d), error=%s%s%s (%d)",
  956. major, minor == NULL ? "" : "-", NONULL(minor),
  957. e->major_code, e->minor_code,
  958. NONULL(extension), extension == NULL ? "" : "-", error,
  959. e->error_code);
  960. return;
  961. }
  962. static bool
  963. should_ignore(xcb_generic_event_t *event)
  964. {
  965. uint8_t response_type = XCB_EVENT_RESPONSE_TYPE(event);
  966. /* Remove completed sequences */
  967. uint32_t sequence = event->full_sequence;
  968. while (globalconf.ignore_enter_leave_events.len > 0) {
  969. uint32_t end = globalconf.ignore_enter_leave_events.tab[0].end.sequence;
  970. /* Do if (end >= sequence) break;, but handle wrap-around: The above is
  971. * equivalent to end-sequence > 0 (assuming unlimited precision). With
  972. * int32_t, this would mean that the sign bit is cleared, which means:
  973. */
  974. if (end - sequence < UINT32_MAX / 2)
  975. break;
  976. sequence_pair_array_take(&globalconf.ignore_enter_leave_events, 0);
  977. }
  978. /* Check if this event should be ignored */
  979. if ((response_type == XCB_ENTER_NOTIFY || response_type == XCB_LEAVE_NOTIFY)
  980. && globalconf.ignore_enter_leave_events.len > 0) {
  981. uint32_t begin = globalconf.ignore_enter_leave_events.tab[0].begin.sequence;
  982. uint32_t end = globalconf.ignore_enter_leave_events.tab[0].end.sequence;
  983. if (sequence >= begin && sequence <= end)
  984. return true;
  985. }
  986. return false;
  987. }
  988. void event_handle(xcb_generic_event_t *event)
  989. {
  990. uint8_t response_type = XCB_EVENT_RESPONSE_TYPE(event);
  991. if (should_ignore(event))
  992. return;
  993. if(response_type == 0)
  994. {
  995. /* This is an error, not a event */
  996. xerror((xcb_generic_error_t *) event);
  997. return;
  998. }
  999. switch(response_type)
  1000. {
  1001. #define EVENT(type, callback) case type: callback((void *) event); return
  1002. EVENT(XCB_BUTTON_PRESS, event_handle_button);
  1003. EVENT(XCB_BUTTON_RELEASE, event_handle_button);
  1004. EVENT(XCB_CONFIGURE_REQUEST, event_handle_configurerequest);
  1005. EVENT(XCB_CONFIGURE_NOTIFY, event_handle_configurenotify);
  1006. EVENT(XCB_DESTROY_NOTIFY, event_handle_destroynotify);
  1007. EVENT(XCB_ENTER_NOTIFY, event_handle_enternotify);
  1008. EVENT(XCB_CLIENT_MESSAGE, event_handle_clientmessage);
  1009. EVENT(XCB_EXPOSE, event_handle_expose);
  1010. EVENT(XCB_FOCUS_IN, event_handle_focusin);
  1011. EVENT(XCB_KEY_PRESS, event_handle_key);
  1012. EVENT(XCB_KEY_RELEASE, event_handle_key);
  1013. EVENT(XCB_LEAVE_NOTIFY, event_handle_leavenotify);
  1014. EVENT(XCB_MAP_REQUEST, event_handle_maprequest);
  1015. EVENT(XCB_MOTION_NOTIFY, event_handle_motionnotify);
  1016. EVENT(XCB_PROPERTY_NOTIFY, property_handle_propertynotify);
  1017. EVENT(XCB_REPARENT_NOTIFY, event_handle_reparentnotify);
  1018. EVENT(XCB_UNMAP_NOTIFY, event_handle_unmapnotify);
  1019. EVENT(XCB_SELECTION_CLEAR, event_handle_selectionclear);
  1020. EVENT(XCB_SELECTION_NOTIFY, event_handle_selectionnotify);
  1021. EVENT(XCB_SELECTION_REQUEST, selection_handle_selectionrequest);
  1022. #undef EVENT
  1023. }
  1024. #define EXTENSION_EVENT(base, offset, callback) \
  1025. if (globalconf.event_base_ ## base != 0 \
  1026. && response_type == globalconf.event_base_ ## base + (offset)) \
  1027. callback((void *) event)
  1028. EXTENSION_EVENT(randr, XCB_RANDR_SCREEN_CHANGE_NOTIFY, event_handle_randr_screen_change_notify);
  1029. EXTENSION_EVENT(randr, XCB_RANDR_NOTIFY, event_handle_randr_output_change_notify);
  1030. EXTENSION_EVENT(shape, XCB_SHAPE_NOTIFY, event_handle_shape_notify);
  1031. EXTENSION_EVENT(xkb, 0, event_handle_xkb_notify);
  1032. EXTENSION_EVENT(xfixes, XCB_XFIXES_SELECTION_NOTIFY, event_handle_xfixes_selection_notify);
  1033. #undef EXTENSION_EVENT
  1034. }
  1035. void event_init(void)
  1036. {
  1037. const xcb_query_extension_reply_t *reply;
  1038. reply = xcb_get_extension_data(globalconf.connection, &xcb_randr_id);
  1039. if (reply && reply->present)
  1040. globalconf.event_base_randr = reply->first_event;
  1041. reply = xcb_get_extension_data(globalconf.connection, &xcb_shape_id);
  1042. if (reply && reply->present)
  1043. globalconf.event_base_shape = reply->first_event;
  1044. reply = xcb_get_extension_data(globalconf.connection, &xcb_xkb_id);
  1045. if (reply && reply->present)
  1046. globalconf.event_base_xkb = reply->first_event;
  1047. reply = xcb_get_extension_data(globalconf.connection, &xcb_xfixes_id);
  1048. if (reply && reply->present)
  1049. globalconf.event_base_xfixes = reply->first_event;
  1050. }
  1051. // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80