tag: Improve tag property::index support (FS#1229)
[awesome.git] / objects / drawin.c
blobfe8f94bcd81705bcec6078bb190cb8764035cdb1
1 /*
2 * drawin.c - drawin functions
4 * Copyright © 2008-2009 Julien Danjou <julien@danjou.info>
5 * Copyright © 2010 Uli Schlachter <psychon@znc.in>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "drawin.h"
24 #include "common/atoms.h"
25 #include "common/xcursor.h"
26 #include "ewmh.h"
27 #include "objects/client.h"
28 #include "objects/screen.h"
29 #include "systray.h"
30 #include "xwindow.h"
32 #include <cairo-xcb.h>
33 #include <xcb/shape.h>
35 LUA_OBJECT_FUNCS(drawin_class, drawin_t, drawin)
37 /** Kick out systray windows.
39 static void
40 drawin_systray_kickout(drawin_t *w)
42 if(globalconf.systray.parent == w)
44 /* Who! Check that we're not deleting a drawin with a systray, because it
45 * may be its parent. If so, we reparent to root before, otherwise it will
46 * hurt very much. */
47 systray_cleanup();
48 xcb_reparent_window(globalconf.connection,
49 globalconf.systray.window,
50 globalconf.screen->root,
51 -512, -512);
53 globalconf.systray.parent = NULL;
57 static void
58 drawin_wipe(drawin_t *w)
60 /* The drawin must already be unmapped, else it
61 * couldn't be garbage collected -> no unmap needed */
62 p_delete(&w->cursor);
63 if(w->window)
65 /* Make sure we don't accidentally kill the systray window */
66 drawin_systray_kickout(w);
67 xcb_destroy_window(globalconf.connection, w->window);
68 w->window = XCB_NONE;
70 /* No unref needed because we are being garbage collected */
71 w->drawable = NULL;
74 static void
75 drawin_update_drawing(drawin_t *w, int widx)
77 luaA_object_push_item(globalconf.L, widx, w->drawable);
78 drawable_set_geometry(w->drawable, -1, w->geometry);
79 lua_pop(globalconf.L, 1);
82 /** Refresh the window content by copying its pixmap data to its window.
83 * \param w The drawin to refresh.
85 static inline void
86 drawin_refresh_pixmap(drawin_t *w)
88 drawin_refresh_pixmap_partial(w, 0, 0, w->geometry.width, w->geometry.height);
91 /** Move and/or resize a drawin
92 * \param L The Lua VM state.
93 * \param udx The index of the drawin.
94 * \param geometry The new geometry.
96 static void
97 drawin_moveresize(lua_State *L, int udx, area_t geometry)
99 drawin_t *w = luaA_checkudata(L, udx, &drawin_class);
100 int number_of_vals = 0;
101 uint32_t moveresize_win_vals[4], mask_vals = 0;
103 if(w->geometry.x != geometry.x)
105 w->geometry.x = moveresize_win_vals[number_of_vals++] = geometry.x;
106 mask_vals |= XCB_CONFIG_WINDOW_X;
109 if(w->geometry.y != geometry.y)
111 w->geometry.y = moveresize_win_vals[number_of_vals++] = geometry.y;
112 mask_vals |= XCB_CONFIG_WINDOW_Y;
115 if(geometry.width > 0 && w->geometry.width != geometry.width)
117 w->geometry.width = moveresize_win_vals[number_of_vals++] = geometry.width;
118 mask_vals |= XCB_CONFIG_WINDOW_WIDTH;
121 if(geometry.height > 0 && w->geometry.height != geometry.height)
123 w->geometry.height = moveresize_win_vals[number_of_vals++] = geometry.height;
124 mask_vals |= XCB_CONFIG_WINDOW_HEIGHT;
127 drawin_update_drawing(w, udx);
129 /* Activate BMA */
130 client_ignore_enterleave_events();
132 if(mask_vals)
133 xcb_configure_window(globalconf.connection, w->window, mask_vals, moveresize_win_vals);
135 /* Deactivate BMA */
136 client_restore_enterleave_events();
138 if(mask_vals & XCB_CONFIG_WINDOW_X)
139 luaA_object_emit_signal(L, udx, "property::x", 0);
140 if(mask_vals & XCB_CONFIG_WINDOW_Y)
141 luaA_object_emit_signal(L, udx, "property::y", 0);
142 if(mask_vals & XCB_CONFIG_WINDOW_WIDTH)
143 luaA_object_emit_signal(L, udx, "property::width", 0);
144 if(mask_vals & XCB_CONFIG_WINDOW_HEIGHT)
145 luaA_object_emit_signal(L, udx, "property::height", 0);
148 /** Refresh the window content by copying its pixmap data to its window.
149 * \param drawin The drawin to refresh.
150 * \param x The copy starting point x component.
151 * \param y The copy starting point y component.
152 * \param w The copy width from the x component.
153 * \param h The copy height from the y component.
155 void
156 drawin_refresh_pixmap_partial(drawin_t *drawin,
157 int16_t x, int16_t y,
158 uint16_t w, uint16_t h)
160 if (!drawin->drawable || !drawin->drawable->pixmap || !drawin->drawable->refreshed)
161 return;
163 /* Make cairo do all pending drawing */
164 cairo_surface_flush(drawin->drawable->surface);
165 xcb_copy_area(globalconf.connection, drawin->drawable->pixmap,
166 drawin->window, globalconf.gc, x, y, x, y,
167 w, h);
170 static void
171 drawin_map(drawin_t *drawin, int widx)
173 /* Activate BMA */
174 client_ignore_enterleave_events();
175 /* Map the drawin */
176 xcb_map_window(globalconf.connection, drawin->window);
177 /* Deactivate BMA */
178 client_restore_enterleave_events();
179 /* Stack this drawin correctly */
180 stack_windows();
181 /* Add it to the list of visible drawins */
182 drawin_array_append(&globalconf.drawins, drawin);
183 /* Make sure it has a surface */
184 if(drawin->drawable->surface == NULL)
185 drawin_update_drawing(drawin, widx);
188 static void
189 drawin_unmap(drawin_t *drawin)
191 xcb_unmap_window(globalconf.connection, drawin->window);
192 foreach(item, globalconf.drawins)
193 if(*item == drawin)
195 drawin_array_remove(&globalconf.drawins, item);
196 break;
200 /** Get a drawin by its window.
201 * \param win The window id.
202 * \return A drawin if found, NULL otherwise.
204 drawin_t *
205 drawin_getbywin(xcb_window_t win)
207 foreach(w, globalconf.drawins)
208 if((*w)->window == win)
209 return *w;
210 return NULL;
213 /** Set a drawin visible or not.
214 * \param L The Lua VM state.
215 * \param udx The drawin.
216 * \param v The visible value.
218 static void
219 drawin_set_visible(lua_State *L, int udx, bool v)
221 drawin_t *drawin = luaA_checkudata(L, udx, &drawin_class);
222 if(v != drawin->visible)
224 drawin->visible = v;
226 if(drawin->visible)
228 drawin_map(drawin, udx);
229 /* duplicate drawin */
230 lua_pushvalue(L, udx);
231 /* ref it */
232 luaA_object_ref_class(globalconf.L, -1, &drawin_class);
234 else
236 /* Active BMA */
237 client_ignore_enterleave_events();
238 /* Unmap window */
239 drawin_unmap(drawin);
240 /* Active BMA */
241 client_restore_enterleave_events();
242 /* unref it */
243 luaA_object_unref(globalconf.L, drawin);
246 luaA_object_emit_signal(L, udx, "property::visible", 0);
247 if(strut_has_value(&drawin->strut))
249 luaA_object_push(L, screen_getbycoord(drawin->geometry.x, drawin->geometry.y));
250 luaA_object_emit_signal(L, -1, "property::workarea", 0);
251 lua_pop(L, 1);
256 static drawin_t *
257 drawin_allocator(lua_State *L)
259 xcb_screen_t *s = globalconf.screen;
260 drawin_t *w = drawin_new(L);
262 w->visible = false;
264 w->opacity = -1;
265 w->cursor = a_strdup("left_ptr");
266 w->geometry.width = 1;
267 w->geometry.height = 1;
268 w->type = _NET_WM_WINDOW_TYPE_NORMAL;
270 drawable_allocator(L, (drawable_refresh_callback *) drawin_refresh_pixmap, w);
271 w->drawable = luaA_object_ref_item(L, -2, -1);
273 w->window = xcb_generate_id(globalconf.connection);
274 xcb_create_window(globalconf.connection, globalconf.default_depth, w->window, s->root,
275 w->geometry.x, w->geometry.y,
276 w->geometry.width, w->geometry.height,
277 w->border_width, XCB_COPY_FROM_PARENT, globalconf.visual->visual_id,
278 XCB_CW_BORDER_PIXEL | XCB_CW_BIT_GRAVITY
279 | XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK | XCB_CW_COLORMAP
280 | XCB_CW_CURSOR,
281 (const uint32_t [])
283 w->border_color.pixel,
284 XCB_GRAVITY_NORTH_WEST,
286 XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT
287 | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_ENTER_WINDOW
288 | XCB_EVENT_MASK_LEAVE_WINDOW | XCB_EVENT_MASK_STRUCTURE_NOTIFY
289 | XCB_EVENT_MASK_POINTER_MOTION | XCB_EVENT_MASK_EXPOSURE
290 | XCB_EVENT_MASK_PROPERTY_CHANGE,
291 globalconf.default_cmap,
292 xcursor_new(globalconf.cursor_ctx, xcursor_font_fromstr(w->cursor))
295 /* Set the right properties */
296 ewmh_update_window_type(w->window, window_translate_type(w->type));
297 ewmh_update_strut(w->window, &w->strut);
299 return w;
302 /** Create a new drawin.
303 * \param L The Lua VM state.
304 * \return The number of elements pushed on stack.
306 static int
307 luaA_drawin_new(lua_State *L)
309 luaA_class_new(L, &drawin_class);
311 return 1;
314 /* Set or get the drawin geometry.
315 * \param L The Lua VM state.
316 * \return The number of elements pushed on stack.
317 * \luastack
318 * \lparam An optional table with drawin geometry.
319 * \lreturn The drawin geometry.
321 static int
322 luaA_drawin_geometry(lua_State *L)
324 drawin_t *drawin = luaA_checkudata(L, 1, &drawin_class);
326 if(lua_gettop(L) == 2)
328 area_t wingeom;
330 luaA_checktable(L, 2);
331 wingeom.x = luaA_getopt_number(L, 2, "x", drawin->geometry.x);
332 wingeom.y = luaA_getopt_number(L, 2, "y", drawin->geometry.y);
333 wingeom.width = luaA_getopt_number(L, 2, "width", drawin->geometry.width);
334 wingeom.height = luaA_getopt_number(L, 2, "height", drawin->geometry.height);
336 if(wingeom.width > 0 && wingeom.height > 0)
337 drawin_moveresize(L, 1, wingeom);
340 return luaA_pusharea(L, drawin->geometry);
344 LUA_OBJECT_EXPORT_PROPERTY(drawin, drawin_t, ontop, lua_pushboolean)
345 LUA_OBJECT_EXPORT_PROPERTY(drawin, drawin_t, cursor, lua_pushstring)
346 LUA_OBJECT_EXPORT_PROPERTY(drawin, drawin_t, visible, lua_pushboolean)
348 static int
349 luaA_drawin_set_x(lua_State *L, drawin_t *drawin)
351 drawin_moveresize(L, -3, (area_t) { .x = luaL_checknumber(L, -1),
352 .y = drawin->geometry.y,
353 .width = drawin->geometry.width,
354 .height = drawin->geometry.height });
355 return 0;
358 static int
359 luaA_drawin_get_x(lua_State *L, drawin_t *drawin)
361 lua_pushnumber(L, drawin->geometry.x);
362 return 1;
365 static int
366 luaA_drawin_set_y(lua_State *L, drawin_t *drawin)
368 drawin_moveresize(L, -3, (area_t) { .x = drawin->geometry.x,
369 .y = luaL_checknumber(L, -1),
370 .width = drawin->geometry.width,
371 .height = drawin->geometry.height });
372 return 0;
375 static int
376 luaA_drawin_get_y(lua_State *L, drawin_t *drawin)
378 lua_pushnumber(L, drawin->geometry.y);
379 return 1;
382 static int
383 luaA_drawin_set_width(lua_State *L, drawin_t *drawin)
385 int width = luaL_checknumber(L, -1);
386 if(width <= 0)
387 luaL_error(L, "invalid width");
388 drawin_moveresize(L, -3, (area_t) { .x = drawin->geometry.x,
389 .y = drawin->geometry.y,
390 .width = width,
391 .height = drawin->geometry.height });
392 return 0;
395 static int
396 luaA_drawin_get_width(lua_State *L, drawin_t *drawin)
398 lua_pushnumber(L, drawin->geometry.width);
399 return 1;
402 static int
403 luaA_drawin_set_height(lua_State *L, drawin_t *drawin)
405 int height = luaL_checknumber(L, -1);
406 if(height <= 0)
407 luaL_error(L, "invalid height");
408 drawin_moveresize(L, -3, (area_t) { .x = drawin->geometry.x,
409 .y = drawin->geometry.y,
410 .width = drawin->geometry.width,
411 .height = height });
412 return 0;
415 static int
416 luaA_drawin_get_height(lua_State *L, drawin_t *drawin)
418 lua_pushnumber(L, drawin->geometry.height);
419 return 1;
422 /** Set the drawin on top status.
423 * \param L The Lua VM state.
424 * \param drawin The drawin object.
425 * \return The number of elements pushed on stack.
427 static int
428 luaA_drawin_set_ontop(lua_State *L, drawin_t *drawin)
430 bool b = luaA_checkboolean(L, -1);
431 if(b != drawin->ontop)
433 drawin->ontop = b;
434 stack_windows();
435 luaA_object_emit_signal(L, -3, "property::ontop", 0);
437 return 0;
440 /** Set the drawin cursor.
441 * \param L The Lua VM state.
442 * \param drawin The drawin object.
443 * \return The number of elements pushed on stack.
445 static int
446 luaA_drawin_set_cursor(lua_State *L, drawin_t *drawin)
448 const char *buf = luaL_checkstring(L, -1);
449 if(buf)
451 uint16_t cursor_font = xcursor_font_fromstr(buf);
452 if(cursor_font)
454 xcb_cursor_t cursor = xcursor_new(globalconf.cursor_ctx, cursor_font);
455 p_delete(&drawin->cursor);
456 drawin->cursor = a_strdup(buf);
457 xwindow_set_cursor(drawin->window, cursor);
458 luaA_object_emit_signal(L, -3, "property::cursor", 0);
461 return 0;
464 /** Set the drawin visibility.
465 * \param L The Lua VM state.
466 * \param drawin The drawin object.
467 * \return The number of elements pushed on stack.
469 static int
470 luaA_drawin_set_visible(lua_State *L, drawin_t *drawin)
472 drawin_set_visible(L, -3, luaA_checkboolean(L, -1));
473 return 0;
476 /** Get a drawin's drawable
477 * \param L The Lua VM state.
478 * \param drawin The drawin object.
479 * \return The number of elements pushed on stack.
481 static int
482 luaA_drawin_get_drawable(lua_State *L, drawin_t *drawin)
484 luaA_object_push_item(L, -2, drawin->drawable);
485 return 1;
488 /** Get the drawin's bounding shape.
489 * \param L The Lua VM state.
490 * \param drawin The drawin object.
491 * \return The number of elements pushed on stack.
493 static int
494 luaA_drawin_get_shape_bounding(lua_State *L, drawin_t *drawin)
496 cairo_surface_t *surf = xwindow_get_shape(drawin->window, XCB_SHAPE_SK_BOUNDING);
497 if (!surf)
498 return 0;
499 /* lua has to make sure to free the ref or we have a leak */
500 lua_pushlightuserdata(L, surf);
501 return 1;
504 /** Set the drawin's bounding shape.
505 * \param L The Lua VM state.
506 * \param drawin The drawin object.
507 * \return The number of elements pushed on stack.
509 static int
510 luaA_drawin_set_shape_bounding(lua_State *L, drawin_t *drawin)
512 cairo_surface_t *surf = NULL;
513 if(!lua_isnil(L, -1))
514 surf = (cairo_surface_t *)lua_touserdata(L, -1);
515 xwindow_set_shape(drawin->window,
516 drawin->geometry.width + 2*drawin->border_width,
517 drawin->geometry.height + 2*drawin->border_width,
518 XCB_SHAPE_SK_BOUNDING, surf, -drawin->border_width);
519 luaA_object_emit_signal(L, -3, "property::shape_bounding", 0);
520 return 0;
523 /** Get the drawin's clip shape.
524 * \param L The Lua VM state.
525 * \param drawin The drawin object.
526 * \return The number of elements pushed on stack.
528 static int
529 luaA_drawin_get_shape_clip(lua_State *L, drawin_t *drawin)
531 cairo_surface_t *surf = xwindow_get_shape(drawin->window, XCB_SHAPE_SK_CLIP);
532 if (!surf)
533 return 0;
534 /* lua has to make sure to free the ref or we have a leak */
535 lua_pushlightuserdata(L, surf);
536 return 1;
539 /** Set the drawin's clip shape.
540 * \param L The Lua VM state.
541 * \param drawin The drawin object.
542 * \return The number of elements pushed on stack.
544 static int
545 luaA_drawin_set_shape_clip(lua_State *L, drawin_t *drawin)
547 cairo_surface_t *surf = NULL;
548 if(!lua_isnil(L, -1))
549 surf = (cairo_surface_t *)lua_touserdata(L, -1);
550 xwindow_set_shape(drawin->window, drawin->geometry.width, drawin->geometry.height,
551 XCB_SHAPE_SK_CLIP, surf, 0);
552 luaA_object_emit_signal(L, -3, "property::shape_clip", 0);
553 return 0;
556 void
557 drawin_class_setup(lua_State *L)
559 static const struct luaL_Reg drawin_methods[] =
561 LUA_CLASS_METHODS(drawin)
562 { "__call", luaA_drawin_new },
563 { NULL, NULL }
566 static const struct luaL_Reg drawin_meta[] =
568 LUA_OBJECT_META(drawin)
569 LUA_CLASS_META
570 { "geometry", luaA_drawin_geometry },
571 { NULL, NULL },
574 luaA_class_setup(L, &drawin_class, "drawin", &window_class,
575 (lua_class_allocator_t) drawin_allocator,
576 (lua_class_collector_t) drawin_wipe,
577 NULL,
578 luaA_class_index_miss_property, luaA_class_newindex_miss_property,
579 drawin_methods, drawin_meta);
580 luaA_class_add_property(&drawin_class, "drawable",
581 NULL,
582 (lua_class_propfunc_t) luaA_drawin_get_drawable,
583 NULL);
584 luaA_class_add_property(&drawin_class, "visible",
585 (lua_class_propfunc_t) luaA_drawin_set_visible,
586 (lua_class_propfunc_t) luaA_drawin_get_visible,
587 (lua_class_propfunc_t) luaA_drawin_set_visible);
588 luaA_class_add_property(&drawin_class, "ontop",
589 (lua_class_propfunc_t) luaA_drawin_set_ontop,
590 (lua_class_propfunc_t) luaA_drawin_get_ontop,
591 (lua_class_propfunc_t) luaA_drawin_set_ontop);
592 luaA_class_add_property(&drawin_class, "cursor",
593 (lua_class_propfunc_t) luaA_drawin_set_cursor,
594 (lua_class_propfunc_t) luaA_drawin_get_cursor,
595 (lua_class_propfunc_t) luaA_drawin_set_cursor);
596 luaA_class_add_property(&drawin_class, "x",
597 (lua_class_propfunc_t) luaA_drawin_set_x,
598 (lua_class_propfunc_t) luaA_drawin_get_x,
599 (lua_class_propfunc_t) luaA_drawin_set_x);
600 luaA_class_add_property(&drawin_class, "y",
601 (lua_class_propfunc_t) luaA_drawin_set_y,
602 (lua_class_propfunc_t) luaA_drawin_get_y,
603 (lua_class_propfunc_t) luaA_drawin_set_y);
604 luaA_class_add_property(&drawin_class, "width",
605 (lua_class_propfunc_t) luaA_drawin_set_width,
606 (lua_class_propfunc_t) luaA_drawin_get_width,
607 (lua_class_propfunc_t) luaA_drawin_set_width);
608 luaA_class_add_property(&drawin_class, "height",
609 (lua_class_propfunc_t) luaA_drawin_set_height,
610 (lua_class_propfunc_t) luaA_drawin_get_height,
611 (lua_class_propfunc_t) luaA_drawin_set_height);
612 luaA_class_add_property(&drawin_class, "type",
613 (lua_class_propfunc_t) luaA_window_set_type,
614 (lua_class_propfunc_t) luaA_window_get_type,
615 (lua_class_propfunc_t) luaA_window_set_type);
616 luaA_class_add_property(&drawin_class, "shape_bounding",
617 (lua_class_propfunc_t) luaA_drawin_set_shape_bounding,
618 (lua_class_propfunc_t) luaA_drawin_get_shape_bounding,
619 (lua_class_propfunc_t) luaA_drawin_set_shape_bounding);
620 luaA_class_add_property(&drawin_class, "shape_clip",
621 (lua_class_propfunc_t) luaA_drawin_set_shape_clip,
622 (lua_class_propfunc_t) luaA_drawin_get_shape_clip,
623 (lua_class_propfunc_t) luaA_drawin_set_shape_clip);
625 signal_add(&drawin_class.signals, "property::shape_bounding");
626 signal_add(&drawin_class.signals, "property::shape_clip");
627 signal_add(&drawin_class.signals, "property::border_width");
628 signal_add(&drawin_class.signals, "property::cursor");
629 signal_add(&drawin_class.signals, "property::height");
630 signal_add(&drawin_class.signals, "property::ontop");
631 signal_add(&drawin_class.signals, "property::visible");
632 signal_add(&drawin_class.signals, "property::width");
633 signal_add(&drawin_class.signals, "property::x");
634 signal_add(&drawin_class.signals, "property::y");
637 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80