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.

root.c 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623
  1. /*
  2. * root.c - root window management
  3. *
  4. * Copyright © 2008-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. /** awesome root window API
  22. * @author Julien Danjou &lt;julien@danjou.info&gt;
  23. * @copyright 2008-2009 Julien Danjou
  24. * @coreclassmod root
  25. */
  26. #include "globalconf.h"
  27. #include "common/atoms.h"
  28. #include "common/xcursor.h"
  29. #include "common/xutil.h"
  30. #include "objects/button.h"
  31. #include "common/luaclass.h"
  32. #include "xwindow.h"
  33. #include "math.h"
  34. #include <xcb/xtest.h>
  35. #include <xcb/xcb_aux.h>
  36. #include <cairo-xcb.h>
  37. static int miss_index_handler = LUA_REFNIL;
  38. static int miss_newindex_handler = LUA_REFNIL;
  39. static int miss_call_handler = LUA_REFNIL;
  40. static void
  41. root_set_wallpaper_pixmap(xcb_connection_t *c, xcb_pixmap_t p)
  42. {
  43. xcb_get_property_cookie_t prop_c;
  44. xcb_get_property_reply_t *prop_r;
  45. const xcb_screen_t *screen = globalconf.screen;
  46. /* We now have the pattern painted to the pixmap p. Now turn p into the root
  47. * window's background pixmap.
  48. */
  49. xcb_change_window_attributes(c, screen->root, XCB_CW_BACK_PIXMAP, &p);
  50. xcb_clear_area(c, 0, screen->root, 0, 0, 0, 0);
  51. prop_c = xcb_get_property_unchecked(c, false,
  52. screen->root, ESETROOT_PMAP_ID, XCB_ATOM_PIXMAP, 0, 1);
  53. /* Theoretically, this should be enough to set the wallpaper. However, to
  54. * make pseudo-transparency work, clients need a way to get the wallpaper.
  55. * You can't query a window's back pixmap, so properties are (ab)used.
  56. */
  57. xcb_change_property(c, XCB_PROP_MODE_REPLACE, screen->root, _XROOTPMAP_ID, XCB_ATOM_PIXMAP, 32, 1, &p);
  58. xcb_change_property(c, XCB_PROP_MODE_REPLACE, screen->root, ESETROOT_PMAP_ID, XCB_ATOM_PIXMAP, 32, 1, &p);
  59. /* Now make sure that the old wallpaper is freed (but only do this for ESETROOT_PMAP_ID) */
  60. prop_r = xcb_get_property_reply(c, prop_c, NULL);
  61. if (prop_r && prop_r->value_len)
  62. {
  63. xcb_pixmap_t *rootpix = xcb_get_property_value(prop_r);
  64. if (rootpix)
  65. xcb_kill_client(c, *rootpix);
  66. }
  67. p_delete(&prop_r);
  68. }
  69. static bool
  70. root_set_wallpaper(cairo_pattern_t *pattern)
  71. {
  72. lua_State *L = globalconf_get_lua_State();
  73. xcb_connection_t *c = xcb_connect(NULL, NULL);
  74. xcb_pixmap_t p = xcb_generate_id(c);
  75. /* globalconf.connection should be connected to the same X11 server, so we
  76. * can just use the info from that other connection.
  77. */
  78. const xcb_screen_t *screen = globalconf.screen;
  79. uint16_t width = screen->width_in_pixels;
  80. uint16_t height = screen->height_in_pixels;
  81. bool result = false;
  82. cairo_surface_t *surface;
  83. cairo_t *cr;
  84. if (xcb_connection_has_error(c))
  85. goto disconnect;
  86. /* Create a pixmap and make sure it is already created, because we are going
  87. * to use it from the other X11 connection (Juggling with X11 connections
  88. * is a really, really bad idea).
  89. */
  90. xcb_create_pixmap(c, screen->root_depth, p, screen->root, width, height);
  91. xcb_aux_sync(c);
  92. /* Now paint to the picture from the main connection so that cairo sees that
  93. * it can tell the X server to copy between the (possible) old pixmap and
  94. * the new one directly and doesn't need GetImage and PutImage.
  95. */
  96. surface = cairo_xcb_surface_create(globalconf.connection, p, draw_default_visual(screen), width, height);
  97. cr = cairo_create(surface);
  98. /* Paint the pattern to the surface */
  99. cairo_set_source(cr, pattern);
  100. cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
  101. cairo_paint(cr);
  102. cairo_destroy(cr);
  103. cairo_surface_flush(surface);
  104. xcb_aux_sync(globalconf.connection);
  105. /* Change the wallpaper, without sending us a PropertyNotify event */
  106. xcb_grab_server(globalconf.connection);
  107. xcb_change_window_attributes(globalconf.connection,
  108. globalconf.screen->root,
  109. XCB_CW_EVENT_MASK,
  110. (uint32_t[]) { 0 });
  111. root_set_wallpaper_pixmap(globalconf.connection, p);
  112. xcb_change_window_attributes(globalconf.connection,
  113. globalconf.screen->root,
  114. XCB_CW_EVENT_MASK,
  115. ROOT_WINDOW_EVENT_MASK);
  116. xutil_ungrab_server(globalconf.connection);
  117. /* Make sure our pixmap is not destroyed when we disconnect. */
  118. xcb_set_close_down_mode(c, XCB_CLOSE_DOWN_RETAIN_PERMANENT);
  119. /* Tell Lua that the wallpaper changed */
  120. cairo_surface_destroy(globalconf.wallpaper);
  121. globalconf.wallpaper = surface;
  122. signal_object_emit(L, &global_signals, "wallpaper_changed", 0);
  123. result = true;
  124. disconnect:
  125. xcb_aux_sync(c);
  126. xcb_disconnect(c);
  127. return result;
  128. }
  129. void
  130. root_update_wallpaper(void)
  131. {
  132. xcb_get_property_cookie_t prop_c;
  133. xcb_get_property_reply_t *prop_r;
  134. xcb_get_geometry_cookie_t geom_c;
  135. xcb_get_geometry_reply_t *geom_r;
  136. xcb_pixmap_t *rootpix;
  137. cairo_surface_destroy(globalconf.wallpaper);
  138. globalconf.wallpaper = NULL;
  139. prop_c = xcb_get_property_unchecked(globalconf.connection, false,
  140. globalconf.screen->root, _XROOTPMAP_ID, XCB_ATOM_PIXMAP, 0, 1);
  141. prop_r = xcb_get_property_reply(globalconf.connection, prop_c, NULL);
  142. if (!prop_r || !prop_r->value_len)
  143. {
  144. p_delete(&prop_r);
  145. return;
  146. }
  147. rootpix = xcb_get_property_value(prop_r);
  148. if (!rootpix)
  149. {
  150. p_delete(&prop_r);
  151. return;
  152. }
  153. geom_c = xcb_get_geometry_unchecked(globalconf.connection, *rootpix);
  154. geom_r = xcb_get_geometry_reply(globalconf.connection, geom_c, NULL);
  155. if (!geom_r)
  156. {
  157. p_delete(&prop_r);
  158. return;
  159. }
  160. /* Only the default visual makes sense, so just the default depth */
  161. if (geom_r->depth != draw_visual_depth(globalconf.screen, globalconf.default_visual->visual_id))
  162. warn("Got a pixmap with depth %d, but the default depth is %d, continuing anyway",
  163. geom_r->depth, draw_visual_depth(globalconf.screen, globalconf.default_visual->visual_id));
  164. globalconf.wallpaper = cairo_xcb_surface_create(globalconf.connection,
  165. *rootpix,
  166. globalconf.default_visual,
  167. geom_r->width,
  168. geom_r->height);
  169. p_delete(&prop_r);
  170. p_delete(&geom_r);
  171. }
  172. static xcb_keycode_t
  173. _string_to_key_code(const char *s)
  174. {
  175. xcb_keysym_t keysym;
  176. xcb_keycode_t *keycodes;
  177. keysym = XStringToKeysym(s);
  178. keycodes = xcb_key_symbols_get_keycode(globalconf.keysyms, keysym);
  179. if(keycodes) {
  180. return keycodes[0]; /* XXX only returning the first is probably not
  181. * the best */
  182. } else {
  183. return 0;
  184. }
  185. }
  186. /** Send fake keyboard or mouse events.
  187. *
  188. * Usually the currently focused client or the keybindings will receive those
  189. * events. If a `keygrabber` or `mousegrabber` is running, then it will get them.
  190. *
  191. * Some keys have different names compared to the ones generally used in
  192. * Awesome. For example, Awesome uses "modifier keys" for keybindings using
  193. * their X11 names such as "Control" or "Mod1" (for "Alt"). These are not the
  194. * name of the key but is only the name of the modifier they represent. Some
  195. * modifiers are even present twice on some keyboard like the left and right
  196. * "Shift". Here is a list of the "real" key names matching the modifiers in
  197. * `fake_input`:
  198. *
  199. * <table class='widget_list' border=1>
  200. * <tr style='font-weight: bold;'>
  201. * <th align='center'>Modifier name </th>
  202. * <th align='center'>Key name</th>
  203. * <th align='center'>Other key name</th>
  204. * </tr>
  205. * <tr><td> Mod4</td><td align='center'> Super_L </td><td align='center'> Super_R </td></tr>
  206. * <tr><td> Control </td><td align='center'> Control_L </td><td align='center'> Control_R </td></tr>
  207. * <tr><td> Shift </td><td align='center'> Shift_L </td><td align='center'> Shift_R </td></tr>
  208. * <tr><td> Mod1</td><td align='center'> Alt_L </td><td align='center'> Alt_R </td></tr>
  209. * </table>
  210. *
  211. * Note that this is valid for most of the modern "western" keyboard layouts.
  212. * Some older, custom or foreign layouts may break this convention.
  213. *
  214. * This function is very low level, to be more useful, it can be wrapped into
  215. * higher level constructs such as:
  216. *
  217. * **Sending strings:**
  218. *
  219. * @DOC_text_root_fake_string_EXAMPLE@
  220. *
  221. * Note that this example works for most ASCII inputs but may fail depending on
  222. * how the string is encoded. Some multi-byte characters may not represent
  223. * keys and some UTF-8 encoding format create characters by combining multiple
  224. * elements such as accent + base character or various escape sequences. If you
  225. * wish to use this example for "real world" i18n use cases, learning about
  226. * XKB event and UTF-8 encoding is a prerequisites.
  227. *
  228. * **Clicking:**
  229. *
  230. * ![Client geometry](../images/mouse.svg)
  231. *
  232. * @DOC_text_root_fake_click_EXAMPLE@
  233. *
  234. * @param event_type The event type: key\_press, key\_release, button\_press,
  235. * button\_release or motion\_notify.
  236. * @param detail The detail: in case of a key event, this is the keycode
  237. * to send, in case of a button event this is the number of the button. In
  238. * case of a motion event, this is a boolean value which if true makes the
  239. * coordinates relatives.
  240. * @param x In case of a motion event, this is the X coordinate.
  241. * @param y In case of a motion event, this is the Y coordinate.
  242. * @staticfct fake_input
  243. */
  244. static int
  245. luaA_root_fake_input(lua_State *L)
  246. {
  247. if(!globalconf.have_xtest)
  248. {
  249. luaA_warn(L, "XTest extension is not available, cannot fake input.");
  250. return 0;
  251. }
  252. const char *stype = luaL_checkstring(L, 1);
  253. uint8_t type, detail;
  254. int x = 0, y = 0;
  255. if (A_STREQ(stype, "key_press"))
  256. {
  257. type = XCB_KEY_PRESS;
  258. if(lua_type(L, 2) == LUA_TSTRING) {
  259. detail = _string_to_key_code(lua_tostring(L, 2)); /* keysym */
  260. } else {
  261. detail = luaL_checkinteger(L, 2); /* keycode */
  262. }
  263. }
  264. else if(A_STREQ(stype, "key_release"))
  265. {
  266. type = XCB_KEY_RELEASE;
  267. if(lua_type(L, 2) == LUA_TSTRING) {
  268. detail = _string_to_key_code(lua_tostring(L, 2)); /* keysym */
  269. } else {
  270. detail = luaL_checkinteger(L, 2); /* keycode */
  271. }
  272. }
  273. else if(A_STREQ(stype, "button_press"))
  274. {
  275. type = XCB_BUTTON_PRESS;
  276. detail = luaL_checkinteger(L, 2); /* button number */
  277. }
  278. else if(A_STREQ(stype, "button_release"))
  279. {
  280. type = XCB_BUTTON_RELEASE;
  281. detail = luaL_checkinteger(L, 2); /* button number */
  282. }
  283. else if(A_STREQ(stype, "motion_notify"))
  284. {
  285. type = XCB_MOTION_NOTIFY;
  286. detail = luaA_checkboolean(L, 2); /* relative to the current position or not */
  287. x = round(luaA_checknumber_range(L, 3, MIN_X11_COORDINATE, MAX_X11_COORDINATE));
  288. y = round(luaA_checknumber_range(L, 4, MIN_X11_COORDINATE, MAX_X11_COORDINATE));
  289. }
  290. else
  291. return 0;
  292. xcb_test_fake_input(globalconf.connection,
  293. type,
  294. detail,
  295. 0, /* This is a delay, not a timestamp! */
  296. XCB_NONE,
  297. x, y,
  298. 0);
  299. return 0;
  300. }
  301. /** Get or set global key bindings.
  302. * These bindings will be available when you press keys on the root window
  303. * (the wallpaper).
  304. *
  305. * @property keys
  306. * @param table
  307. * @see awful.key
  308. */
  309. static int
  310. luaA_root_keys(lua_State *L)
  311. {
  312. if(lua_gettop(L) == 1)
  313. {
  314. luaA_checktable(L, 1);
  315. foreach(key, globalconf.keys)
  316. luaA_object_unref(L, *key);
  317. key_array_wipe(&globalconf.keys);
  318. key_array_init(&globalconf.keys);
  319. lua_pushnil(L);
  320. while(lua_next(L, 1))
  321. key_array_append(&globalconf.keys, luaA_object_ref_class(L, -1, &key_class));
  322. xcb_screen_t *s = globalconf.screen;
  323. xwindow_grabkeys(s->root, &globalconf.keys);
  324. return 1;
  325. }
  326. lua_createtable(L, globalconf.keys.len, 0);
  327. for(int i = 0; i < globalconf.keys.len; i++)
  328. {
  329. luaA_object_push(L, globalconf.keys.tab[i]);
  330. lua_rawseti(L, -2, i + 1);
  331. }
  332. return 1;
  333. }
  334. /**
  335. * Store the list of mouse buttons to be applied on the wallpaper (also
  336. * known as root window).
  337. *
  338. * @property buttons
  339. * @tparam[opt={}] table buttons The list of buttons.
  340. * @see awful.button
  341. *
  342. * @usage
  343. * root.buttons = {
  344. * awful.button({ }, 3, function () mymainmenu:toggle() end),
  345. * awful.button({ }, 4, awful.tag.viewnext),
  346. * awful.button({ }, 5, awful.tag.viewprev),
  347. * }
  348. */
  349. static int
  350. luaA_root_buttons(lua_State *L)
  351. {
  352. if(lua_gettop(L) == 1)
  353. {
  354. luaA_checktable(L, 1);
  355. foreach(button, globalconf.buttons)
  356. luaA_object_unref(L, *button);
  357. button_array_wipe(&globalconf.buttons);
  358. button_array_init(&globalconf.buttons);
  359. lua_pushnil(L);
  360. while(lua_next(L, 1))
  361. button_array_append(&globalconf.buttons, luaA_object_ref(L, -1));
  362. return 1;
  363. }
  364. lua_createtable(L, globalconf.buttons.len, 0);
  365. for(int i = 0; i < globalconf.buttons.len; i++)
  366. {
  367. luaA_object_push(L, globalconf.buttons.tab[i]);
  368. lua_rawseti(L, -2, i + 1);
  369. }
  370. return 1;
  371. }
  372. /** Set the root cursor
  373. *
  374. * The possible values are:
  375. *
  376. *@DOC_cursor_c_COMMON@
  377. *
  378. * @param cursor_name A X cursor name.
  379. * @staticfct cursor
  380. */
  381. static int
  382. luaA_root_cursor(lua_State *L)
  383. {
  384. const char *cursor_name = luaL_checkstring(L, 1);
  385. uint16_t cursor_font = xcursor_font_fromstr(cursor_name);
  386. if(cursor_font)
  387. {
  388. uint32_t change_win_vals[] = { xcursor_new(globalconf.cursor_ctx, cursor_font) };
  389. xcb_change_window_attributes(globalconf.connection,
  390. globalconf.screen->root,
  391. XCB_CW_CURSOR,
  392. change_win_vals);
  393. }
  394. else
  395. luaA_warn(L, "invalid cursor %s", cursor_name);
  396. return 0;
  397. }
  398. /** Get the drawins attached to a screen.
  399. *
  400. * @return A table with all drawins.
  401. * @staticfct drawins
  402. */
  403. static int
  404. luaA_root_drawins(lua_State *L)
  405. {
  406. lua_createtable(L, globalconf.drawins.len, 0);
  407. for(int i = 0; i < globalconf.drawins.len; i++)
  408. {
  409. luaA_object_push(L, globalconf.drawins.tab[i]);
  410. lua_rawseti(L, -2, i + 1);
  411. }
  412. return 1;
  413. }
  414. /** Get the wallpaper as a cairo surface or set it as a cairo pattern.
  415. *
  416. * @param pattern A cairo pattern as light userdata
  417. * @return A cairo surface or nothing.
  418. * @staticfct wallpaper
  419. */
  420. static int
  421. luaA_root_wallpaper(lua_State *L)
  422. {
  423. if(lua_gettop(L) == 1)
  424. {
  425. cairo_pattern_t *pattern = (cairo_pattern_t *)lua_touserdata(L, -1);
  426. lua_pushboolean(L, root_set_wallpaper(pattern));
  427. /* Don't return the wallpaper, it's too easy to get memleaks */
  428. return 1;
  429. }
  430. if(globalconf.wallpaper == NULL)
  431. return 0;
  432. /* lua has to make sure this surface gets destroyed */
  433. lua_pushlightuserdata(L, cairo_surface_reference(globalconf.wallpaper));
  434. return 1;
  435. }
  436. /** Get the size of the root window.
  437. *
  438. * @return Width of the root window.
  439. * @return height of the root window.
  440. * @staticfct size
  441. */
  442. static int
  443. luaA_root_size(lua_State *L)
  444. {
  445. lua_pushinteger(L, globalconf.screen->width_in_pixels);
  446. lua_pushinteger(L, globalconf.screen->height_in_pixels);
  447. return 2;
  448. }
  449. /** Get the physical size of the root window, in millimeter.
  450. *
  451. * @return Width of the root window, in millimeters.
  452. * @return height of the root window, in millimeters.
  453. * @staticfct size_mm
  454. */
  455. static int
  456. luaA_root_size_mm(lua_State *L)
  457. {
  458. lua_pushinteger(L, globalconf.screen->width_in_millimeters);
  459. lua_pushinteger(L, globalconf.screen->height_in_millimeters);
  460. return 2;
  461. }
  462. /** Get the attached tags.
  463. * @return A table with all tags.
  464. * @staticfct tags
  465. */
  466. static int
  467. luaA_root_tags(lua_State *L)
  468. {
  469. lua_createtable(L, globalconf.tags.len, 0);
  470. for(int i = 0; i < globalconf.tags.len; i++)
  471. {
  472. luaA_object_push(L, globalconf.tags.tab[i]);
  473. lua_rawseti(L, -2, i + 1);
  474. }
  475. return 1;
  476. }
  477. /**
  478. * Add a custom call handler.
  479. */
  480. static int
  481. luaA_root_set_call_handler(lua_State *L)
  482. {
  483. return luaA_registerfct(L, 1, &miss_call_handler);
  484. }
  485. /**
  486. * Add a custom property handler (getter).
  487. */
  488. static int
  489. luaA_root_set_index_miss_handler(lua_State *L)
  490. {
  491. return luaA_registerfct(L, 1, &miss_index_handler);
  492. }
  493. /**
  494. * Add a custom property handler (setter).
  495. */
  496. static int
  497. luaA_root_set_newindex_miss_handler(lua_State *L)
  498. {
  499. return luaA_registerfct(L, 1, &miss_newindex_handler);
  500. }
  501. /** Root library.
  502. * \param L The Lua VM state.
  503. * \return The number of elements pushed on stack.
  504. * \luastack
  505. */
  506. static int
  507. luaA_root_index(lua_State *L)
  508. {
  509. if (miss_index_handler != LUA_REFNIL)
  510. return luaA_call_handler(L, miss_index_handler);
  511. return luaA_default_index(L);
  512. }
  513. /** Newindex for root.
  514. * \param L The Lua VM state.
  515. * \return The number of elements pushed on stack.
  516. */
  517. static int
  518. luaA_root_newindex(lua_State *L)
  519. {
  520. /* Call the lua root property handler */
  521. if (miss_newindex_handler != LUA_REFNIL)
  522. return luaA_call_handler(L, miss_newindex_handler);
  523. return luaA_default_newindex(L);
  524. }
  525. const struct luaL_Reg awesome_root_methods[] =
  526. {
  527. { "_buttons", luaA_root_buttons },
  528. { "_keys", luaA_root_keys },
  529. { "cursor", luaA_root_cursor },
  530. { "fake_input", luaA_root_fake_input },
  531. { "drawins", luaA_root_drawins },
  532. { "wallpaper", luaA_root_wallpaper },
  533. { "size", luaA_root_size },
  534. { "size_mm", luaA_root_size_mm },
  535. { "tags", luaA_root_tags },
  536. { "__index", luaA_root_index },
  537. { "__newindex", luaA_root_newindex },
  538. { "set_index_miss_handler", luaA_root_set_index_miss_handler},
  539. { "set_call_handler", luaA_root_set_call_handler},
  540. { "set_newindex_miss_handler", luaA_root_set_newindex_miss_handler},
  541. { NULL, NULL }
  542. };
  543. const struct luaL_Reg awesome_root_meta[] =
  544. {
  545. { NULL, NULL }
  546. };
  547. // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80