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
;
59 drawin_wipe(drawin_t
*w
)
61 /* The drawin must already be unmapped, else it
62 * couldn't be garbage collected -> no unmap needed */
64 cairo_surface_finish(w
->drawable
->surface
);
68 client_ignore_enterleave_events();
69 /* Make sure we don't accidentally kill the systray window */
70 drawin_systray_kickout(w
);
71 xcb_destroy_window(globalconf
.connection
, w
->window
);
73 client_restore_enterleave_events();
78 xcb_free_pixmap(globalconf
.connection
, w
->pixmap
);
81 luaA_object_unref_item(globalconf
.L
, -1, w
->drawable
);
86 drawin_unref_simplified(drawin_t
**item
)
88 luaA_object_unref(globalconf
.L
, *item
);
92 drawin_update_drawing(drawin_t
*w
, int widx
)
94 /* If this drawin isn't visible, we don't need an up-to-date cairo surface
95 * for it. (drawin_map() will later make sure we are called again) */
98 /* Clean up old stuff */
99 luaA_object_push_item(globalconf
.L
, widx
, w
->drawable
);
100 drawable_set_surface(w
->drawable
, -1, NULL
);
102 xcb_free_pixmap(globalconf
.connection
, w
->pixmap
);
104 /* Create a pixmap */
105 xcb_screen_t
*s
= globalconf
.screen
;
106 w
->pixmap
= xcb_generate_id(globalconf
.connection
);
107 xcb_create_pixmap(globalconf
.connection
, globalconf
.default_depth
, w
->pixmap
, s
->root
,
108 w
->geometry
.width
, w
->geometry
.height
);
109 /* and create a surface for that pixmap */
110 cairo_surface_t
*surface
= cairo_xcb_surface_create(globalconf
.connection
,
111 w
->pixmap
, globalconf
.visual
,
112 w
->geometry
.width
, w
->geometry
.height
);
113 drawable_set_surface(w
->drawable
, -1, surface
);
114 drawable_set_geometry(w
->drawable
, -1, w
->geometry
);
115 lua_pop(globalconf
.L
, 1);
118 /** Initialize a drawin.
119 * \param w The drawin to initialize.
122 drawin_init(drawin_t
*w
)
124 xcb_screen_t
*s
= globalconf
.screen
;
126 w
->window
= xcb_generate_id(globalconf
.connection
);
127 xcb_create_window(globalconf
.connection
, globalconf
.default_depth
, w
->window
, s
->root
,
128 w
->geometry
.x
, w
->geometry
.y
,
129 w
->geometry
.width
, w
->geometry
.height
,
130 w
->border_width
, XCB_COPY_FROM_PARENT
, globalconf
.visual
->visual_id
,
131 XCB_CW_BORDER_PIXEL
| XCB_CW_BIT_GRAVITY
132 | XCB_CW_OVERRIDE_REDIRECT
| XCB_CW_EVENT_MASK
| XCB_CW_COLORMAP
136 w
->border_color
.pixel
,
137 XCB_GRAVITY_NORTH_WEST
,
139 XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT
140 | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY
| XCB_EVENT_MASK_ENTER_WINDOW
141 | XCB_EVENT_MASK_LEAVE_WINDOW
| XCB_EVENT_MASK_STRUCTURE_NOTIFY
142 | XCB_EVENT_MASK_POINTER_MOTION
| XCB_EVENT_MASK_EXPOSURE
143 | XCB_EVENT_MASK_PROPERTY_CHANGE
,
144 globalconf
.default_cmap
,
145 xcursor_new(globalconf
.display
, xcursor_font_fromstr(w
->cursor
))
148 /* Set the right properties */
149 ewmh_update_window_type(w
->window
, window_translate_type(w
->type
));
150 ewmh_update_strut(w
->window
, &w
->strut
);
153 /** Refresh the window content by copying its pixmap data to its window.
154 * \param w The drawin to refresh.
157 drawin_refresh_pixmap(drawin_t
*w
)
159 drawin_refresh_pixmap_partial(w
, 0, 0, w
->geometry
.width
, w
->geometry
.height
);
162 /** Move and/or resize a drawin
163 * \param L The Lua VM state.
164 * \param udx The index of the drawin.
165 * \param geometry The new geometry.
168 drawin_moveresize(lua_State
*L
, int udx
, area_t geometry
)
170 drawin_t
*w
= luaA_checkudata(L
, udx
, &drawin_class
);
171 int number_of_vals
= 0;
172 uint32_t moveresize_win_vals
[4], mask_vals
= 0;
174 if(w
->geometry
.x
!= geometry
.x
)
176 w
->geometry
.x
= moveresize_win_vals
[number_of_vals
++] = geometry
.x
;
177 mask_vals
|= XCB_CONFIG_WINDOW_X
;
180 if(w
->geometry
.y
!= geometry
.y
)
182 w
->geometry
.y
= moveresize_win_vals
[number_of_vals
++] = geometry
.y
;
183 mask_vals
|= XCB_CONFIG_WINDOW_Y
;
186 if(geometry
.width
> 0 && w
->geometry
.width
!= geometry
.width
)
188 w
->geometry
.width
= moveresize_win_vals
[number_of_vals
++] = geometry
.width
;
189 mask_vals
|= XCB_CONFIG_WINDOW_WIDTH
;
192 if(geometry
.height
> 0 && w
->geometry
.height
!= geometry
.height
)
194 w
->geometry
.height
= moveresize_win_vals
[number_of_vals
++] = geometry
.height
;
195 mask_vals
|= XCB_CONFIG_WINDOW_HEIGHT
;
198 if(mask_vals
& (XCB_CONFIG_WINDOW_WIDTH
| XCB_CONFIG_WINDOW_HEIGHT
))
199 drawin_update_drawing(w
, udx
);
201 /* We still have to set x/y */
202 luaA_object_push_item(L
, udx
, w
->drawable
);
203 drawable_set_geometry(w
->drawable
, -1, w
->geometry
);
208 client_ignore_enterleave_events();
211 xcb_configure_window(globalconf
.connection
, w
->window
, mask_vals
, moveresize_win_vals
);
214 client_restore_enterleave_events();
216 if(mask_vals
& XCB_CONFIG_WINDOW_X
)
217 luaA_object_emit_signal(L
, udx
, "property::x", 0);
218 if(mask_vals
& XCB_CONFIG_WINDOW_Y
)
219 luaA_object_emit_signal(L
, udx
, "property::y", 0);
220 if(mask_vals
& XCB_CONFIG_WINDOW_WIDTH
)
221 luaA_object_emit_signal(L
, udx
, "property::width", 0);
222 if(mask_vals
& XCB_CONFIG_WINDOW_HEIGHT
)
223 luaA_object_emit_signal(L
, udx
, "property::height", 0);
226 /** Refresh the window content by copying its pixmap data to its window.
227 * \param drawin The drawin to refresh.
228 * \param x The copy starting point x component.
229 * \param y The copy starting point y component.
230 * \param w The copy width from the x component.
231 * \param h The copy height from the y component.
234 drawin_refresh_pixmap_partial(drawin_t
*drawin
,
235 int16_t x
, int16_t y
,
236 uint16_t w
, uint16_t h
)
238 if (!drawin
->visible
)
241 /* Make cairo do all pending drawing */
242 cairo_surface_flush(drawin
->drawable
->surface
);
243 xcb_copy_area(globalconf
.connection
, drawin
->pixmap
,
244 drawin
->window
, globalconf
.gc
, x
, y
, x
, y
,
249 drawin_map(drawin_t
*drawin
, int widx
)
252 client_ignore_enterleave_events();
254 xcb_map_window(globalconf
.connection
, drawin
->window
);
255 drawin_update_drawing(drawin
, widx
);
257 client_restore_enterleave_events();
258 /* Stack this drawin correctly */
260 /* Add it to the list of visible drawins */
261 drawin_array_append(&globalconf
.drawins
, drawin
);
265 drawin_unmap(drawin_t
*drawin
)
267 xcb_unmap_window(globalconf
.connection
, drawin
->window
);
268 foreach(item
, globalconf
.drawins
)
271 drawin_array_remove(&globalconf
.drawins
, item
);
276 /** Get a drawin by its window.
277 * \param win The window id.
278 * \return A drawin if found, NULL otherwise.
281 drawin_getbywin(xcb_window_t win
)
283 foreach(w
, globalconf
.drawins
)
284 if((*w
)->window
== win
)
289 /** Set a drawin visible or not.
290 * \param L The Lua VM state.
291 * \param udx The drawin.
292 * \param v The visible value.
295 drawin_set_visible(lua_State
*L
, int udx
, bool v
)
297 drawin_t
*drawin
= luaA_checkudata(L
, udx
, &drawin_class
);
298 if(v
!= drawin
->visible
)
304 drawin_map(drawin
, udx
);
305 /* duplicate drawin */
306 lua_pushvalue(L
, udx
);
308 luaA_object_ref_class(globalconf
.L
, -1, &drawin_class
);
313 client_ignore_enterleave_events();
315 drawin_unmap(drawin
);
317 client_restore_enterleave_events();
319 luaA_object_unref(globalconf
.L
, drawin
);
322 luaA_object_emit_signal(L
, udx
, "property::visible", 0);
323 if(strut_has_value(&drawin
->strut
))
326 screen_getbycoord(drawin
->geometry
.x
, drawin
->geometry
.y
);
327 screen_emit_signal(globalconf
.L
, screen
, "property::workarea", 0);
333 drawin_allocator(lua_State
*L
)
335 drawin_t
*w
= drawin_new(L
);
340 w
->cursor
= a_strdup("left_ptr");
341 w
->geometry
.width
= 1;
342 w
->geometry
.height
= 1;
343 w
->type
= _NET_WM_WINDOW_TYPE_NORMAL
;
345 drawable_allocator(L
, (drawable_refresh_callback
*) drawin_refresh_pixmap
, w
);
346 w
->drawable
= luaA_object_ref_item(L
, -2, -1);
353 /** Create a new drawin.
354 * \param L The Lua VM state.
355 * \return The number of elements pushed on stack.
358 luaA_drawin_new(lua_State
*L
)
360 luaA_class_new(L
, &drawin_class
);
365 /* Set or get the drawin geometry.
366 * \param L The Lua VM state.
367 * \return The number of elements pushed on stack.
369 * \lparam An optional table with drawin geometry.
370 * \lreturn The drawin geometry.
373 luaA_drawin_geometry(lua_State
*L
)
375 drawin_t
*drawin
= luaA_checkudata(L
, 1, &drawin_class
);
377 if(lua_gettop(L
) == 2)
381 luaA_checktable(L
, 2);
382 wingeom
.x
= luaA_getopt_number(L
, 2, "x", drawin
->geometry
.x
);
383 wingeom
.y
= luaA_getopt_number(L
, 2, "y", drawin
->geometry
.y
);
384 wingeom
.width
= luaA_getopt_number(L
, 2, "width", drawin
->geometry
.width
);
385 wingeom
.height
= luaA_getopt_number(L
, 2, "height", drawin
->geometry
.height
);
387 if(wingeom
.width
> 0 && wingeom
.height
> 0)
388 drawin_moveresize(L
, 1, wingeom
);
391 return luaA_pusharea(L
, drawin
->geometry
);
395 LUA_OBJECT_EXPORT_PROPERTY(drawin
, drawin_t
, ontop
, lua_pushboolean
)
396 LUA_OBJECT_EXPORT_PROPERTY(drawin
, drawin_t
, cursor
, lua_pushstring
)
397 LUA_OBJECT_EXPORT_PROPERTY(drawin
, drawin_t
, visible
, lua_pushboolean
)
400 luaA_drawin_set_x(lua_State
*L
, drawin_t
*drawin
)
402 drawin_moveresize(L
, -3, (area_t
) { .x
= luaL_checknumber(L
, -1),
403 .y
= drawin
->geometry
.y
,
404 .width
= drawin
->geometry
.width
,
405 .height
= drawin
->geometry
.height
});
410 luaA_drawin_get_x(lua_State
*L
, drawin_t
*drawin
)
412 lua_pushnumber(L
, drawin
->geometry
.x
);
417 luaA_drawin_set_y(lua_State
*L
, drawin_t
*drawin
)
419 drawin_moveresize(L
, -3, (area_t
) { .x
= drawin
->geometry
.x
,
420 .y
= luaL_checknumber(L
, -1),
421 .width
= drawin
->geometry
.width
,
422 .height
= drawin
->geometry
.height
});
427 luaA_drawin_get_y(lua_State
*L
, drawin_t
*drawin
)
429 lua_pushnumber(L
, drawin
->geometry
.y
);
434 luaA_drawin_set_width(lua_State
*L
, drawin_t
*drawin
)
436 int width
= luaL_checknumber(L
, -1);
438 luaL_error(L
, "invalid width");
439 drawin_moveresize(L
, -3, (area_t
) { .x
= drawin
->geometry
.x
,
440 .y
= drawin
->geometry
.y
,
442 .height
= drawin
->geometry
.height
});
447 luaA_drawin_get_width(lua_State
*L
, drawin_t
*drawin
)
449 lua_pushnumber(L
, drawin
->geometry
.width
);
454 luaA_drawin_set_height(lua_State
*L
, drawin_t
*drawin
)
456 int height
= luaL_checknumber(L
, -1);
458 luaL_error(L
, "invalid height");
459 drawin_moveresize(L
, -3, (area_t
) { .x
= drawin
->geometry
.x
,
460 .y
= drawin
->geometry
.y
,
461 .width
= drawin
->geometry
.width
,
467 luaA_drawin_get_height(lua_State
*L
, drawin_t
*drawin
)
469 lua_pushnumber(L
, drawin
->geometry
.height
);
473 /** Set the drawin on top status.
474 * \param L The Lua VM state.
475 * \param drawin The drawin object.
476 * \return The number of elements pushed on stack.
479 luaA_drawin_set_ontop(lua_State
*L
, drawin_t
*drawin
)
481 bool b
= luaA_checkboolean(L
, -1);
482 if(b
!= drawin
->ontop
)
486 luaA_object_emit_signal(L
, -3, "property::ontop", 0);
491 /** Set the drawin cursor.
492 * \param L The Lua VM state.
493 * \param drawin The drawin object.
494 * \return The number of elements pushed on stack.
497 luaA_drawin_set_cursor(lua_State
*L
, drawin_t
*drawin
)
499 const char *buf
= luaL_checkstring(L
, -1);
502 uint16_t cursor_font
= xcursor_font_fromstr(buf
);
505 xcb_cursor_t cursor
= xcursor_new(globalconf
.display
, cursor_font
);
506 p_delete(&drawin
->cursor
);
507 drawin
->cursor
= a_strdup(buf
);
508 xwindow_set_cursor(drawin
->window
, cursor
);
509 luaA_object_emit_signal(L
, -3, "property::cursor", 0);
515 /** Set the drawin visibility.
516 * \param L The Lua VM state.
517 * \param drawin The drawin object.
518 * \return The number of elements pushed on stack.
521 luaA_drawin_set_visible(lua_State
*L
, drawin_t
*drawin
)
523 drawin_set_visible(L
, -3, luaA_checkboolean(L
, -1));
527 /** Get a drawin's drawable
528 * \param L The Lua VM state.
529 * \param drawin The drawin object.
530 * \return The number of elements pushed on stack.
533 luaA_drawin_get_drawable(lua_State
*L
, drawin_t
*drawin
)
535 luaA_object_push_item(L
, -2, drawin
->drawable
);
540 drawin_class_setup(lua_State
*L
)
542 static const struct luaL_Reg drawin_methods
[] =
544 LUA_CLASS_METHODS(drawin
)
545 { "__call", luaA_drawin_new
},
549 static const struct luaL_Reg drawin_meta
[] =
551 LUA_OBJECT_META(drawin
)
553 { "geometry", luaA_drawin_geometry
},
557 luaA_class_setup(L
, &drawin_class
, "drawin", &window_class
,
558 (lua_class_allocator_t
) drawin_allocator
,
559 (lua_class_collector_t
) drawin_wipe
,
561 luaA_class_index_miss_property
, luaA_class_newindex_miss_property
,
562 drawin_methods
, drawin_meta
);
563 luaA_class_add_property(&drawin_class
, "drawable",
565 (lua_class_propfunc_t
) luaA_drawin_get_drawable
,
567 luaA_class_add_property(&drawin_class
, "visible",
568 (lua_class_propfunc_t
) luaA_drawin_set_visible
,
569 (lua_class_propfunc_t
) luaA_drawin_get_visible
,
570 (lua_class_propfunc_t
) luaA_drawin_set_visible
);
571 luaA_class_add_property(&drawin_class
, "ontop",
572 (lua_class_propfunc_t
) luaA_drawin_set_ontop
,
573 (lua_class_propfunc_t
) luaA_drawin_get_ontop
,
574 (lua_class_propfunc_t
) luaA_drawin_set_ontop
);
575 luaA_class_add_property(&drawin_class
, "cursor",
576 (lua_class_propfunc_t
) luaA_drawin_set_cursor
,
577 (lua_class_propfunc_t
) luaA_drawin_get_cursor
,
578 (lua_class_propfunc_t
) luaA_drawin_set_cursor
);
579 luaA_class_add_property(&drawin_class
, "x",
580 (lua_class_propfunc_t
) luaA_drawin_set_x
,
581 (lua_class_propfunc_t
) luaA_drawin_get_x
,
582 (lua_class_propfunc_t
) luaA_drawin_set_x
);
583 luaA_class_add_property(&drawin_class
, "y",
584 (lua_class_propfunc_t
) luaA_drawin_set_y
,
585 (lua_class_propfunc_t
) luaA_drawin_get_y
,
586 (lua_class_propfunc_t
) luaA_drawin_set_y
);
587 luaA_class_add_property(&drawin_class
, "width",
588 (lua_class_propfunc_t
) luaA_drawin_set_width
,
589 (lua_class_propfunc_t
) luaA_drawin_get_width
,
590 (lua_class_propfunc_t
) luaA_drawin_set_width
);
591 luaA_class_add_property(&drawin_class
, "height",
592 (lua_class_propfunc_t
) luaA_drawin_set_height
,
593 (lua_class_propfunc_t
) luaA_drawin_get_height
,
594 (lua_class_propfunc_t
) luaA_drawin_set_height
);
595 luaA_class_add_property(&drawin_class
, "type",
596 (lua_class_propfunc_t
) luaA_window_set_type
,
597 (lua_class_propfunc_t
) luaA_window_get_type
,
598 (lua_class_propfunc_t
) luaA_window_set_type
);
600 signal_add(&drawin_class
.signals
, "property::border_width");
601 signal_add(&drawin_class
.signals
, "property::cursor");
602 signal_add(&drawin_class
.signals
, "property::height");
603 signal_add(&drawin_class
.signals
, "property::ontop");
604 signal_add(&drawin_class
.signals
, "property::visible");
605 signal_add(&drawin_class
.signals
, "property::width");
606 signal_add(&drawin_class
.signals
, "property::x");
607 signal_add(&drawin_class
.signals
, "property::y");
610 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80