Add a "deep" option to awful.util.table.clone
[awesome.git] / root.c
blobbbb555413401c05270421453e030a9b928031f0f
1 /*
2 * root.c - root window management
4 * Copyright © 2008-2009 Julien Danjou <julien@danjou.info>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include <X11/keysym.h>
23 #include <X11/XF86keysym.h>
24 #include <xcb/xtest.h>
25 #include <cairo-xcb.h>
27 #include "globalconf.h"
28 #include "objects/button.h"
29 #include "objects/drawin.h"
30 #include "luaa.h"
31 #include "xwindow.h"
32 #include "common/xcursor.h"
33 #include "common/xutil.h"
35 static void
36 root_set_wallpaper_pixmap(xcb_connection_t *c, xcb_pixmap_t p)
38 xcb_get_property_cookie_t prop_c;
39 xcb_get_property_reply_t *prop_r;
40 const xcb_screen_t *screen = globalconf.screen;
42 /* We now have the pattern painted to the pixmap p. Now turn p into the root
43 * window's background pixmap.
45 xcb_change_window_attributes(c, screen->root, XCB_CW_BACK_PIXMAP, &p);
46 xcb_clear_area(c, 0, screen->root, 0, 0, 0, 0);
48 prop_c = xcb_get_property_unchecked(c, false,
49 screen->root, ESETROOT_PMAP_ID, XCB_ATOM_PIXMAP, 0, 1);
51 /* Theoretically, this should be enough to set the wallpaper. However, to
52 * make pseudo-transparency work, clients need a way to get the wallpaper.
53 * You can't query a window's back pixmap, so properties are (ab)used.
55 xcb_change_property(c, XCB_PROP_MODE_REPLACE, screen->root, _XROOTPMAP_ID, XCB_ATOM_PIXMAP, 32, 1, &p);
56 xcb_change_property(c, XCB_PROP_MODE_REPLACE, screen->root, ESETROOT_PMAP_ID, XCB_ATOM_PIXMAP, 32, 1, &p);
58 /* Now make sure that the old wallpaper is freed (but only do this for ESETROOT_PMAP_ID) */
59 prop_r = xcb_get_property_reply(c, prop_c, NULL);
60 if (prop_r && prop_r->value_len)
62 xcb_pixmap_t *rootpix = xcb_get_property_value(prop_r);
63 if (rootpix)
64 xcb_kill_client(c, *rootpix);
66 p_delete(&prop_r);
69 static bool
70 root_set_wallpaper(cairo_pattern_t *pattern)
72 xcb_connection_t *c = xcb_connect(NULL, NULL);
73 xcb_pixmap_t p = xcb_generate_id(c);
74 /* globalconf.connection should be connected to the same X11 server, so we
75 * can just use the info from that other connection.
77 const xcb_screen_t *screen = globalconf.screen;
78 uint16_t width = screen->width_in_pixels;
79 uint16_t height = screen->height_in_pixels;
80 bool result = false;
81 cairo_surface_t *surface;
82 cairo_t *cr;
84 if (xcb_connection_has_error(c))
85 goto disconnect;
87 /* Create a pixmap and make sure it is already created, because we are going
88 * to use it from the other X11 connection (Juggling with X11 connections
89 * is a really, really bad idea).
91 xcb_create_pixmap(c, screen->root_depth, p, screen->root, width, height);
92 xcb_aux_sync(c);
94 /* Now paint to the picture from the main connection so that cairo sees that
95 * it can tell the X server to copy between the (possible) old pixmap and
96 * the new one directly and doesn't need GetImage and PutImage.
98 surface = cairo_xcb_surface_create(globalconf.connection, p, draw_default_visual(screen), width, height);
99 cr = cairo_create(surface);
100 /* Paint the pattern to the surface */
101 cairo_set_source(cr, pattern);
102 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
103 cairo_paint(cr);
104 cairo_destroy(cr);
105 cairo_surface_finish(surface);
106 cairo_surface_destroy(surface);
107 xcb_aux_sync(globalconf.connection);
109 root_set_wallpaper_pixmap(c, p);
111 /* Make sure our pixmap is not destroyed when we disconnect. */
112 xcb_set_close_down_mode(c, XCB_CLOSE_DOWN_RETAIN_PERMANENT);
114 result = true;
115 disconnect:
116 xcb_flush(c);
117 xcb_disconnect(c);
118 return result;
121 static xcb_keycode_t
122 _string_to_key_code(const char *s)
124 xcb_keysym_t keysym;
125 xcb_keycode_t *keycodes;
127 keysym = XStringToKeysym(s);
128 keycodes = xcb_key_symbols_get_keycode(globalconf.keysyms, keysym);
130 if(keycodes) {
131 return keycodes[0]; /* XXX only returning the first is probably not
132 * the best */
133 } else {
134 return 0;
138 /** Send fake events. Usually the current focused client will get it.
139 * \param L The Lua VM state.
140 * \return The number of element pushed on stack.
141 * \luastack
142 * \lparam The event type: key_press, key_release, button_press, button_release
143 * or motion_notify.
144 * \lparam The detail: in case of a key event, this is the keycode to send, in
145 * case of a button event this is the number of the button. In case of a motion
146 * event, this is a boolean value which if true make the coordinates relatives.
147 * \lparam In case of a motion event, this is the X coordinate.
148 * \lparam In case of a motion event, this is the Y coordinate.
149 * If not specified, the current one is used.
151 static int
152 luaA_root_fake_input(lua_State *L)
154 if(!globalconf.have_xtest)
156 luaA_warn(L, "XTest extension is not available, cannot fake input.");
157 return 0;
160 const char *stype = luaL_checkstring(L, 1);
161 uint8_t type, detail;
162 int x = 0, y = 0;
164 if (A_STREQ(stype, "key_press"))
166 type = XCB_KEY_PRESS;
167 if(lua_type(L, 2) == LUA_TSTRING) {
168 detail = _string_to_key_code(lua_tostring(L, 2)); /* keysym */
169 } else {
170 detail = luaL_checknumber(L, 2); /* keycode */
173 else if(A_STREQ(stype, "key_release"))
175 type = XCB_KEY_RELEASE;
176 if(lua_type(L, 2) == LUA_TSTRING) {
177 detail = _string_to_key_code(lua_tostring(L, 2)); /* keysym */
178 } else {
179 detail = luaL_checknumber(L, 2); /* keycode */
182 else if(A_STREQ(stype, "button_press"))
184 type = XCB_BUTTON_PRESS;
185 detail = luaL_checknumber(L, 2); /* button number */
187 else if(A_STREQ(stype, "button_release"))
189 type = XCB_BUTTON_RELEASE;
190 detail = luaL_checknumber(L, 2); /* button number */
192 else if(A_STREQ(stype, "motion_notify"))
194 type = XCB_MOTION_NOTIFY;
195 detail = luaA_checkboolean(L, 2); /* relative to the current position or not */
196 x = luaL_checknumber(L, 3);
197 y = luaL_checknumber(L, 4);
199 else
200 return 0;
202 xcb_test_fake_input(globalconf.connection,
203 type,
204 detail,
205 XCB_CURRENT_TIME,
206 XCB_NONE,
207 x, y,
209 return 0;
212 /** Get or set global key bindings.
213 * This binding will be available when you'll press keys on root window.
214 * \param L The Lua VM state.
215 * \return The number of element pushed on stack.
216 * \luastack
217 * \lparam An array of key bindings objects, or nothing.
218 * \lreturn The array of key bindings objects of this client.
220 static int
221 luaA_root_keys(lua_State *L)
223 if(lua_gettop(L) == 1)
225 luaA_checktable(L, 1);
227 foreach(key, globalconf.keys)
228 luaA_object_unref(globalconf.L, *key);
230 key_array_wipe(&globalconf.keys);
231 key_array_init(&globalconf.keys);
233 lua_pushnil(L);
234 while(lua_next(L, 1))
235 key_array_append(&globalconf.keys, luaA_object_ref_class(L, -1, &key_class));
237 xcb_screen_t *s = globalconf.screen;
238 xcb_ungrab_key(globalconf.connection, XCB_GRAB_ANY, s->root, XCB_BUTTON_MASK_ANY);
239 xwindow_grabkeys(s->root, &globalconf.keys);
241 return 1;
244 lua_createtable(L, globalconf.keys.len, 0);
245 for(int i = 0; i < globalconf.keys.len; i++)
247 luaA_object_push(L, globalconf.keys.tab[i]);
248 lua_rawseti(L, -2, i + 1);
251 return 1;
254 /** Get or set global mouse bindings.
255 * This binding will be available when you'll click on root window.
256 * \param L The Lua VM state.
257 * \return The number of element pushed on stack.
258 * \luastack
259 * \lparam An array of mouse button bindings objects, or nothing.
260 * \lreturn The array of mouse button bindings objects.
262 static int
263 luaA_root_buttons(lua_State *L)
265 if(lua_gettop(L) == 1)
267 luaA_checktable(L, 1);
269 foreach(button, globalconf.buttons)
270 luaA_object_unref(globalconf.L, *button);
272 button_array_wipe(&globalconf.buttons);
273 button_array_init(&globalconf.buttons);
275 lua_pushnil(L);
276 while(lua_next(L, 1))
277 button_array_append(&globalconf.buttons, luaA_object_ref(L, -1));
279 return 1;
282 lua_createtable(L, globalconf.buttons.len, 0);
283 for(int i = 0; i < globalconf.buttons.len; i++)
285 luaA_object_push(L, globalconf.buttons.tab[i]);
286 lua_rawseti(L, -2, i + 1);
289 return 1;
292 /** Set the root cursor.
293 * \param L The Lua VM state.
294 * \return The number of element pushed on stack.
295 * \luastack
296 * \lparam A X cursor name.
298 static int
299 luaA_root_cursor(lua_State *L)
301 const char *cursor_name = luaL_checkstring(L, 1);
302 uint16_t cursor_font = xcursor_font_fromstr(cursor_name);
304 if(cursor_font)
306 uint32_t change_win_vals[] = { xcursor_new(globalconf.cursor_ctx, cursor_font) };
308 xcb_change_window_attributes(globalconf.connection,
309 globalconf.screen->root,
310 XCB_CW_CURSOR,
311 change_win_vals);
313 else
314 luaA_warn(L, "invalid cursor %s", cursor_name);
316 return 0;
319 /** Get the drawins attached to a screen.
320 * \param L The Lua VM state.
321 * \return The number of element pushed on stack.
322 * \luastack
323 * \lreturn A table with all drawins.
325 static int
326 luaA_root_drawins(lua_State *L)
328 lua_createtable(L, globalconf.drawins.len, 0);
330 for(int i = 0; i < globalconf.drawins.len; i++)
332 luaA_object_push(L, globalconf.drawins.tab[i]);
333 lua_rawseti(L, -2, i + 1);
336 return 1;
339 /** Get the screen's wallpaper
340 * \param L The Lua VM state.
341 * \return The number of element pushed on stack.
342 * \luastack
343 * \lreturn A cairo surface for the wallpaper.
345 static int
346 luaA_root_wallpaper(lua_State *L)
348 xcb_get_property_cookie_t prop_c;
349 xcb_get_property_reply_t *prop_r;
350 xcb_get_geometry_cookie_t geom_c;
351 xcb_get_geometry_reply_t *geom_r;
352 xcb_pixmap_t *rootpix;
353 cairo_surface_t *surface;
355 if(lua_gettop(L) == 1)
357 cairo_pattern_t *pattern = (cairo_pattern_t *)lua_touserdata(L, -1);
358 lua_pushboolean(L, root_set_wallpaper(pattern));
359 /* Don't return the wallpaper, it's too easy to get memleaks */
360 return 1;
363 prop_c = xcb_get_property_unchecked(globalconf.connection, false,
364 globalconf.screen->root, _XROOTPMAP_ID, XCB_ATOM_PIXMAP, 0, 1);
365 prop_r = xcb_get_property_reply(globalconf.connection, prop_c, NULL);
367 if (!prop_r || !prop_r->value_len)
369 p_delete(&prop_r);
370 return 0;
373 rootpix = xcb_get_property_value(prop_r);
374 if (!rootpix)
376 p_delete(&prop_r);
377 return 0;
380 geom_c = xcb_get_geometry_unchecked(globalconf.connection, *rootpix);
381 geom_r = xcb_get_geometry_reply(globalconf.connection, geom_c, NULL);
382 if (!geom_r)
384 p_delete(&prop_r);
385 return 0;
388 /* Only the default visual makes sense, so just the default depth */
389 if (geom_r->depth != draw_visual_depth(globalconf.screen, globalconf.default_visual->visual_id))
390 warn("Got a pixmap with depth %d, but the default depth is %d, continuing anyway",
391 geom_r->depth, draw_visual_depth(globalconf.screen, globalconf.default_visual->visual_id));
393 surface = cairo_xcb_surface_create(globalconf.connection, *rootpix, globalconf.default_visual,
394 geom_r->width, geom_r->height);
396 /* lua has to make sure this surface gets destroyed */
397 lua_pushlightuserdata(L, surface);
398 p_delete(&prop_r);
399 p_delete(&geom_r);
400 return 1;
403 /** Get the screen's wallpaper
404 * \param L The Lua VM state.
405 * \return The number of element pushed on stack.
406 * \luastack
407 * \lreturn A cairo surface for the wallpaper.
409 static int
410 luaA_root_tags(lua_State *L)
412 lua_createtable(L, globalconf.tags.len, 0);
413 for(int i = 0; i < globalconf.tags.len; i++)
415 luaA_object_push(L, globalconf.tags.tab[i]);
416 lua_rawseti(L, -2, i + 1);
419 return 1;
422 const struct luaL_Reg awesome_root_lib[] =
424 { "buttons", luaA_root_buttons },
425 { "keys", luaA_root_keys },
426 { "cursor", luaA_root_cursor },
427 { "fake_input", luaA_root_fake_input },
428 { "drawins", luaA_root_drawins },
429 { "wallpaper", luaA_root_wallpaper },
430 { "tags", luaA_root_tags },
431 { NULL, NULL }
434 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80