xevents.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  1. /*
  2. * calmwm - the calm window manager
  3. *
  4. * Copyright (c) 2004 Marius Aamodt Eriksen <marius@monkey.org>
  5. *
  6. * Permission to use, copy, modify, and distribute this software for any
  7. * purpose with or without fee is hereby granted, provided that the above
  8. * copyright notice and this permission notice appear in all copies.
  9. *
  10. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  11. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  12. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  13. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  14. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  15. * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  16. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  17. *
  18. * $OpenBSD: xevents.c,v 1.118 2015/07/01 14:36:42 okan Exp $
  19. */
  20. /*
  21. * NOTE:
  22. * It is the responsibility of the caller to deal with memory
  23. * management of the xevent's.
  24. */
  25. #include <sys/types.h>
  26. #include <sys/queue.h>
  27. #include <err.h>
  28. #include <errno.h>
  29. #include <limits.h>
  30. #include <stdio.h>
  31. #include <stdlib.h>
  32. #include <string.h>
  33. #include <unistd.h>
  34. #include "calmwm.h"
  35. static void xev_handle_maprequest(XEvent *);
  36. static void xev_handle_unmapnotify(XEvent *);
  37. static void xev_handle_destroynotify(XEvent *);
  38. static void xev_handle_configurerequest(XEvent *);
  39. static void xev_handle_propertynotify(XEvent *);
  40. static void xev_handle_enternotify(XEvent *);
  41. static void xev_handle_buttonpress(XEvent *);
  42. static void xev_handle_buttonrelease(XEvent *);
  43. static void xev_handle_keypress(XEvent *);
  44. static void xev_handle_keyrelease(XEvent *);
  45. static void xev_handle_clientmessage(XEvent *);
  46. static void xev_handle_randr(XEvent *);
  47. static void xev_handle_mappingnotify(XEvent *);
  48. static void xev_handle_expose(XEvent *);
  49. void (*xev_handlers[LASTEvent])(XEvent *) = {
  50. [MapRequest] = xev_handle_maprequest,
  51. [UnmapNotify] = xev_handle_unmapnotify,
  52. [DestroyNotify] = xev_handle_destroynotify,
  53. [ConfigureRequest] = xev_handle_configurerequest,
  54. [PropertyNotify] = xev_handle_propertynotify,
  55. [EnterNotify] = xev_handle_enternotify,
  56. [ButtonPress] = xev_handle_buttonpress,
  57. [ButtonRelease] = xev_handle_buttonrelease,
  58. [KeyPress] = xev_handle_keypress,
  59. [KeyRelease] = xev_handle_keyrelease,
  60. [ClientMessage] = xev_handle_clientmessage,
  61. [MappingNotify] = xev_handle_mappingnotify,
  62. [Expose] = xev_handle_expose,
  63. };
  64. static KeySym modkeys[] = { XK_Alt_L, XK_Alt_R, XK_Super_L, XK_Super_R,
  65. XK_Control_L, XK_Control_R };
  66. static void
  67. xev_handle_maprequest(XEvent *ee)
  68. {
  69. XMapRequestEvent *e = &ee->xmaprequest;
  70. struct client_ctx *cc = NULL, *old_cc;
  71. if ((old_cc = client_current()))
  72. client_ptrsave(old_cc);
  73. if ((cc = client_find(e->window)) == NULL)
  74. cc = client_init(e->window, NULL);
  75. if ((cc != NULL) && (!(cc->flags & CLIENT_IGNORE)))
  76. client_ptrwarp(cc);
  77. }
  78. static void
  79. xev_handle_unmapnotify(XEvent *ee)
  80. {
  81. XUnmapEvent *e = &ee->xunmap;
  82. struct client_ctx *cc;
  83. if ((cc = client_find(e->window)) != NULL) {
  84. if (e->send_event) {
  85. client_set_wm_state(cc, WithdrawnState);
  86. } else {
  87. if (!(cc->flags & CLIENT_HIDDEN))
  88. client_delete(cc);
  89. }
  90. }
  91. }
  92. static void
  93. xev_handle_destroynotify(XEvent *ee)
  94. {
  95. XDestroyWindowEvent *e = &ee->xdestroywindow;
  96. struct client_ctx *cc;
  97. if ((cc = client_find(e->window)) != NULL)
  98. client_delete(cc);
  99. }
  100. static void
  101. xev_handle_configurerequest(XEvent *ee)
  102. {
  103. XConfigureRequestEvent *e = &ee->xconfigurerequest;
  104. struct client_ctx *cc;
  105. struct screen_ctx *sc;
  106. XWindowChanges wc;
  107. if ((cc = client_find(e->window)) != NULL) {
  108. sc = cc->sc;
  109. if (e->value_mask & CWWidth)
  110. cc->geom.w = e->width;
  111. if (e->value_mask & CWHeight)
  112. cc->geom.h = e->height;
  113. if (e->value_mask & CWX)
  114. cc->geom.x = e->x;
  115. if (e->value_mask & CWY)
  116. cc->geom.y = e->y;
  117. if (e->value_mask & CWBorderWidth)
  118. cc->bwidth = e->border_width;
  119. if (e->value_mask & CWSibling)
  120. wc.sibling = e->above;
  121. if (e->value_mask & CWStackMode)
  122. wc.stack_mode = e->detail;
  123. if (cc->geom.x == 0 && cc->geom.w >= sc->view.w)
  124. cc->geom.x -= cc->bwidth;
  125. if (cc->geom.y == 0 && cc->geom.h >= sc->view.h)
  126. cc->geom.y -= cc->bwidth;
  127. wc.x = cc->geom.x;
  128. wc.y = cc->geom.y;
  129. wc.width = cc->geom.w;
  130. wc.height = cc->geom.h;
  131. wc.border_width = cc->bwidth;
  132. XConfigureWindow(X_Dpy, cc->win, e->value_mask, &wc);
  133. client_config(cc);
  134. } else {
  135. /* let it do what it wants, it'll be ours when we map it. */
  136. wc.x = e->x;
  137. wc.y = e->y;
  138. wc.width = e->width;
  139. wc.height = e->height;
  140. wc.border_width = e->border_width;
  141. wc.stack_mode = Above;
  142. e->value_mask &= ~CWStackMode;
  143. XConfigureWindow(X_Dpy, e->window, e->value_mask, &wc);
  144. }
  145. }
  146. static void
  147. xev_handle_propertynotify(XEvent *ee)
  148. {
  149. XPropertyEvent *e = &ee->xproperty;
  150. struct screen_ctx *sc;
  151. struct client_ctx *cc;
  152. if ((cc = client_find(e->window)) != NULL) {
  153. switch (e->atom) {
  154. case XA_WM_NORMAL_HINTS:
  155. client_getsizehints(cc);
  156. break;
  157. case XA_WM_NAME:
  158. client_setname(cc);
  159. break;
  160. case XA_WM_HINTS:
  161. client_wm_hints(cc);
  162. client_draw_border(cc);
  163. break;
  164. case XA_WM_TRANSIENT_FOR:
  165. client_transient(cc);
  166. break;
  167. default:
  168. /* do nothing */
  169. break;
  170. }
  171. } else {
  172. TAILQ_FOREACH(sc, &Screenq, entry) {
  173. if (sc->rootwin == e->window) {
  174. if (e->atom == ewmh[_NET_DESKTOP_NAMES])
  175. xu_ewmh_net_desktop_names(sc);
  176. }
  177. }
  178. }
  179. }
  180. static void
  181. xev_handle_enternotify(XEvent *ee)
  182. {
  183. XCrossingEvent *e = &ee->xcrossing;
  184. struct client_ctx *cc;
  185. Last_Event_Time = e->time;
  186. if ((cc = client_find(e->window)) != NULL)
  187. client_setactive(cc);
  188. }
  189. /* We can split this into two event handlers. */
  190. static void
  191. xev_handle_buttonpress(XEvent *ee)
  192. {
  193. XButtonEvent *e = &ee->xbutton;
  194. struct client_ctx *cc, fakecc;
  195. struct binding *mb;
  196. e->state &= ~IGNOREMODMASK;
  197. TAILQ_FOREACH(mb, &Conf.mousebindingq, entry) {
  198. if (e->button == mb->press.button && e->state == mb->modmask)
  199. break;
  200. }
  201. if (mb == NULL)
  202. return;
  203. if (mb->flags & CWM_WIN) {
  204. if (((cc = client_find(e->window)) == NULL) &&
  205. (cc = client_current()) == NULL)
  206. return;
  207. } else {
  208. if (e->window != e->root)
  209. return;
  210. cc = &fakecc;
  211. if ((cc->sc = screen_find(e->window)) == NULL)
  212. return;
  213. }
  214. (*mb->callback)(cc, &mb->argument);
  215. }
  216. static void
  217. xev_handle_buttonrelease(XEvent *ee)
  218. {
  219. struct client_ctx *cc;
  220. if ((cc = client_current()))
  221. group_toggle_membership_leave(cc);
  222. }
  223. static void
  224. xev_handle_keypress(XEvent *ee)
  225. {
  226. XKeyEvent *e = &ee->xkey;
  227. struct client_ctx *cc = NULL, fakecc;
  228. struct binding *kb;
  229. KeySym keysym, skeysym;
  230. unsigned int modshift;
  231. keysym = XkbKeycodeToKeysym(X_Dpy, e->keycode, 0, 0);
  232. skeysym = XkbKeycodeToKeysym(X_Dpy, e->keycode, 0, 1);
  233. e->state &= ~IGNOREMODMASK;
  234. TAILQ_FOREACH(kb, &Conf.keybindingq, entry) {
  235. if (keysym != kb->press.keysym && skeysym == kb->press.keysym)
  236. modshift = ShiftMask;
  237. else
  238. modshift = 0;
  239. if ((kb->modmask | modshift) != e->state)
  240. continue;
  241. if (kb->press.keysym == ((modshift == 0) ? keysym : skeysym))
  242. break;
  243. }
  244. if (kb == NULL)
  245. return;
  246. if (kb->flags & CWM_WIN) {
  247. if (((cc = client_find(e->window)) == NULL) &&
  248. (cc = client_current()) == NULL)
  249. return;
  250. } else {
  251. cc = &fakecc;
  252. if ((cc->sc = screen_find(e->window)) == NULL)
  253. return;
  254. }
  255. (*kb->callback)(cc, &kb->argument);
  256. }
  257. /*
  258. * This is only used for the modifier suppression detection.
  259. */
  260. static void
  261. xev_handle_keyrelease(XEvent *ee)
  262. {
  263. XKeyEvent *e = &ee->xkey;
  264. struct screen_ctx *sc;
  265. KeySym keysym;
  266. unsigned int i;
  267. if ((sc = screen_find(e->root)) == NULL)
  268. return;
  269. keysym = XkbKeycodeToKeysym(X_Dpy, e->keycode, 0, 0);
  270. for (i = 0; i < nitems(modkeys); i++) {
  271. if (keysym == modkeys[i]) {
  272. client_cycle_leave(sc);
  273. break;
  274. }
  275. }
  276. }
  277. static void
  278. xev_handle_clientmessage(XEvent *ee)
  279. {
  280. XClientMessageEvent *e = &ee->xclient;
  281. struct client_ctx *cc, *old_cc;
  282. struct screen_ctx *sc;
  283. if (e->message_type == cwmh[WM_CHANGE_STATE]) {
  284. if ((cc = client_find(e->window)) != NULL) {
  285. if (e->data.l[0] == IconicState)
  286. client_hide(cc);
  287. }
  288. } else if (e->message_type == ewmh[_NET_CLOSE_WINDOW]) {
  289. if ((cc = client_find(e->window)) != NULL) {
  290. client_send_delete(cc);
  291. }
  292. } else if (e->message_type == ewmh[_NET_ACTIVE_WINDOW]) {
  293. if ((cc = client_find(e->window)) != NULL) {
  294. if ((old_cc = client_current()))
  295. client_ptrsave(old_cc);
  296. client_ptrwarp(cc);
  297. }
  298. } else if (e->message_type == ewmh[_NET_WM_DESKTOP]) {
  299. if ((cc = client_find(e->window)) != NULL) {
  300. /*
  301. * The EWMH spec states that if the cardinal returned
  302. * is 0xFFFFFFFF (-1) then the window should appear
  303. * on all desktops, in our case, group 0.
  304. */
  305. if (e->data.l[0] == (unsigned long)-1)
  306. group_movetogroup(cc, 0);
  307. else
  308. group_movetogroup(cc, e->data.l[0]);
  309. }
  310. } else if (e->message_type == ewmh[_NET_WM_STATE]) {
  311. if ((cc = client_find(e->window)) != NULL) {
  312. xu_ewmh_handle_net_wm_state_msg(cc,
  313. e->data.l[0], e->data.l[1], e->data.l[2]);
  314. }
  315. } else if (e->message_type == ewmh[_NET_CURRENT_DESKTOP]) {
  316. if ((sc = screen_find(e->window)) != NULL) {
  317. group_only(sc, e->data.l[0]);
  318. }
  319. }
  320. }
  321. static void
  322. xev_handle_randr(XEvent *ee)
  323. {
  324. XRRScreenChangeNotifyEvent *rev = (XRRScreenChangeNotifyEvent *)ee;
  325. struct screen_ctx *sc;
  326. int i;
  327. i = XRRRootToScreen(X_Dpy, rev->root);
  328. TAILQ_FOREACH(sc, &Screenq, entry) {
  329. if (sc->which == i) {
  330. XRRUpdateConfiguration(ee);
  331. screen_update_geometry(sc);
  332. }
  333. }
  334. }
  335. /*
  336. * Called when the keymap has changed.
  337. * Ungrab all keys, reload keymap and then regrab
  338. */
  339. static void
  340. xev_handle_mappingnotify(XEvent *ee)
  341. {
  342. XMappingEvent *e = &ee->xmapping;
  343. struct screen_ctx *sc;
  344. XRefreshKeyboardMapping(e);
  345. if (e->request == MappingKeyboard) {
  346. TAILQ_FOREACH(sc, &Screenq, entry)
  347. conf_grab_kbd(sc->rootwin);
  348. }
  349. }
  350. static void
  351. xev_handle_expose(XEvent *ee)
  352. {
  353. XExposeEvent *e = &ee->xexpose;
  354. struct client_ctx *cc;
  355. if ((cc = client_find(e->window)) != NULL && e->count == 0)
  356. client_draw_border(cc);
  357. }
  358. void
  359. xev_process(void)
  360. {
  361. XEvent e;
  362. XNextEvent(X_Dpy, &e);
  363. if (e.type - Randr_ev == RRScreenChangeNotify)
  364. xev_handle_randr(&e);
  365. else if (e.type < LASTEvent && xev_handlers[e.type] != NULL)
  366. (*xev_handlers[e.type])(&e);
  367. }