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.
25 #include "objects/client.h"
31 #include "common/xcursor.h"
32 #include "common/xutil.h"
34 #include <cairo-xcb.h>
35 #include <xcb/shape.h>
37 LUA_OBJECT_FUNCS(drawin_class
, drawin_t
, drawin
)
39 /** Kick out systray windows.
42 drawin_systray_kickout(drawin_t
*w
)
44 if(globalconf
.systray
.parent
== w
)
46 /* Who! Check that we're not deleting a drawin with a systray, because it
47 * may be its parent. If so, we reparent to root before, otherwise it will
50 xcb_reparent_window(globalconf
.connection
,
51 globalconf
.systray
.window
,
52 globalconf
.screen
->root
,
55 globalconf
.systray
.parent
= NULL
;
60 drawin_wipe(drawin_t
*w
)
62 /* The drawin must already be unmapped, else it
63 * couldn't be garbage collected -> no unmap needed */
67 /* Make sure we don't accidentally kill the systray window */
68 drawin_systray_kickout(w
);
69 xcb_destroy_window(globalconf
.connection
, w
->window
);
72 /* No unref needed because we are being garbage collected */
77 drawin_unref_simplified(drawin_t
**item
)
79 luaA_object_unref(globalconf
.L
, *item
);
83 drawin_update_drawing(drawin_t
*w
, int widx
)
85 /* If this drawin isn't visible, we don't need an up-to-date cairo surface
86 * for it. (drawin_map() will later make sure we are called again) */
90 luaA_object_push_item(globalconf
.L
, widx
, w
->drawable
);
91 drawable_set_geometry(w
->drawable
, -1, w
->geometry
);
92 lua_pop(globalconf
.L
, 1);
95 /** Refresh the window content by copying its pixmap data to its window.
96 * \param w The drawin to refresh.
99 drawin_refresh_pixmap(drawin_t
*w
)
101 drawin_refresh_pixmap_partial(w
, 0, 0, w
->geometry
.width
, w
->geometry
.height
);
104 /** Move and/or resize a drawin
105 * \param L The Lua VM state.
106 * \param udx The index of the drawin.
107 * \param geometry The new geometry.
110 drawin_moveresize(lua_State
*L
, int udx
, area_t geometry
)
112 drawin_t
*w
= luaA_checkudata(L
, udx
, &drawin_class
);
113 int number_of_vals
= 0;
114 uint32_t moveresize_win_vals
[4], mask_vals
= 0;
116 if(w
->geometry
.x
!= geometry
.x
)
118 w
->geometry
.x
= moveresize_win_vals
[number_of_vals
++] = geometry
.x
;
119 mask_vals
|= XCB_CONFIG_WINDOW_X
;
122 if(w
->geometry
.y
!= geometry
.y
)
124 w
->geometry
.y
= moveresize_win_vals
[number_of_vals
++] = geometry
.y
;
125 mask_vals
|= XCB_CONFIG_WINDOW_Y
;
128 if(geometry
.width
> 0 && w
->geometry
.width
!= geometry
.width
)
130 w
->geometry
.width
= moveresize_win_vals
[number_of_vals
++] = geometry
.width
;
131 mask_vals
|= XCB_CONFIG_WINDOW_WIDTH
;
134 if(geometry
.height
> 0 && w
->geometry
.height
!= geometry
.height
)
136 w
->geometry
.height
= moveresize_win_vals
[number_of_vals
++] = geometry
.height
;
137 mask_vals
|= XCB_CONFIG_WINDOW_HEIGHT
;
140 if(mask_vals
& (XCB_CONFIG_WINDOW_WIDTH
| XCB_CONFIG_WINDOW_HEIGHT
))
141 drawin_update_drawing(w
, udx
);
143 /* We still have to set x/y */
144 luaA_object_push_item(L
, udx
, w
->drawable
);
145 drawable_set_geometry(w
->drawable
, -1, w
->geometry
);
150 client_ignore_enterleave_events();
153 xcb_configure_window(globalconf
.connection
, w
->window
, mask_vals
, moveresize_win_vals
);
156 client_restore_enterleave_events();
158 if(mask_vals
& XCB_CONFIG_WINDOW_X
)
159 luaA_object_emit_signal(L
, udx
, "property::x", 0);
160 if(mask_vals
& XCB_CONFIG_WINDOW_Y
)
161 luaA_object_emit_signal(L
, udx
, "property::y", 0);
162 if(mask_vals
& XCB_CONFIG_WINDOW_WIDTH
)
163 luaA_object_emit_signal(L
, udx
, "property::width", 0);
164 if(mask_vals
& XCB_CONFIG_WINDOW_HEIGHT
)
165 luaA_object_emit_signal(L
, udx
, "property::height", 0);
168 /** Refresh the window content by copying its pixmap data to its window.
169 * \param drawin The drawin to refresh.
170 * \param x The copy starting point x component.
171 * \param y The copy starting point y component.
172 * \param w The copy width from the x component.
173 * \param h The copy height from the y component.
176 drawin_refresh_pixmap_partial(drawin_t
*drawin
,
177 int16_t x
, int16_t y
,
178 uint16_t w
, uint16_t h
)
180 if (!drawin
->drawable
|| !drawin
->drawable
->pixmap
|| !drawin
->drawable
->refreshed
)
183 /* Make cairo do all pending drawing */
184 cairo_surface_flush(drawin
->drawable
->surface
);
185 xcb_copy_area(globalconf
.connection
, drawin
->drawable
->pixmap
,
186 drawin
->window
, globalconf
.gc
, x
, y
, x
, y
,
191 drawin_map(drawin_t
*drawin
, int widx
)
194 client_ignore_enterleave_events();
196 xcb_map_window(globalconf
.connection
, drawin
->window
);
198 client_restore_enterleave_events();
199 /* Stack this drawin correctly */
201 /* Add it to the list of visible drawins */
202 drawin_array_append(&globalconf
.drawins
, drawin
);
203 /* Make sure it has a surface */
204 if(drawin
->drawable
->surface
== NULL
)
205 drawin_update_drawing(drawin
, widx
);
209 drawin_unmap(drawin_t
*drawin
)
211 xcb_unmap_window(globalconf
.connection
, drawin
->window
);
212 foreach(item
, globalconf
.drawins
)
215 drawin_array_remove(&globalconf
.drawins
, item
);
220 /** Get a drawin by its window.
221 * \param win The window id.
222 * \return A drawin if found, NULL otherwise.
225 drawin_getbywin(xcb_window_t win
)
227 foreach(w
, globalconf
.drawins
)
228 if((*w
)->window
== win
)
233 /** Set a drawin visible or not.
234 * \param L The Lua VM state.
235 * \param udx The drawin.
236 * \param v The visible value.
239 drawin_set_visible(lua_State
*L
, int udx
, bool v
)
241 drawin_t
*drawin
= luaA_checkudata(L
, udx
, &drawin_class
);
242 if(v
!= drawin
->visible
)
248 drawin_map(drawin
, udx
);
249 /* duplicate drawin */
250 lua_pushvalue(L
, udx
);
252 luaA_object_ref_class(globalconf
.L
, -1, &drawin_class
);
257 client_ignore_enterleave_events();
259 drawin_unmap(drawin
);
261 client_restore_enterleave_events();
263 luaA_object_unref(globalconf
.L
, drawin
);
266 luaA_object_emit_signal(L
, udx
, "property::visible", 0);
267 if(strut_has_value(&drawin
->strut
))
270 screen_getbycoord(drawin
->geometry
.x
, drawin
->geometry
.y
);
271 screen_emit_signal(globalconf
.L
, screen
, "property::workarea", 0);
277 drawin_allocator(lua_State
*L
)
279 xcb_screen_t
*s
= globalconf
.screen
;
280 drawin_t
*w
= drawin_new(L
);
285 w
->cursor
= a_strdup("left_ptr");
286 w
->geometry
.width
= 1;
287 w
->geometry
.height
= 1;
288 w
->type
= _NET_WM_WINDOW_TYPE_NORMAL
;
290 drawable_allocator(L
, (drawable_refresh_callback
*) drawin_refresh_pixmap
, w
);
291 w
->drawable
= luaA_object_ref_item(L
, -2, -1);
293 w
->window
= xcb_generate_id(globalconf
.connection
);
294 xcb_create_window(globalconf
.connection
, globalconf
.default_depth
, w
->window
, s
->root
,
295 w
->geometry
.x
, w
->geometry
.y
,
296 w
->geometry
.width
, w
->geometry
.height
,
297 w
->border_width
, XCB_COPY_FROM_PARENT
, globalconf
.visual
->visual_id
,
298 XCB_CW_BORDER_PIXEL
| XCB_CW_BIT_GRAVITY
299 | XCB_CW_OVERRIDE_REDIRECT
| XCB_CW_EVENT_MASK
| XCB_CW_COLORMAP
303 w
->border_color
.pixel
,
304 XCB_GRAVITY_NORTH_WEST
,
306 XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT
307 | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY
| XCB_EVENT_MASK_ENTER_WINDOW
308 | XCB_EVENT_MASK_LEAVE_WINDOW
| XCB_EVENT_MASK_STRUCTURE_NOTIFY
309 | XCB_EVENT_MASK_POINTER_MOTION
| XCB_EVENT_MASK_EXPOSURE
310 | XCB_EVENT_MASK_PROPERTY_CHANGE
,
311 globalconf
.default_cmap
,
312 xcursor_new(globalconf
.cursor_ctx
, xcursor_font_fromstr(w
->cursor
))
315 /* Set the right properties */
316 ewmh_update_window_type(w
->window
, window_translate_type(w
->type
));
317 ewmh_update_strut(w
->window
, &w
->strut
);
322 /** Create a new drawin.
323 * \param L The Lua VM state.
324 * \return The number of elements pushed on stack.
327 luaA_drawin_new(lua_State
*L
)
329 luaA_class_new(L
, &drawin_class
);
334 /* Set or get the drawin geometry.
335 * \param L The Lua VM state.
336 * \return The number of elements pushed on stack.
338 * \lparam An optional table with drawin geometry.
339 * \lreturn The drawin geometry.
342 luaA_drawin_geometry(lua_State
*L
)
344 drawin_t
*drawin
= luaA_checkudata(L
, 1, &drawin_class
);
346 if(lua_gettop(L
) == 2)
350 luaA_checktable(L
, 2);
351 wingeom
.x
= luaA_getopt_number(L
, 2, "x", drawin
->geometry
.x
);
352 wingeom
.y
= luaA_getopt_number(L
, 2, "y", drawin
->geometry
.y
);
353 wingeom
.width
= luaA_getopt_number(L
, 2, "width", drawin
->geometry
.width
);
354 wingeom
.height
= luaA_getopt_number(L
, 2, "height", drawin
->geometry
.height
);
356 if(wingeom
.width
> 0 && wingeom
.height
> 0)
357 drawin_moveresize(L
, 1, wingeom
);
360 return luaA_pusharea(L
, drawin
->geometry
);
364 LUA_OBJECT_EXPORT_PROPERTY(drawin
, drawin_t
, ontop
, lua_pushboolean
)
365 LUA_OBJECT_EXPORT_PROPERTY(drawin
, drawin_t
, cursor
, lua_pushstring
)
366 LUA_OBJECT_EXPORT_PROPERTY(drawin
, drawin_t
, visible
, lua_pushboolean
)
369 luaA_drawin_set_x(lua_State
*L
, drawin_t
*drawin
)
371 drawin_moveresize(L
, -3, (area_t
) { .x
= luaL_checknumber(L
, -1),
372 .y
= drawin
->geometry
.y
,
373 .width
= drawin
->geometry
.width
,
374 .height
= drawin
->geometry
.height
});
379 luaA_drawin_get_x(lua_State
*L
, drawin_t
*drawin
)
381 lua_pushnumber(L
, drawin
->geometry
.x
);
386 luaA_drawin_set_y(lua_State
*L
, drawin_t
*drawin
)
388 drawin_moveresize(L
, -3, (area_t
) { .x
= drawin
->geometry
.x
,
389 .y
= luaL_checknumber(L
, -1),
390 .width
= drawin
->geometry
.width
,
391 .height
= drawin
->geometry
.height
});
396 luaA_drawin_get_y(lua_State
*L
, drawin_t
*drawin
)
398 lua_pushnumber(L
, drawin
->geometry
.y
);
403 luaA_drawin_set_width(lua_State
*L
, drawin_t
*drawin
)
405 int width
= luaL_checknumber(L
, -1);
407 luaL_error(L
, "invalid width");
408 drawin_moveresize(L
, -3, (area_t
) { .x
= drawin
->geometry
.x
,
409 .y
= drawin
->geometry
.y
,
411 .height
= drawin
->geometry
.height
});
416 luaA_drawin_get_width(lua_State
*L
, drawin_t
*drawin
)
418 lua_pushnumber(L
, drawin
->geometry
.width
);
423 luaA_drawin_set_height(lua_State
*L
, drawin_t
*drawin
)
425 int height
= luaL_checknumber(L
, -1);
427 luaL_error(L
, "invalid height");
428 drawin_moveresize(L
, -3, (area_t
) { .x
= drawin
->geometry
.x
,
429 .y
= drawin
->geometry
.y
,
430 .width
= drawin
->geometry
.width
,
436 luaA_drawin_get_height(lua_State
*L
, drawin_t
*drawin
)
438 lua_pushnumber(L
, drawin
->geometry
.height
);
442 /** Set the drawin on top status.
443 * \param L The Lua VM state.
444 * \param drawin The drawin object.
445 * \return The number of elements pushed on stack.
448 luaA_drawin_set_ontop(lua_State
*L
, drawin_t
*drawin
)
450 bool b
= luaA_checkboolean(L
, -1);
451 if(b
!= drawin
->ontop
)
455 luaA_object_emit_signal(L
, -3, "property::ontop", 0);
460 /** Set the drawin cursor.
461 * \param L The Lua VM state.
462 * \param drawin The drawin object.
463 * \return The number of elements pushed on stack.
466 luaA_drawin_set_cursor(lua_State
*L
, drawin_t
*drawin
)
468 const char *buf
= luaL_checkstring(L
, -1);
471 uint16_t cursor_font
= xcursor_font_fromstr(buf
);
474 xcb_cursor_t cursor
= xcursor_new(globalconf
.cursor_ctx
, cursor_font
);
475 p_delete(&drawin
->cursor
);
476 drawin
->cursor
= a_strdup(buf
);
477 xwindow_set_cursor(drawin
->window
, cursor
);
478 luaA_object_emit_signal(L
, -3, "property::cursor", 0);
484 /** Set the drawin visibility.
485 * \param L The Lua VM state.
486 * \param drawin The drawin object.
487 * \return The number of elements pushed on stack.
490 luaA_drawin_set_visible(lua_State
*L
, drawin_t
*drawin
)
492 drawin_set_visible(L
, -3, luaA_checkboolean(L
, -1));
496 /** Get a drawin's drawable
497 * \param L The Lua VM state.
498 * \param drawin The drawin object.
499 * \return The number of elements pushed on stack.
502 luaA_drawin_get_drawable(lua_State
*L
, drawin_t
*drawin
)
504 luaA_object_push_item(L
, -2, drawin
->drawable
);
508 /** Get the drawin's bounding shape.
509 * \param L The Lua VM state.
510 * \param drawin The drawin object.
511 * \return The number of elements pushed on stack.
514 luaA_drawin_get_shape_bounding(lua_State
*L
, drawin_t
*drawin
)
516 cairo_surface_t
*surf
= xwindow_get_shape(drawin
->window
, XCB_SHAPE_SK_BOUNDING
);
519 /* lua has to make sure to free the ref or we have a leak */
520 lua_pushlightuserdata(L
, surf
);
524 /** Set the drawin's bounding shape.
525 * \param L The Lua VM state.
526 * \param drawin The drawin object.
527 * \return The number of elements pushed on stack.
530 luaA_drawin_set_shape_bounding(lua_State
*L
, drawin_t
*drawin
)
532 cairo_surface_t
*surf
= NULL
;
533 if(!lua_isnil(L
, -1))
534 surf
= (cairo_surface_t
*)lua_touserdata(L
, -1);
535 xwindow_set_shape(drawin
->window
,
536 drawin
->geometry
.width
+ 2*drawin
->border_width
,
537 drawin
->geometry
.height
+ 2*drawin
->border_width
,
538 XCB_SHAPE_SK_BOUNDING
, surf
, -drawin
->border_width
);
539 luaA_object_emit_signal(L
, -3, "property::shape_bounding", 0);
543 /** Get the drawin's clip shape.
544 * \param L The Lua VM state.
545 * \param drawin The drawin object.
546 * \return The number of elements pushed on stack.
549 luaA_drawin_get_shape_clip(lua_State
*L
, drawin_t
*drawin
)
551 cairo_surface_t
*surf
= xwindow_get_shape(drawin
->window
, XCB_SHAPE_SK_CLIP
);
554 /* lua has to make sure to free the ref or we have a leak */
555 lua_pushlightuserdata(L
, surf
);
559 /** Set the drawin's clip shape.
560 * \param L The Lua VM state.
561 * \param drawin The drawin object.
562 * \return The number of elements pushed on stack.
565 luaA_drawin_set_shape_clip(lua_State
*L
, drawin_t
*drawin
)
567 cairo_surface_t
*surf
= NULL
;
568 if(!lua_isnil(L
, -1))
569 surf
= (cairo_surface_t
*)lua_touserdata(L
, -1);
570 xwindow_set_shape(drawin
->window
, drawin
->geometry
.width
, drawin
->geometry
.height
,
571 XCB_SHAPE_SK_CLIP
, surf
, 0);
572 luaA_object_emit_signal(L
, -3, "property::shape_clip", 0);
577 drawin_class_setup(lua_State
*L
)
579 static const struct luaL_Reg drawin_methods
[] =
581 LUA_CLASS_METHODS(drawin
)
582 { "__call", luaA_drawin_new
},
586 static const struct luaL_Reg drawin_meta
[] =
588 LUA_OBJECT_META(drawin
)
590 { "geometry", luaA_drawin_geometry
},
594 luaA_class_setup(L
, &drawin_class
, "drawin", &window_class
,
595 (lua_class_allocator_t
) drawin_allocator
,
596 (lua_class_collector_t
) drawin_wipe
,
598 luaA_class_index_miss_property
, luaA_class_newindex_miss_property
,
599 drawin_methods
, drawin_meta
);
600 luaA_class_add_property(&drawin_class
, "drawable",
602 (lua_class_propfunc_t
) luaA_drawin_get_drawable
,
604 luaA_class_add_property(&drawin_class
, "visible",
605 (lua_class_propfunc_t
) luaA_drawin_set_visible
,
606 (lua_class_propfunc_t
) luaA_drawin_get_visible
,
607 (lua_class_propfunc_t
) luaA_drawin_set_visible
);
608 luaA_class_add_property(&drawin_class
, "ontop",
609 (lua_class_propfunc_t
) luaA_drawin_set_ontop
,
610 (lua_class_propfunc_t
) luaA_drawin_get_ontop
,
611 (lua_class_propfunc_t
) luaA_drawin_set_ontop
);
612 luaA_class_add_property(&drawin_class
, "cursor",
613 (lua_class_propfunc_t
) luaA_drawin_set_cursor
,
614 (lua_class_propfunc_t
) luaA_drawin_get_cursor
,
615 (lua_class_propfunc_t
) luaA_drawin_set_cursor
);
616 luaA_class_add_property(&drawin_class
, "x",
617 (lua_class_propfunc_t
) luaA_drawin_set_x
,
618 (lua_class_propfunc_t
) luaA_drawin_get_x
,
619 (lua_class_propfunc_t
) luaA_drawin_set_x
);
620 luaA_class_add_property(&drawin_class
, "y",
621 (lua_class_propfunc_t
) luaA_drawin_set_y
,
622 (lua_class_propfunc_t
) luaA_drawin_get_y
,
623 (lua_class_propfunc_t
) luaA_drawin_set_y
);
624 luaA_class_add_property(&drawin_class
, "width",
625 (lua_class_propfunc_t
) luaA_drawin_set_width
,
626 (lua_class_propfunc_t
) luaA_drawin_get_width
,
627 (lua_class_propfunc_t
) luaA_drawin_set_width
);
628 luaA_class_add_property(&drawin_class
, "height",
629 (lua_class_propfunc_t
) luaA_drawin_set_height
,
630 (lua_class_propfunc_t
) luaA_drawin_get_height
,
631 (lua_class_propfunc_t
) luaA_drawin_set_height
);
632 luaA_class_add_property(&drawin_class
, "type",
633 (lua_class_propfunc_t
) luaA_window_set_type
,
634 (lua_class_propfunc_t
) luaA_window_get_type
,
635 (lua_class_propfunc_t
) luaA_window_set_type
);
636 luaA_class_add_property(&drawin_class
, "shape_bounding",
637 (lua_class_propfunc_t
) luaA_drawin_set_shape_bounding
,
638 (lua_class_propfunc_t
) luaA_drawin_get_shape_bounding
,
639 (lua_class_propfunc_t
) luaA_drawin_set_shape_bounding
);
640 luaA_class_add_property(&drawin_class
, "shape_clip",
641 (lua_class_propfunc_t
) luaA_drawin_set_shape_clip
,
642 (lua_class_propfunc_t
) luaA_drawin_get_shape_clip
,
643 (lua_class_propfunc_t
) luaA_drawin_set_shape_clip
);
645 signal_add(&client_class
.signals
, "property::shape_bounding");
646 signal_add(&client_class
.signals
, "property::shape_clip");
647 signal_add(&drawin_class
.signals
, "property::border_width");
648 signal_add(&drawin_class
.signals
, "property::cursor");
649 signal_add(&drawin_class
.signals
, "property::height");
650 signal_add(&drawin_class
.signals
, "property::ontop");
651 signal_add(&drawin_class
.signals
, "property::visible");
652 signal_add(&drawin_class
.signals
, "property::width");
653 signal_add(&drawin_class
.signals
, "property::x");
654 signal_add(&drawin_class
.signals
, "property::y");
657 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80