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>
36 LUA_OBJECT_FUNCS(drawin_class
, drawin_t
, drawin
)
38 /** Kick out systray windows.
41 drawin_systray_kickout(drawin_t
*w
)
43 if(globalconf
.systray
.parent
== w
)
45 /* Who! Check that we're not deleting a drawin with a systray, because it
46 * may be its parent. If so, we reparent to root before, otherwise it will
49 xcb_reparent_window(globalconf
.connection
,
50 globalconf
.systray
.window
,
51 globalconf
.screen
->root
,
54 globalconf
.systray
.parent
= NULL
;
58 /** Destroy all X resources of a drawin.
59 * \param w The drawin to wipe.
62 drawin_wipe_resources(drawin_t
*w
)
67 client_ignore_enterleave_events();
68 /* Make sure we don't accidentally kill the systray window */
69 drawin_systray_kickout(w
);
70 xcb_destroy_window(globalconf
.connection
, w
->window
);
72 client_restore_enterleave_events();
77 xcb_free_pixmap(globalconf
.connection
, w
->pixmap
);
82 cairo_surface_destroy(w
->surface
);
88 drawin_wipe(drawin_t
*drawin
)
90 p_delete(&drawin
->cursor
);
91 drawin_wipe_resources(drawin
);
95 drawin_unref_simplified(drawin_t
**item
)
97 luaA_object_unref(globalconf
.L
, *item
);
101 drawin_update_drawing(drawin_t
*w
)
103 /* Clean up old stuff */
106 /* In case lua still got a reference to the surface, it still won't be
107 * able to do anything with it because we finish it. */
108 cairo_surface_finish(w
->surface
);
109 cairo_surface_destroy(w
->surface
);
112 xcb_free_pixmap(globalconf
.connection
, w
->pixmap
);
114 /* Create a pixmap */
115 xcb_screen_t
*s
= globalconf
.screen
;
116 w
->pixmap
= xcb_generate_id(globalconf
.connection
);
117 xcb_create_pixmap(globalconf
.connection
, globalconf
.default_depth
, w
->pixmap
, s
->root
,
118 w
->geometry
.width
, w
->geometry
.height
);
119 /* and create a surface for that pixmap */
120 w
->surface
= cairo_xcb_surface_create(globalconf
.connection
,
121 w
->pixmap
, globalconf
.visual
,
122 w
->geometry
.width
, w
->geometry
.height
);
123 /* Make sure the pixmap doesn't contain garbage by filling it with black */
124 cairo_t
*cr
= cairo_create(w
->surface
);
125 cairo_set_operator(cr
, CAIRO_OPERATOR_CLEAR
);
130 /** Initialize a drawin.
131 * \param w The drawin to initialize.
134 drawin_init(drawin_t
*w
)
136 xcb_screen_t
*s
= globalconf
.screen
;
138 w
->window
= xcb_generate_id(globalconf
.connection
);
139 xcb_create_window(globalconf
.connection
, globalconf
.default_depth
, w
->window
, s
->root
,
140 w
->geometry
.x
, w
->geometry
.y
,
141 w
->geometry
.width
, w
->geometry
.height
,
142 w
->border_width
, XCB_COPY_FROM_PARENT
, globalconf
.visual
->visual_id
,
143 XCB_CW_BACK_PIXEL
| XCB_CW_BORDER_PIXEL
| XCB_CW_BIT_GRAVITY
144 | XCB_CW_OVERRIDE_REDIRECT
| XCB_CW_EVENT_MASK
| XCB_CW_COLORMAP
,
148 w
->border_color
.pixel
,
149 XCB_GRAVITY_NORTH_WEST
,
151 XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT
152 | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY
| XCB_EVENT_MASK_ENTER_WINDOW
153 | XCB_EVENT_MASK_LEAVE_WINDOW
| XCB_EVENT_MASK_STRUCTURE_NOTIFY
154 | XCB_EVENT_MASK_POINTER_MOTION
| XCB_EVENT_MASK_EXPOSURE
155 | XCB_EVENT_MASK_PROPERTY_CHANGE
,
156 globalconf
.default_cmap
159 drawin_update_drawing(w
);
161 /* Set the right type property */
162 ewmh_update_window_type(w
->window
, window_translate_type(w
->type
));
165 /** Refresh the window content by copying its pixmap data to its window.
166 * \param w The drawin to refresh.
169 drawin_refresh_pixmap(drawin_t
*w
)
171 drawin_refresh_pixmap_partial(w
, 0, 0, w
->geometry
.width
, w
->geometry
.height
);
174 /** Move and/or resize a drawin
175 * \param L The Lua VM state.
176 * \param udx The index of the drawin.
177 * \param geometry The new geometry.
180 drawin_moveresize(lua_State
*L
, int udx
, area_t geometry
)
182 drawin_t
*w
= luaA_checkudata(L
, udx
, &drawin_class
);
185 int number_of_vals
= 0;
186 uint32_t moveresize_win_vals
[4], mask_vals
= 0;
188 if(w
->geometry
.x
!= geometry
.x
)
190 w
->geometry
.x
= moveresize_win_vals
[number_of_vals
++] = geometry
.x
;
191 mask_vals
|= XCB_CONFIG_WINDOW_X
;
194 if(w
->geometry
.y
!= geometry
.y
)
196 w
->geometry
.y
= moveresize_win_vals
[number_of_vals
++] = geometry
.y
;
197 mask_vals
|= XCB_CONFIG_WINDOW_Y
;
200 if(geometry
.width
> 0 && w
->geometry
.width
!= geometry
.width
)
202 w
->geometry
.width
= moveresize_win_vals
[number_of_vals
++] = geometry
.width
;
203 mask_vals
|= XCB_CONFIG_WINDOW_WIDTH
;
206 if(geometry
.height
> 0 && w
->geometry
.height
!= geometry
.height
)
208 w
->geometry
.height
= moveresize_win_vals
[number_of_vals
++] = geometry
.height
;
209 mask_vals
|= XCB_CONFIG_WINDOW_HEIGHT
;
212 if(mask_vals
& (XCB_CONFIG_WINDOW_WIDTH
| XCB_CONFIG_WINDOW_HEIGHT
))
213 drawin_update_drawing(w
);
216 client_ignore_enterleave_events();
219 xcb_configure_window(globalconf
.connection
, w
->window
, mask_vals
, moveresize_win_vals
);
222 client_restore_enterleave_events();
224 w
->screen
= screen_getbycoord(w
->geometry
.x
, w
->geometry
.y
);
226 if(mask_vals
& XCB_CONFIG_WINDOW_X
)
227 luaA_object_emit_signal(L
, udx
, "property::x", 0);
228 if(mask_vals
& XCB_CONFIG_WINDOW_Y
)
229 luaA_object_emit_signal(L
, udx
, "property::y", 0);
230 if(mask_vals
& XCB_CONFIG_WINDOW_WIDTH
)
231 luaA_object_emit_signal(L
, udx
, "property::width", 0);
232 if(mask_vals
& XCB_CONFIG_WINDOW_HEIGHT
)
233 luaA_object_emit_signal(L
, udx
, "property::height", 0);
237 #define DO_DRAWIN_GEOMETRY_CHECK_AND_EMIT(prop) \
238 if(w->geometry.prop != geometry.prop) \
240 w->geometry.prop = geometry.prop; \
241 luaA_object_emit_signal(L, udx, "property::" #prop, 0); \
243 DO_DRAWIN_GEOMETRY_CHECK_AND_EMIT(x
)
244 DO_DRAWIN_GEOMETRY_CHECK_AND_EMIT(y
)
245 DO_DRAWIN_GEOMETRY_CHECK_AND_EMIT(width
)
246 DO_DRAWIN_GEOMETRY_CHECK_AND_EMIT(height
)
247 #undef DO_DRAWIN_GEOMETRY_CHECK_AND_EMIT
251 /** Refresh the window content by copying its pixmap data to its window.
252 * \param drawin The drawin to refresh.
253 * \param x The copy starting point x component.
254 * \param y The copy starting point y component.
255 * \param w The copy width from the x component.
256 * \param h The copy height from the y component.
259 drawin_refresh_pixmap_partial(drawin_t
*drawin
,
260 int16_t x
, int16_t y
,
261 uint16_t w
, uint16_t h
)
263 /* Make cairo do all pending drawing */
264 cairo_surface_flush(drawin
->surface
);
265 xcb_copy_area(globalconf
.connection
, drawin
->pixmap
,
266 drawin
->window
, globalconf
.gc
, x
, y
, x
, y
,
271 drawin_map(drawin_t
*drawin
)
274 client_ignore_enterleave_events();
276 xcb_map_window(globalconf
.connection
, drawin
->window
);
278 client_restore_enterleave_events();
279 /* Stack this drawin correctly */
283 /** Get a drawin by its window.
284 * \param win The window id.
285 * \return A drawin if found, NULL otherwise.
288 drawin_getbywin(xcb_window_t win
)
290 foreach(w
, globalconf
.drawins
)
291 if((*w
)->window
== win
)
296 /** Set a drawin visible or not.
297 * \param L The Lua VM state.
298 * \param udx The drawin.
299 * \param v The visible value.
302 drawin_set_visible(lua_State
*L
, int udx
, bool v
)
304 drawin_t
*drawin
= luaA_checkudata(L
, udx
, &drawin_class
);
305 if(v
!= drawin
->visible
)
316 client_ignore_enterleave_events();
318 xcb_unmap_window(globalconf
.connection
, drawin
->window
);
320 client_restore_enterleave_events();
324 luaA_object_emit_signal(L
, udx
, "property::visible", 0);
328 /** Remove a drawin from a screen.
329 * \param L The Lua VM state.
330 * \param udx drawin to detach from screen.
333 drawin_detach(lua_State
*L
, int udx
)
335 drawin_t
*drawin
= luaA_checkudata(L
, udx
, &drawin_class
);
340 /* save visible state */
342 drawin
->visible
= false;
343 /* restore visibility */
346 drawin_wipe_resources(drawin
);
348 foreach(item
, globalconf
.drawins
)
351 drawin_array_remove(&globalconf
.drawins
, item
);
355 if(strut_has_value(&drawin
->strut
))
356 screen_emit_signal(L
, drawin
->screen
, "property::workarea", 0);
358 drawin
->screen
= NULL
;
359 luaA_object_emit_signal(L
, udx
, "property::screen", 0);
361 luaA_object_unref(globalconf
.L
, drawin
);
365 /** Attach a drawin that is on top of the stack.
366 * \param L The Lua VM state.
367 * \param udx The drawin to attach.
368 * \param s The screen to attach the drawin to.
371 drawin_attach(lua_State
*L
, int udx
, screen_t
*s
)
373 /* duplicate drawin */
374 lua_pushvalue(L
, udx
);
376 drawin_t
*drawin
= luaA_object_ref_class(globalconf
.L
, -1, &drawin_class
);
378 drawin_detach(L
, udx
);
380 /* Set the drawin screen */
383 /* Check that the drawin coordinates matches the screen. */
385 screen_getbycoord(drawin
->geometry
.x
, drawin
->geometry
.y
);
387 /* If it does not match, move it to the screen coordinates */
388 if(cscreen
!= drawin
->screen
)
389 drawin_moveresize(L
, udx
, (area_t
) { .x
= s
->geometry
.x
,
391 .width
= drawin
->geometry
.width
,
392 .height
= drawin
->geometry
.height
});
394 drawin_array_append(&globalconf
.drawins
, drawin
);
398 xwindow_set_cursor(drawin
->window
,
399 xcursor_new(globalconf
.connection
, xcursor_font_fromstr(drawin
->cursor
)));
401 if(drawin
->opacity
!= -1)
402 xwindow_set_opacity(drawin
->window
, drawin
->opacity
);
404 ewmh_update_strut(drawin
->window
, &drawin
->strut
);
409 luaA_object_emit_signal(L
, udx
, "property::screen", 0);
411 if(strut_has_value(&drawin
->strut
))
412 screen_emit_signal(L
, drawin
->screen
, "property::workarea", 0);
415 /** Create a new drawin.
416 * \param L The Lua VM state.
417 * \return The number of elements pushed on stack.
420 luaA_drawin_new(lua_State
*L
)
422 luaA_class_new(L
, &drawin_class
);
424 drawin_t
*w
= luaA_checkudata(L
, -1, &drawin_class
);
432 w
->cursor
= a_strdup("left_ptr");
434 if(!w
->geometry
.width
)
435 w
->geometry
.width
= 1;
437 if(!w
->geometry
.height
)
438 w
->geometry
.height
= 1;
441 w
->type
= _NET_WM_WINDOW_TYPE_NORMAL
;
446 /* Set or get the drawin geometry.
447 * \param L The Lua VM state.
448 * \return The number of elements pushed on stack.
450 * \lparam An optional table with drawin geometry.
451 * \lreturn The drawin geometry.
454 luaA_drawin_geometry(lua_State
*L
)
456 drawin_t
*drawin
= luaA_checkudata(L
, 1, &drawin_class
);
458 if(lua_gettop(L
) == 2)
462 luaA_checktable(L
, 2);
463 wingeom
.x
= luaA_getopt_number(L
, 2, "x", drawin
->geometry
.x
);
464 wingeom
.y
= luaA_getopt_number(L
, 2, "y", drawin
->geometry
.y
);
465 wingeom
.width
= luaA_getopt_number(L
, 2, "width", drawin
->geometry
.width
);
466 wingeom
.height
= luaA_getopt_number(L
, 2, "height", drawin
->geometry
.height
);
468 if(wingeom
.width
> 0 && wingeom
.height
> 0)
469 drawin_moveresize(L
, 1, wingeom
);
472 return luaA_pusharea(L
, drawin
->geometry
);
475 LUA_OBJECT_EXPORT_PROPERTY(drawin
, drawin_t
, ontop
, lua_pushboolean
)
476 LUA_OBJECT_EXPORT_PROPERTY(drawin
, drawin_t
, cursor
, lua_pushstring
)
477 LUA_OBJECT_EXPORT_PROPERTY(drawin
, drawin_t
, visible
, lua_pushboolean
)
480 luaA_drawin_set_x(lua_State
*L
, drawin_t
*drawin
)
482 drawin_moveresize(L
, -3, (area_t
) { .x
= luaL_checknumber(L
, -1),
483 .y
= drawin
->geometry
.y
,
484 .width
= drawin
->geometry
.width
,
485 .height
= drawin
->geometry
.height
});
490 luaA_drawin_get_x(lua_State
*L
, drawin_t
*drawin
)
492 lua_pushnumber(L
, drawin
->geometry
.x
);
497 luaA_drawin_set_y(lua_State
*L
, drawin_t
*drawin
)
499 drawin_moveresize(L
, -3, (area_t
) { .x
= drawin
->geometry
.x
,
500 .y
= luaL_checknumber(L
, -1),
501 .width
= drawin
->geometry
.width
,
502 .height
= drawin
->geometry
.height
});
507 luaA_drawin_get_y(lua_State
*L
, drawin_t
*drawin
)
509 lua_pushnumber(L
, drawin
->geometry
.y
);
514 luaA_drawin_set_width(lua_State
*L
, drawin_t
*drawin
)
516 int width
= luaL_checknumber(L
, -1);
518 luaL_error(L
, "invalid width");
519 drawin_moveresize(L
, -3, (area_t
) { .x
= drawin
->geometry
.x
,
520 .y
= drawin
->geometry
.y
,
522 .height
= drawin
->geometry
.height
});
527 luaA_drawin_get_width(lua_State
*L
, drawin_t
*drawin
)
529 lua_pushnumber(L
, drawin
->geometry
.width
);
534 luaA_drawin_set_height(lua_State
*L
, drawin_t
*drawin
)
536 int height
= luaL_checknumber(L
, -1);
538 luaL_error(L
, "invalid height");
539 drawin_moveresize(L
, -3, (area_t
) { .x
= drawin
->geometry
.x
,
540 .y
= drawin
->geometry
.y
,
541 .width
= drawin
->geometry
.width
,
547 luaA_drawin_get_height(lua_State
*L
, drawin_t
*drawin
)
549 lua_pushnumber(L
, drawin
->geometry
.height
);
553 /** Set the drawin on top status.
554 * \param L The Lua VM state.
555 * \param drawin The drawin object.
556 * \return The number of elements pushed on stack.
559 luaA_drawin_set_ontop(lua_State
*L
, drawin_t
*drawin
)
561 bool b
= luaA_checkboolean(L
, -1);
562 if(b
!= drawin
->ontop
)
566 luaA_object_emit_signal(L
, -3, "property::ontop", 0);
571 /** Set the drawin cursor.
572 * \param L The Lua VM state.
573 * \param drawin The drawin object.
574 * \return The number of elements pushed on stack.
577 luaA_drawin_set_cursor(lua_State
*L
, drawin_t
*drawin
)
579 const char *buf
= luaL_checkstring(L
, -1);
582 uint16_t cursor_font
= xcursor_font_fromstr(buf
);
585 xcb_cursor_t cursor
= xcursor_new(globalconf
.connection
, cursor_font
);
586 p_delete(&drawin
->cursor
);
587 drawin
->cursor
= a_strdup(buf
);
588 xwindow_set_cursor(drawin
->window
, cursor
);
589 luaA_object_emit_signal(L
, -3, "property::cursor", 0);
595 /** Set the drawin screen.
596 * \param L The Lua VM state.
597 * \param drawin The drawin object.
598 * \return The number of elements pushed on stack.
601 luaA_drawin_set_screen(lua_State
*L
, drawin_t
*drawin
)
604 drawin_detach(L
, -3);
607 int screen
= luaL_checknumber(L
, -1) - 1;
608 luaA_checkscreen(screen
);
609 if(!drawin
->screen
|| screen
!= screen_array_indexof(&globalconf
.screens
, drawin
->screen
))
610 drawin_attach(L
, -3, &globalconf
.screens
.tab
[screen
]);
615 /** Get the drawin screen.
616 * \param L The Lua VM state.
617 * \param drawin The drawin object.
618 * \return The number of elements pushed on stack.
621 luaA_drawin_get_screen(lua_State
*L
, drawin_t
*drawin
)
625 lua_pushnumber(L
, screen_array_indexof(&globalconf
.screens
, drawin
->screen
) + 1);
629 /** Set the drawin visibility.
630 * \param L The Lua VM state.
631 * \param drawin The drawin object.
632 * \return The number of elements pushed on stack.
635 luaA_drawin_set_visible(lua_State
*L
, drawin_t
*drawin
)
637 drawin_set_visible(L
, -3, luaA_checkboolean(L
, -1));
641 /** Get a drawin's surface
642 * \param L The Lua VM state.
643 * \param drawin The drawin object.
644 * \return The number of elements pushed on stack.
647 luaA_drawin_get_surface(lua_State
*L
, drawin_t
*drawin
)
649 return oocairo_surface_push(L
, drawin
->surface
);
652 /** Refresh a drawin's content. This has to be called whenever some drawing to
653 * the drawin's surface has been done and should become visible.
654 * \param L The Lua VM state.
655 * \return The number of elements pushed on stack.
658 luaA_drawin_refresh(lua_State
*L
)
660 drawin_t
*drawin
= luaA_checkudata(L
, 1, &drawin_class
);
661 drawin_refresh_pixmap(drawin
);
667 drawin_class_setup(lua_State
*L
)
669 static const struct luaL_reg drawin_methods
[] =
671 LUA_CLASS_METHODS(drawin
)
672 { "__call", luaA_drawin_new
},
676 static const struct luaL_reg drawin_meta
[] =
678 LUA_OBJECT_META(drawin
)
680 { "geometry", luaA_drawin_geometry
},
681 { "refresh", luaA_drawin_refresh
},
685 luaA_class_setup(L
, &drawin_class
, "drawin", &window_class
,
686 (lua_class_allocator_t
) drawin_new
,
687 (lua_class_collector_t
) drawin_wipe
,
689 luaA_class_index_miss_property
, luaA_class_newindex_miss_property
,
690 drawin_methods
, drawin_meta
);
691 luaA_class_add_property(&drawin_class
, "surface",
693 (lua_class_propfunc_t
) luaA_drawin_get_surface
,
695 luaA_class_add_property(&drawin_class
, "visible",
696 (lua_class_propfunc_t
) luaA_drawin_set_visible
,
697 (lua_class_propfunc_t
) luaA_drawin_get_visible
,
698 (lua_class_propfunc_t
) luaA_drawin_set_visible
);
699 luaA_class_add_property(&drawin_class
, "ontop",
700 (lua_class_propfunc_t
) luaA_drawin_set_ontop
,
701 (lua_class_propfunc_t
) luaA_drawin_get_ontop
,
702 (lua_class_propfunc_t
) luaA_drawin_set_ontop
);
703 luaA_class_add_property(&drawin_class
, "screen",
705 (lua_class_propfunc_t
) luaA_drawin_get_screen
,
706 (lua_class_propfunc_t
) luaA_drawin_set_screen
);
707 luaA_class_add_property(&drawin_class
, "cursor",
708 (lua_class_propfunc_t
) luaA_drawin_set_cursor
,
709 (lua_class_propfunc_t
) luaA_drawin_get_cursor
,
710 (lua_class_propfunc_t
) luaA_drawin_set_cursor
);
711 luaA_class_add_property(&drawin_class
, "x",
712 (lua_class_propfunc_t
) luaA_drawin_set_x
,
713 (lua_class_propfunc_t
) luaA_drawin_get_x
,
714 (lua_class_propfunc_t
) luaA_drawin_set_x
);
715 luaA_class_add_property(&drawin_class
, "y",
716 (lua_class_propfunc_t
) luaA_drawin_set_y
,
717 (lua_class_propfunc_t
) luaA_drawin_get_y
,
718 (lua_class_propfunc_t
) luaA_drawin_set_y
);
719 luaA_class_add_property(&drawin_class
, "width",
720 (lua_class_propfunc_t
) luaA_drawin_set_width
,
721 (lua_class_propfunc_t
) luaA_drawin_get_width
,
722 (lua_class_propfunc_t
) luaA_drawin_set_width
);
723 luaA_class_add_property(&drawin_class
, "height",
724 (lua_class_propfunc_t
) luaA_drawin_set_height
,
725 (lua_class_propfunc_t
) luaA_drawin_get_height
,
726 (lua_class_propfunc_t
) luaA_drawin_set_height
);
727 luaA_class_add_property(&drawin_class
, "type",
728 (lua_class_propfunc_t
) luaA_window_set_type
,
729 (lua_class_propfunc_t
) luaA_window_get_type
,
730 (lua_class_propfunc_t
) luaA_window_set_type
);
732 signal_add(&drawin_class
.signals
, "mouse::enter");
733 signal_add(&drawin_class
.signals
, "mouse::leave");
734 signal_add(&drawin_class
.signals
, "property::border_width");
735 signal_add(&drawin_class
.signals
, "property::cursor");
736 signal_add(&drawin_class
.signals
, "property::height");
737 signal_add(&drawin_class
.signals
, "property::ontop");
738 signal_add(&drawin_class
.signals
, "property::screen");
739 signal_add(&drawin_class
.signals
, "property::visible");
740 signal_add(&drawin_class
.signals
, "property::widgets");
741 signal_add(&drawin_class
.signals
, "property::width");
742 signal_add(&drawin_class
.signals
, "property::x");
743 signal_add(&drawin_class
.signals
, "property::y");
746 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80