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.

ewmh.c 26KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803
  1. /*
  2. * ewmh.c - EWMH support functions
  3. *
  4. * Copyright © 2007-2009 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 "ewmh.h"
  22. #include "objects/client.h"
  23. #include "objects/tag.h"
  24. #include "common/atoms.h"
  25. #include "xwindow.h"
  26. #include <sys/types.h>
  27. #include <unistd.h>
  28. #include <xcb/xcb.h>
  29. #include <xcb/xcb_atom.h>
  30. #define _NET_WM_STATE_REMOVE 0
  31. #define _NET_WM_STATE_ADD 1
  32. #define _NET_WM_STATE_TOGGLE 2
  33. #define ALL_DESKTOPS 0xffffffff
  34. /** Update client EWMH hints.
  35. * \param L The Lua VM state.
  36. */
  37. static int
  38. ewmh_client_update_hints(lua_State *L)
  39. {
  40. client_t *c = luaA_checkudata(L, 1, &client_class);
  41. xcb_atom_t state[10]; /* number of defined state atoms */
  42. int i = 0;
  43. if(c->modal)
  44. state[i++] = _NET_WM_STATE_MODAL;
  45. if(c->fullscreen)
  46. state[i++] = _NET_WM_STATE_FULLSCREEN;
  47. if(c->maximized_vertical || c->maximized)
  48. state[i++] = _NET_WM_STATE_MAXIMIZED_VERT;
  49. if(c->maximized_horizontal || c->maximized)
  50. state[i++] = _NET_WM_STATE_MAXIMIZED_HORZ;
  51. if(c->sticky)
  52. state[i++] = _NET_WM_STATE_STICKY;
  53. if(c->skip_taskbar)
  54. state[i++] = _NET_WM_STATE_SKIP_TASKBAR;
  55. if(c->above)
  56. state[i++] = _NET_WM_STATE_ABOVE;
  57. if(c->below)
  58. state[i++] = _NET_WM_STATE_BELOW;
  59. if(c->minimized)
  60. state[i++] = _NET_WM_STATE_HIDDEN;
  61. if(c->urgent)
  62. state[i++] = _NET_WM_STATE_DEMANDS_ATTENTION;
  63. xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
  64. c->window, _NET_WM_STATE, XCB_ATOM_ATOM, 32, i, state);
  65. return 0;
  66. }
  67. static int
  68. ewmh_update_net_active_window(lua_State *L)
  69. {
  70. xcb_window_t win;
  71. if(globalconf.focus.client)
  72. win = globalconf.focus.client->window;
  73. else
  74. win = XCB_NONE;
  75. xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
  76. globalconf.screen->root,
  77. _NET_ACTIVE_WINDOW, XCB_ATOM_WINDOW, 32, 1, &win);
  78. return 0;
  79. }
  80. static int
  81. ewmh_update_net_client_list(lua_State *L)
  82. {
  83. xcb_window_t *wins = p_alloca(xcb_window_t, globalconf.clients.len);
  84. int n = 0;
  85. foreach(client, globalconf.clients)
  86. wins[n++] = (*client)->window;
  87. xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
  88. globalconf.screen->root,
  89. _NET_CLIENT_LIST, XCB_ATOM_WINDOW, 32, n, wins);
  90. return 0;
  91. }
  92. static int
  93. ewmh_client_update_frame_extents(lua_State *L)
  94. {
  95. client_t *c = luaA_checkudata(L, 1, &client_class);;
  96. uint32_t extents[4];
  97. extents[0] = c->border_width + c->titlebar[CLIENT_TITLEBAR_LEFT].size;
  98. extents[1] = c->border_width + c->titlebar[CLIENT_TITLEBAR_RIGHT].size;
  99. extents[2] = c->border_width + c->titlebar[CLIENT_TITLEBAR_TOP].size;
  100. extents[3] = c->border_width + c->titlebar[CLIENT_TITLEBAR_BOTTOM].size;
  101. xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
  102. c->window, _NET_FRAME_EXTENTS, XCB_ATOM_CARDINAL, 32, 4, extents);
  103. return 0;
  104. }
  105. void
  106. ewmh_init(void)
  107. {
  108. xcb_window_t father;
  109. xcb_screen_t *xscreen = globalconf.screen;
  110. xcb_atom_t atom[] =
  111. {
  112. _NET_SUPPORTED,
  113. _NET_SUPPORTING_WM_CHECK,
  114. _NET_STARTUP_ID,
  115. _NET_CLIENT_LIST,
  116. _NET_CLIENT_LIST_STACKING,
  117. _NET_NUMBER_OF_DESKTOPS,
  118. _NET_CURRENT_DESKTOP,
  119. _NET_DESKTOP_NAMES,
  120. _NET_ACTIVE_WINDOW,
  121. _NET_CLOSE_WINDOW,
  122. _NET_FRAME_EXTENTS,
  123. _NET_WM_NAME,
  124. _NET_WM_STRUT_PARTIAL,
  125. _NET_WM_ICON_NAME,
  126. _NET_WM_VISIBLE_ICON_NAME,
  127. _NET_WM_DESKTOP,
  128. _NET_WM_WINDOW_TYPE,
  129. _NET_WM_WINDOW_TYPE_DESKTOP,
  130. _NET_WM_WINDOW_TYPE_DOCK,
  131. _NET_WM_WINDOW_TYPE_TOOLBAR,
  132. _NET_WM_WINDOW_TYPE_MENU,
  133. _NET_WM_WINDOW_TYPE_UTILITY,
  134. _NET_WM_WINDOW_TYPE_SPLASH,
  135. _NET_WM_WINDOW_TYPE_DIALOG,
  136. _NET_WM_WINDOW_TYPE_DROPDOWN_MENU,
  137. _NET_WM_WINDOW_TYPE_POPUP_MENU,
  138. _NET_WM_WINDOW_TYPE_TOOLTIP,
  139. _NET_WM_WINDOW_TYPE_NOTIFICATION,
  140. _NET_WM_WINDOW_TYPE_COMBO,
  141. _NET_WM_WINDOW_TYPE_DND,
  142. _NET_WM_WINDOW_TYPE_NORMAL,
  143. _NET_WM_ICON,
  144. _NET_WM_PID,
  145. _NET_WM_STATE,
  146. _NET_WM_STATE_STICKY,
  147. _NET_WM_STATE_SKIP_TASKBAR,
  148. _NET_WM_STATE_FULLSCREEN,
  149. _NET_WM_STATE_MAXIMIZED_HORZ,
  150. _NET_WM_STATE_MAXIMIZED_VERT,
  151. _NET_WM_STATE_ABOVE,
  152. _NET_WM_STATE_BELOW,
  153. _NET_WM_STATE_MODAL,
  154. _NET_WM_STATE_HIDDEN,
  155. _NET_WM_STATE_DEMANDS_ATTENTION
  156. };
  157. int i;
  158. xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
  159. xscreen->root, _NET_SUPPORTED, XCB_ATOM_ATOM, 32,
  160. countof(atom), atom);
  161. /* create our own window */
  162. father = xcb_generate_id(globalconf.connection);
  163. xcb_create_window(globalconf.connection, xscreen->root_depth,
  164. father, xscreen->root, -1, -1, 1, 1, 0,
  165. XCB_COPY_FROM_PARENT, xscreen->root_visual, 0, NULL);
  166. xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
  167. xscreen->root, _NET_SUPPORTING_WM_CHECK, XCB_ATOM_WINDOW, 32,
  168. 1, &father);
  169. xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
  170. father, _NET_SUPPORTING_WM_CHECK, XCB_ATOM_WINDOW, 32,
  171. 1, &father);
  172. /* set the window manager name */
  173. xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
  174. father, _NET_WM_NAME, UTF8_STRING, 8, 7, "awesome");
  175. /* Set an instance, just because we can */
  176. xwindow_set_class_instance(father);
  177. /* set the window manager PID */
  178. i = getpid();
  179. xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
  180. father, _NET_WM_PID, XCB_ATOM_CARDINAL, 32, 1, &i);
  181. }
  182. static void
  183. ewmh_update_maximize(bool h, bool status, bool toggle)
  184. {
  185. lua_State *L = globalconf_get_lua_State();
  186. if (h)
  187. lua_pushstring(L, "client_maximize_horizontal");
  188. else
  189. lua_pushstring(L, "client_maximize_vertical");
  190. /* Create table argument with raise=true. */
  191. lua_newtable(L);
  192. lua_pushstring(L, "toggle");
  193. lua_pushboolean(L, toggle);
  194. lua_settable(L, -3);
  195. lua_pushstring(L, "status");
  196. lua_pushboolean(L, status);
  197. lua_settable(L, -3);
  198. luaA_object_emit_signal(L, -3, "request::geometry", 2);
  199. }
  200. void
  201. ewmh_init_lua(void)
  202. {
  203. lua_State *L = globalconf_get_lua_State();
  204. luaA_class_connect_signal(L, &client_class, "focus", ewmh_update_net_active_window);
  205. luaA_class_connect_signal(L, &client_class, "unfocus", ewmh_update_net_active_window);
  206. luaA_class_connect_signal(L, &client_class, "request::manage", ewmh_update_net_client_list);
  207. luaA_class_connect_signal(L, &client_class, "request::unmanage", ewmh_update_net_client_list);
  208. luaA_class_connect_signal(L, &client_class, "property::modal" , ewmh_client_update_hints);
  209. luaA_class_connect_signal(L, &client_class, "property::fullscreen" , ewmh_client_update_hints);
  210. luaA_class_connect_signal(L, &client_class, "property::maximized_horizontal" , ewmh_client_update_hints);
  211. luaA_class_connect_signal(L, &client_class, "property::maximized_vertical" , ewmh_client_update_hints);
  212. luaA_class_connect_signal(L, &client_class, "property::maximized" , ewmh_client_update_hints);
  213. luaA_class_connect_signal(L, &client_class, "property::sticky" , ewmh_client_update_hints);
  214. luaA_class_connect_signal(L, &client_class, "property::skip_taskbar" , ewmh_client_update_hints);
  215. luaA_class_connect_signal(L, &client_class, "property::above" , ewmh_client_update_hints);
  216. luaA_class_connect_signal(L, &client_class, "property::below" , ewmh_client_update_hints);
  217. luaA_class_connect_signal(L, &client_class, "property::minimized" , ewmh_client_update_hints);
  218. luaA_class_connect_signal(L, &client_class, "property::urgent" , ewmh_client_update_hints);
  219. luaA_class_connect_signal(L, &client_class, "property::titlebar_top" , ewmh_client_update_frame_extents);
  220. luaA_class_connect_signal(L, &client_class, "property::titlebar_bottom" , ewmh_client_update_frame_extents);
  221. luaA_class_connect_signal(L, &client_class, "property::titlebar_right" , ewmh_client_update_frame_extents);
  222. luaA_class_connect_signal(L, &client_class, "property::titlebar_left" , ewmh_client_update_frame_extents);
  223. luaA_class_connect_signal(L, &client_class, "property::border_width" , ewmh_client_update_frame_extents);
  224. luaA_class_connect_signal(L, &client_class, "request::manage", ewmh_client_update_frame_extents);
  225. /* NET_CURRENT_DESKTOP handling */
  226. luaA_class_connect_signal(L, &client_class, "focus", ewmh_update_net_current_desktop);
  227. luaA_class_connect_signal(L, &client_class, "unfocus", ewmh_update_net_current_desktop);
  228. luaA_class_connect_signal(L, &client_class, "tagged", ewmh_update_net_current_desktop);
  229. luaA_class_connect_signal(L, &client_class, "untagged", ewmh_update_net_current_desktop);
  230. luaA_class_connect_signal(L, &tag_class, "property::selected", ewmh_update_net_current_desktop);
  231. }
  232. /** Set the client list in stacking order, bottom to top.
  233. */
  234. void
  235. ewmh_update_net_client_list_stacking(void)
  236. {
  237. int n = 0;
  238. xcb_window_t *wins = p_alloca(xcb_window_t, globalconf.stack.len);
  239. foreach(client, globalconf.stack)
  240. wins[n++] = (*client)->window;
  241. xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
  242. globalconf.screen->root,
  243. _NET_CLIENT_LIST_STACKING, XCB_ATOM_WINDOW, 32, n, wins);
  244. }
  245. void
  246. ewmh_update_net_numbers_of_desktop(void)
  247. {
  248. uint32_t count = globalconf.tags.len;
  249. xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
  250. globalconf.screen->root,
  251. _NET_NUMBER_OF_DESKTOPS, XCB_ATOM_CARDINAL, 32, 1, &count);
  252. }
  253. int
  254. ewmh_update_net_current_desktop(lua_State *L)
  255. {
  256. uint32_t idx = tags_get_current_or_first_selected_index();
  257. xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
  258. globalconf.screen->root,
  259. _NET_CURRENT_DESKTOP, XCB_ATOM_CARDINAL, 32, 1, &idx);
  260. return 0;
  261. }
  262. void
  263. ewmh_update_net_desktop_names(void)
  264. {
  265. buffer_t buf;
  266. buffer_inita(&buf, BUFSIZ);
  267. foreach(tag, globalconf.tags)
  268. {
  269. buffer_adds(&buf, tag_get_name(*tag));
  270. buffer_addc(&buf, '\0');
  271. }
  272. xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
  273. globalconf.screen->root,
  274. _NET_DESKTOP_NAMES, UTF8_STRING, 8, buf.len, buf.s);
  275. buffer_wipe(&buf);
  276. }
  277. static void
  278. ewmh_process_state_atom(client_t *c, xcb_atom_t state, int set)
  279. {
  280. lua_State *L = globalconf_get_lua_State();
  281. luaA_object_push(L, c);
  282. if(state == _NET_WM_STATE_STICKY)
  283. {
  284. if(set == _NET_WM_STATE_REMOVE)
  285. client_set_sticky(L, -1, false);
  286. else if(set == _NET_WM_STATE_ADD)
  287. client_set_sticky(L, -1, true);
  288. else if(set == _NET_WM_STATE_TOGGLE)
  289. client_set_sticky(L, -1, !c->sticky);
  290. }
  291. else if(state == _NET_WM_STATE_SKIP_TASKBAR)
  292. {
  293. if(set == _NET_WM_STATE_REMOVE)
  294. client_set_skip_taskbar(L, -1, false);
  295. else if(set == _NET_WM_STATE_ADD)
  296. client_set_skip_taskbar(L, -1, true);
  297. else if(set == _NET_WM_STATE_TOGGLE)
  298. client_set_skip_taskbar(L, -1, !c->skip_taskbar);
  299. }
  300. else if(state == _NET_WM_STATE_FULLSCREEN)
  301. {
  302. if(set == _NET_WM_STATE_REMOVE)
  303. client_set_fullscreen(L, -1, false);
  304. else if(set == _NET_WM_STATE_ADD)
  305. client_set_fullscreen(L, -1, true);
  306. else if(set == _NET_WM_STATE_TOGGLE)
  307. client_set_fullscreen(L, -1, !c->fullscreen);
  308. }
  309. else if(state == _NET_WM_STATE_MAXIMIZED_HORZ)
  310. {
  311. if(set == _NET_WM_STATE_REMOVE)
  312. ewmh_update_maximize(true, false, false);
  313. else if(set == _NET_WM_STATE_ADD)
  314. ewmh_update_maximize(true, true, false);
  315. else if(set == _NET_WM_STATE_TOGGLE)
  316. ewmh_update_maximize(true, false, true);
  317. }
  318. else if(state == _NET_WM_STATE_MAXIMIZED_VERT)
  319. {
  320. if(set == _NET_WM_STATE_REMOVE)
  321. ewmh_update_maximize(false, false, false);
  322. else if(set == _NET_WM_STATE_ADD)
  323. ewmh_update_maximize(false, true, false);
  324. else if(set == _NET_WM_STATE_TOGGLE)
  325. ewmh_update_maximize(false, false, true);
  326. }
  327. else if(state == _NET_WM_STATE_ABOVE)
  328. {
  329. if(set == _NET_WM_STATE_REMOVE)
  330. client_set_above(L, -1, false);
  331. else if(set == _NET_WM_STATE_ADD)
  332. client_set_above(L, -1, true);
  333. else if(set == _NET_WM_STATE_TOGGLE)
  334. client_set_above(L, -1, !c->above);
  335. }
  336. else if(state == _NET_WM_STATE_BELOW)
  337. {
  338. if(set == _NET_WM_STATE_REMOVE)
  339. client_set_below(L, -1, false);
  340. else if(set == _NET_WM_STATE_ADD)
  341. client_set_below(L, -1, true);
  342. else if(set == _NET_WM_STATE_TOGGLE)
  343. client_set_below(L, -1, !c->below);
  344. }
  345. else if(state == _NET_WM_STATE_MODAL)
  346. {
  347. if(set == _NET_WM_STATE_REMOVE)
  348. client_set_modal(L, -1, false);
  349. else if(set == _NET_WM_STATE_ADD)
  350. client_set_modal(L, -1, true);
  351. else if(set == _NET_WM_STATE_TOGGLE)
  352. client_set_modal(L, -1, !c->modal);
  353. }
  354. else if(state == _NET_WM_STATE_HIDDEN)
  355. {
  356. if(set == _NET_WM_STATE_REMOVE)
  357. client_set_minimized(L, -1, false);
  358. else if(set == _NET_WM_STATE_ADD)
  359. client_set_minimized(L, -1, true);
  360. else if(set == _NET_WM_STATE_TOGGLE)
  361. client_set_minimized(L, -1, !c->minimized);
  362. }
  363. else if(state == _NET_WM_STATE_DEMANDS_ATTENTION)
  364. {
  365. if(set == _NET_WM_STATE_REMOVE) {
  366. lua_pushboolean(L, false);
  367. /*TODO v5: Add a context */
  368. luaA_object_emit_signal(L, -2, "request::urgent", 1);
  369. }
  370. else if(set == _NET_WM_STATE_ADD) {
  371. lua_pushboolean(L, true);
  372. /*TODO v5: Add a context */
  373. luaA_object_emit_signal(L, -2, "request::urgent", 1);
  374. }
  375. else if(set == _NET_WM_STATE_TOGGLE) {
  376. lua_pushboolean(L, !c->urgent);
  377. /*TODO v5: Add a context */
  378. luaA_object_emit_signal(L, -2, "request::urgent", 1);
  379. }
  380. }
  381. lua_pop(L, 1);
  382. }
  383. static void
  384. ewmh_process_desktop(client_t *c, uint32_t desktop)
  385. {
  386. lua_State *L = globalconf_get_lua_State();
  387. int idx = desktop;
  388. if(desktop == ALL_DESKTOPS)
  389. {
  390. luaA_object_push(L, c);
  391. lua_pushboolean(L, true);
  392. /*TODO v5: Move the context argument to arg1 */
  393. luaA_object_emit_signal(L, -2, "request::tag", 1);
  394. /* Pop the client, arguments are already popped */
  395. lua_pop(L, 1);
  396. }
  397. else if (idx >= 0 && idx < globalconf.tags.len)
  398. {
  399. luaA_object_push(L, c);
  400. luaA_object_push(L, globalconf.tags.tab[idx]);
  401. /*TODO v5: Move the context argument to arg1 */
  402. luaA_object_emit_signal(L, -2, "request::tag", 1);
  403. /* Pop the client, arguments are already popped */
  404. lua_pop(L, 1);
  405. }
  406. }
  407. int
  408. ewmh_process_client_message(xcb_client_message_event_t *ev)
  409. {
  410. client_t *c;
  411. if(ev->type == _NET_CURRENT_DESKTOP)
  412. {
  413. int idx = ev->data.data32[0];
  414. if (idx >= 0 && idx < globalconf.tags.len)
  415. {
  416. lua_State *L = globalconf_get_lua_State();
  417. luaA_object_push(L, globalconf.tags.tab[idx]);
  418. lua_pushstring(L, "ewmh");
  419. luaA_object_emit_signal(L, -1, "request::select", 1);
  420. lua_pop(L, 1);
  421. }
  422. }
  423. else if(ev->type == _NET_CLOSE_WINDOW)
  424. {
  425. if((c = client_getbywin(ev->window)))
  426. client_kill(c);
  427. }
  428. else if(ev->type == _NET_WM_DESKTOP)
  429. {
  430. if((c = client_getbywin(ev->window)))
  431. {
  432. ewmh_process_desktop(c, ev->data.data32[0]);
  433. }
  434. }
  435. else if(ev->type == _NET_WM_STATE)
  436. {
  437. if((c = client_getbywin(ev->window)))
  438. {
  439. ewmh_process_state_atom(c, (xcb_atom_t) ev->data.data32[1], ev->data.data32[0]);
  440. if(ev->data.data32[2])
  441. ewmh_process_state_atom(c, (xcb_atom_t) ev->data.data32[2],
  442. ev->data.data32[0]);
  443. }
  444. }
  445. else if(ev->type == _NET_ACTIVE_WINDOW)
  446. {
  447. if((c = client_getbywin(ev->window))) {
  448. lua_State *L = globalconf_get_lua_State();
  449. luaA_object_push(L, c);
  450. lua_pushstring(L, "ewmh");
  451. /* Create table argument with raise=true. */
  452. lua_newtable(L);
  453. lua_pushstring(L, "raise");
  454. lua_pushboolean(L, true);
  455. lua_settable(L, -3);
  456. luaA_object_emit_signal(L, -3, "request::activate", 2);
  457. lua_pop(L, 1);
  458. }
  459. }
  460. return 0;
  461. }
  462. /** Update the client active desktop.
  463. * This is "wrong" since it can be on several tags, but EWMH has a strict view
  464. * of desktop system so just take the first tag.
  465. * \param c The client.
  466. */
  467. void
  468. ewmh_client_update_desktop(client_t *c)
  469. {
  470. int i;
  471. if(c->sticky)
  472. {
  473. xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
  474. c->window, _NET_WM_DESKTOP, XCB_ATOM_CARDINAL, 32, 1,
  475. (uint32_t[]) { ALL_DESKTOPS });
  476. return;
  477. }
  478. for(i = 0; i < globalconf.tags.len; i++)
  479. if(is_client_tagged(c, globalconf.tags.tab[i]))
  480. {
  481. xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
  482. c->window, _NET_WM_DESKTOP, XCB_ATOM_CARDINAL, 32, 1, &i);
  483. return;
  484. }
  485. /* It doesn't have any tags, remove the property */
  486. xcb_delete_property(globalconf.connection, c->window, _NET_WM_DESKTOP);
  487. }
  488. /** Update the client struts.
  489. * \param window The window to update the struts for.
  490. * \param strut The strut type to update the window with.
  491. */
  492. void
  493. ewmh_update_strut(xcb_window_t window, strut_t *strut)
  494. {
  495. if(window)
  496. {
  497. const uint32_t state[] =
  498. {
  499. strut->left,
  500. strut->right,
  501. strut->top,
  502. strut->bottom,
  503. strut->left_start_y,
  504. strut->left_end_y,
  505. strut->right_start_y,
  506. strut->right_end_y,
  507. strut->top_start_x,
  508. strut->top_end_x,
  509. strut->bottom_start_x,
  510. strut->bottom_end_x
  511. };
  512. xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
  513. window, _NET_WM_STRUT_PARTIAL, XCB_ATOM_CARDINAL, 32, countof(state), state);
  514. }
  515. }
  516. /** Update the window type.
  517. * \param window The window to update.
  518. * \param type The new type to set.
  519. */
  520. void
  521. ewmh_update_window_type(xcb_window_t window, uint32_t type)
  522. {
  523. xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
  524. window, _NET_WM_WINDOW_TYPE, XCB_ATOM_ATOM, 32, 1, &type);
  525. }
  526. void
  527. ewmh_client_check_hints(client_t *c)
  528. {
  529. xcb_atom_t *state;
  530. void *data = NULL;
  531. xcb_get_property_cookie_t c0, c1, c2;
  532. xcb_get_property_reply_t *reply;
  533. bool is_h_max = false;
  534. bool is_v_max = false;
  535. /* Send the GetProperty requests which will be processed later */
  536. c0 = xcb_get_property_unchecked(globalconf.connection, false, c->window,
  537. _NET_WM_DESKTOP, XCB_GET_PROPERTY_TYPE_ANY, 0, 1);
  538. c1 = xcb_get_property_unchecked(globalconf.connection, false, c->window,
  539. _NET_WM_STATE, XCB_ATOM_ATOM, 0, UINT32_MAX);
  540. c2 = xcb_get_property_unchecked(globalconf.connection, false, c->window,
  541. _NET_WM_WINDOW_TYPE, XCB_ATOM_ATOM, 0, UINT32_MAX);
  542. reply = xcb_get_property_reply(globalconf.connection, c0, NULL);
  543. if(reply && reply->value_len && (data = xcb_get_property_value(reply)))
  544. {
  545. ewmh_process_desktop(c, *(uint32_t *) data);
  546. }
  547. p_delete(&reply);
  548. reply = xcb_get_property_reply(globalconf.connection, c1, NULL);
  549. if(reply && (data = xcb_get_property_value(reply)))
  550. {
  551. state = (xcb_atom_t *) data;
  552. for(int i = 0; i < xcb_get_property_value_length(reply) / ssizeof(xcb_atom_t); i++)
  553. if (state[i] == _NET_WM_STATE_MAXIMIZED_HORZ)
  554. is_h_max = true;
  555. else if (state[i] == _NET_WM_STATE_MAXIMIZED_VERT)
  556. is_v_max = true;
  557. else
  558. ewmh_process_state_atom(c, state[i], _NET_WM_STATE_ADD);
  559. }
  560. /* Check maximization manually */
  561. if (is_h_max && is_v_max) {
  562. lua_State *L = globalconf_get_lua_State();
  563. luaA_object_push(L, c);
  564. client_set_maximized(L, -1, true);
  565. lua_pop(L, 1);
  566. }
  567. else if(is_h_max)
  568. {
  569. lua_State *L = globalconf_get_lua_State();
  570. luaA_object_push(L, c);
  571. client_set_maximized_horizontal(L, -1, true);
  572. lua_pop(L, 1);
  573. }
  574. else if(is_v_max)
  575. {
  576. lua_State *L = globalconf_get_lua_State();
  577. luaA_object_push(L, c);
  578. client_set_maximized_vertical(L, -1, true);
  579. lua_pop(L, 1);
  580. }
  581. p_delete(&reply);
  582. reply = xcb_get_property_reply(globalconf.connection, c2, NULL);
  583. if(reply && (data = xcb_get_property_value(reply)))
  584. {
  585. c->has_NET_WM_WINDOW_TYPE = true;
  586. state = (xcb_atom_t *) data;
  587. for(int i = 0; i < xcb_get_property_value_length(reply) / ssizeof(xcb_atom_t); i++)
  588. if(state[i] == _NET_WM_WINDOW_TYPE_DESKTOP)
  589. c->type = MAX(c->type, WINDOW_TYPE_DESKTOP);
  590. else if(state[i] == _NET_WM_WINDOW_TYPE_DIALOG)
  591. c->type = MAX(c->type, WINDOW_TYPE_DIALOG);
  592. else if(state[i] == _NET_WM_WINDOW_TYPE_SPLASH)
  593. c->type = MAX(c->type, WINDOW_TYPE_SPLASH);
  594. else if(state[i] == _NET_WM_WINDOW_TYPE_DOCK)
  595. c->type = MAX(c->type, WINDOW_TYPE_DOCK);
  596. else if(state[i] == _NET_WM_WINDOW_TYPE_MENU)
  597. c->type = MAX(c->type, WINDOW_TYPE_MENU);
  598. else if(state[i] == _NET_WM_WINDOW_TYPE_TOOLBAR)
  599. c->type = MAX(c->type, WINDOW_TYPE_TOOLBAR);
  600. else if(state[i] == _NET_WM_WINDOW_TYPE_UTILITY)
  601. c->type = MAX(c->type, WINDOW_TYPE_UTILITY);
  602. } else
  603. c->has_NET_WM_WINDOW_TYPE = false;
  604. p_delete(&reply);
  605. }
  606. /** Process the WM strut of a client.
  607. * \param c The client.
  608. */
  609. void
  610. ewmh_process_client_strut(client_t *c)
  611. {
  612. void *data;
  613. xcb_get_property_reply_t *strut_r;
  614. xcb_get_property_cookie_t strut_q = xcb_get_property_unchecked(globalconf.connection, false, c->window,
  615. _NET_WM_STRUT_PARTIAL, XCB_ATOM_CARDINAL, 0, 12);
  616. strut_r = xcb_get_property_reply(globalconf.connection, strut_q, NULL);
  617. if(strut_r
  618. && strut_r->value_len
  619. && (data = xcb_get_property_value(strut_r)))
  620. {
  621. uint32_t *strut = data;
  622. if(c->strut.left != strut[0]
  623. || c->strut.right != strut[1]
  624. || c->strut.top != strut[2]
  625. || c->strut.bottom != strut[3]
  626. || c->strut.left_start_y != strut[4]
  627. || c->strut.left_end_y != strut[5]
  628. || c->strut.right_start_y != strut[6]
  629. || c->strut.right_end_y != strut[7]
  630. || c->strut.top_start_x != strut[8]
  631. || c->strut.top_end_x != strut[9]
  632. || c->strut.bottom_start_x != strut[10]
  633. || c->strut.bottom_end_x != strut[11])
  634. {
  635. c->strut.left = strut[0];
  636. c->strut.right = strut[1];
  637. c->strut.top = strut[2];
  638. c->strut.bottom = strut[3];
  639. c->strut.left_start_y = strut[4];
  640. c->strut.left_end_y = strut[5];
  641. c->strut.right_start_y = strut[6];
  642. c->strut.right_end_y = strut[7];
  643. c->strut.top_start_x = strut[8];
  644. c->strut.top_end_x = strut[9];
  645. c->strut.bottom_start_x = strut[10];
  646. c->strut.bottom_end_x = strut[11];
  647. lua_State *L = globalconf_get_lua_State();
  648. luaA_object_push(L, c);
  649. luaA_object_emit_signal(L, -1, "property::struts", 0);
  650. lua_pop(L, 1);
  651. }
  652. }
  653. p_delete(&strut_r);
  654. }
  655. /** Send request to get NET_WM_ICON (EWMH)
  656. * \param w The window.
  657. * \return The cookie associated with the request.
  658. */
  659. xcb_get_property_cookie_t
  660. ewmh_window_icon_get_unchecked(xcb_window_t w)
  661. {
  662. return xcb_get_property_unchecked(globalconf.connection, false, w,
  663. _NET_WM_ICON, XCB_ATOM_CARDINAL, 0, UINT32_MAX);
  664. }
  665. static cairo_surface_t *
  666. ewmh_window_icon_from_reply_next(uint32_t **data, uint32_t *data_end)
  667. {
  668. uint32_t width, height;
  669. uint64_t data_len;
  670. uint32_t *icon_data;
  671. if(data_end - *data <= 2)
  672. return NULL;
  673. width = (*data)[0];
  674. height = (*data)[1];
  675. /* Check that we have enough data, handling overflow */
  676. data_len = width * (uint64_t) height;
  677. if (width < 1 || height < 1 || data_len > (uint64_t) (data_end - *data) - 2)
  678. return NULL;
  679. icon_data = *data + 2;
  680. *data += 2 + data_len;
  681. return draw_surface_from_data(width, height, icon_data);
  682. }
  683. static cairo_surface_array_t
  684. ewmh_window_icon_from_reply(xcb_get_property_reply_t *r)
  685. {
  686. uint32_t *data, *data_end;
  687. cairo_surface_array_t result;
  688. cairo_surface_t *s;
  689. cairo_surface_array_init(&result);
  690. if(!r || r->type != XCB_ATOM_CARDINAL || r->format != 32)
  691. return result;
  692. data = (uint32_t*) xcb_get_property_value(r);
  693. data_end = &data[r->length];
  694. if(!data)
  695. return result;
  696. while ((s = ewmh_window_icon_from_reply_next(&data, data_end)) != NULL) {
  697. cairo_surface_array_push(&result, s);
  698. }
  699. return result;
  700. }
  701. /** Get NET_WM_ICON.
  702. * \param cookie The cookie.
  703. * \return An array of icons.
  704. */
  705. cairo_surface_array_t
  706. ewmh_window_icon_get_reply(xcb_get_property_cookie_t cookie)
  707. {
  708. xcb_get_property_reply_t *r = xcb_get_property_reply(globalconf.connection, cookie, NULL);
  709. cairo_surface_array_t result = ewmh_window_icon_from_reply(r);
  710. p_delete(&r);
  711. return result;
  712. }
  713. // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80