1 /*
2  * drawin.c - drawin functions
3  *
4  * Copyright © 2008-2009 Julien Danjou <julien@danjou.info>
5  * Copyright ©      2010 Uli Schlachter <psychon@znc.in>
6  *
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.
11  *
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.
16  *
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.
20  *
21  */
22 
23 /** awesome drawin API
24  *
25  * Furthermore to the classes described here, one can also use signals as
26  * described in @{signals} and X properties as described in @{xproperties}.
27  *
28  * @author Julien Danjou &lt;julien@danjou.info&gt;
29  * @copyright 2008-2009 Julien Danjou
30  * @classmod drawin
31  */
32 
33 #include "drawin.h"
34 #include "common/atoms.h"
35 #include "common/xcursor.h"
36 #include "common/xutil.h"
37 #include "event.h"
38 #include "ewmh.h"
39 #include "objects/client.h"
40 #include "objects/screen.h"
41 #include "systray.h"
42 #include "xwindow.h"
43 
44 #include "math.h"
45 
46 #include <cairo-xcb.h>
47 #include <xcb/shape.h>
48 
49 lua_class_t drawin_class;
50 
51 /** Drawin object.
52  *
53  * @field border_width Border width.
54  * @field border_color Border color.
55  * @field ontop On top of other windows.
56  * @field cursor The mouse cursor.
57  * @field visible Visibility.
58  * @field opacity The opacity of the drawin, between 0 and 1.
59  * @field type The window type (desktop, normal, dock, …).
60  * @field x The x coordinates.
61  * @field y The y coordinates.
62  * @field width The width of the drawin.
63  * @field height The height of the drawin.
64  * @field drawable The drawin's drawable.
65  * @field window The X window id.
66  * @field shape_bounding The drawin's bounding shape as a (native) cairo surface.
67  * @field shape_clip The drawin's clip shape as a (native) cairo surface.
68  * @field shape_input The drawin's input shape as a (native) cairo surface.
69  * @table drawin
70  */
71 
72 /**
73  * @signal property::geometry
74  */
75 
76 /**
77  * @signal property::shape_bounding
78  */
79 
80 /**
81  * @signal property::shape_clip
82  */
83 
84 /**
85  * @signal property::shape_input
86  */
87 
88 /**
89  * @signal property::border_width
90  */
91 
92 /**
93  * @signal property::cursor
94  */
95 
96 /**
97  * @signal property::height
98  */
99 
100 /**
101  * @signal property::ontop
102  */
103 
104 /**
105  * @signal property::visible
106  */
107 
108 /**
109  * @signal property::width
110  */
111 
112 /**
113  * @signal property::x
114  */
115 
116 /**
117  * @signal property::y
118  */
119 
120 /** Get or set mouse buttons bindings to a drawin.
121  *
122  * @param buttons_table A table of buttons objects, or nothing.
123  * @function buttons
124  */
125 
126 /** Get or set drawin struts.
127  *
128  * @param strut A table with new strut, or nothing
129  * @return The drawin strut in a table.
130  * @function struts
131  */
132 
133 /** Get the number of instances.
134  *
135  * @return The number of drawin objects alive.
136  * @function instances
137  */
138 
139 /** Set a __index metamethod for all drawin instances.
140  * @tparam function cb The meta-method
141  * @function set_index_miss_handler
142  */
143 
144 /** Set a __newindex metamethod for all drawin instances.
145  * @tparam function cb The meta-method
146  * @function set_newindex_miss_handler
147  */
148 
LUA_OBJECT_FUNCS(drawin_class,drawin_t,drawin)149 LUA_OBJECT_FUNCS(drawin_class, drawin_t, drawin)
150 
151 /** Kick out systray windows.
152  */
153 static void
154 drawin_systray_kickout(drawin_t *w)
155 {
156     if(globalconf.systray.parent == w)
157     {
158         /* Who! Check that we're not deleting a drawin with a systray, because it
159          * may be its parent. If so, we reparent to root before, otherwise it will
160          * hurt very much. */
161         xcb_reparent_window(globalconf.connection,
162                             globalconf.systray.window,
163                             globalconf.screen->root,
164                             -512, -512);
165 
166         globalconf.systray.parent = NULL;
167     }
168 }
169 
170 void
luaA_drawin_systray_kickout(lua_State * L)171 luaA_drawin_systray_kickout(lua_State *L)
172 {
173     drawin_systray_kickout(luaA_checkudata(L, 1, &drawin_class));
174 }
175 
176 static void
drawin_wipe(drawin_t * w)177 drawin_wipe(drawin_t *w)
178 {
179     /* The drawin must already be unmapped, else it
180      * couldn't be garbage collected -> no unmap needed */
181     p_delete(&w->cursor);
182     if(w->window)
183     {
184         /* Make sure we don't accidentally kill the systray window */
185         drawin_systray_kickout(w);
186         xcb_destroy_window(globalconf.connection, w->window);
187         w->window = XCB_NONE;
188     }
189     /* No unref needed because we are being garbage collected */
190     w->drawable = NULL;
191 }
192 
193 static void
drawin_update_drawing(lua_State * L,int widx)194 drawin_update_drawing(lua_State *L, int widx)
195 {
196     drawin_t *w = luaA_checkudata(L, widx, &drawin_class);
197     luaA_object_push_item(L, widx, w->drawable);
198     drawable_set_geometry(L, -1, w->geometry);
199     lua_pop(L, 1);
200 }
201 
202 /** Refresh the window content by copying its pixmap data to its window.
203  * \param w The drawin to refresh.
204  */
205 static inline void
drawin_refresh_pixmap(drawin_t * w)206 drawin_refresh_pixmap(drawin_t *w)
207 {
208     drawin_refresh_pixmap_partial(w, 0, 0, w->geometry.width, w->geometry.height);
209 }
210 
211 static void
drawin_apply_moveresize(drawin_t * w)212 drawin_apply_moveresize(drawin_t *w)
213 {
214     if (!w->geometry_dirty)
215         return;
216 
217     w->geometry_dirty = false;
218     client_ignore_enterleave_events();
219     xcb_configure_window(globalconf.connection, w->window,
220                          XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y
221                          | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT,
222                          (const uint32_t [])
223                          {
224                              w->geometry.x,
225                              w->geometry.y,
226                              w->geometry.width,
227                              w->geometry.height
228                          });
229     client_restore_enterleave_events();
230 }
231 
232 void
drawin_refresh(void)233 drawin_refresh(void)
234 {
235     foreach(item, globalconf.drawins)
236     {
237         drawin_apply_moveresize(*item);
238         window_border_refresh((window_t *) *item);
239     }
240 }
241 
242 /** Get all drawins into a table.
243  * @treturn table A table with drawins.
244  * @function get
245  */
246 static int
luaA_drawin_get(lua_State * L)247 luaA_drawin_get(lua_State *L)
248 {
249     int i = 1;
250 
251     lua_newtable(L);
252 
253     foreach(d, globalconf.drawins) {
254         luaA_object_push(L, *d);
255         lua_rawseti(L, -2, i++);
256     }
257 
258     return 1;
259 }
260 
261 /** Move and/or resize a drawin
262  * \param L The Lua VM state.
263  * \param udx The index of the drawin.
264  * \param geometry The new geometry.
265  */
266 static void
drawin_moveresize(lua_State * L,int udx,area_t geometry)267 drawin_moveresize(lua_State *L, int udx, area_t geometry)
268 {
269     drawin_t *w = luaA_checkudata(L, udx, &drawin_class);
270     area_t old_geometry = w->geometry;
271 
272     w->geometry = geometry;
273     if(w->geometry.width <= 0)
274         w->geometry.width = old_geometry.width;
275     if(w->geometry.height <= 0)
276         w->geometry.height = old_geometry.height;
277 
278     w->geometry_dirty = true;
279     drawin_update_drawing(L, udx);
280 
281     if (!AREA_EQUAL(old_geometry, w->geometry))
282         luaA_object_emit_signal(L, udx, "property::geometry", 0);
283     if (old_geometry.x != w->geometry.x)
284         luaA_object_emit_signal(L, udx, "property::x", 0);
285     if (old_geometry.y != w->geometry.y)
286         luaA_object_emit_signal(L, udx, "property::y", 0);
287     if (old_geometry.width != w->geometry.width)
288         luaA_object_emit_signal(L, udx, "property::width", 0);
289     if (old_geometry.height != w->geometry.height)
290         luaA_object_emit_signal(L, udx, "property::height", 0);
291 
292     screen_t *old_screen = screen_getbycoord(old_geometry.x, old_geometry.y);
293     screen_t *new_screen = screen_getbycoord(w->geometry.x, w->geometry.y);
294     if (old_screen != new_screen && strut_has_value(&w->strut))
295     {
296         screen_update_workarea(old_screen);
297         screen_update_workarea(new_screen);
298     }
299 }
300 
301 /** Refresh the window content by copying its pixmap data to its window.
302  * \param drawin The drawin to refresh.
303  * \param x The copy starting point x component.
304  * \param y The copy starting point y component.
305  * \param w The copy width from the x component.
306  * \param h The copy height from the y component.
307  */
308 void
drawin_refresh_pixmap_partial(drawin_t * drawin,int16_t x,int16_t y,uint16_t w,uint16_t h)309 drawin_refresh_pixmap_partial(drawin_t *drawin,
310                               int16_t x, int16_t y,
311                               uint16_t w, uint16_t h)
312 {
313     if (!drawin->drawable || !drawin->drawable->pixmap || !drawin->drawable->refreshed)
314         return;
315 
316     /* Make sure it really has the size it should have */
317     drawin_apply_moveresize(drawin);
318 
319     /* Make cairo do all pending drawing */
320     cairo_surface_flush(drawin->drawable->surface);
321     xcb_copy_area(globalconf.connection, drawin->drawable->pixmap,
322                   drawin->window, globalconf.gc, x, y, x, y,
323                   w, h);
324 }
325 
326 static void
drawin_map(lua_State * L,int widx)327 drawin_map(lua_State *L, int widx)
328 {
329     drawin_t *drawin = luaA_checkudata(L, widx, &drawin_class);
330     /* Apply any pending changes */
331     drawin_apply_moveresize(drawin);
332     /* Activate BMA */
333     client_ignore_enterleave_events();
334     /* Map the drawin */
335     xcb_map_window(globalconf.connection, drawin->window);
336     /* Deactivate BMA */
337     client_restore_enterleave_events();
338     /* Stack this drawin correctly */
339     stack_windows();
340     /* Add it to the list of visible drawins */
341     drawin_array_append(&globalconf.drawins, drawin);
342     /* Make sure it has a surface */
343     if(drawin->drawable->surface == NULL)
344         drawin_update_drawing(L, widx);
345 }
346 
347 static void
drawin_unmap(drawin_t * drawin)348 drawin_unmap(drawin_t *drawin)
349 {
350     xcb_unmap_window(globalconf.connection, drawin->window);
351     foreach(item, globalconf.drawins)
352         if(*item == drawin)
353         {
354             drawin_array_remove(&globalconf.drawins, item);
355             break;
356         }
357 }
358 
359 /** Get a drawin by its window.
360  * \param win The window id.
361  * \return A drawin if found, NULL otherwise.
362  */
363 drawin_t *
drawin_getbywin(xcb_window_t win)364 drawin_getbywin(xcb_window_t win)
365 {
366     foreach(w, globalconf.drawins)
367         if((*w)->window == win)
368             return *w;
369     return NULL;
370 }
371 
372 /** Set a drawin visible or not.
373  * \param L The Lua VM state.
374  * \param udx The drawin.
375  * \param v The visible value.
376  */
377 static void
drawin_set_visible(lua_State * L,int udx,bool v)378 drawin_set_visible(lua_State *L, int udx, bool v)
379 {
380     drawin_t *drawin = luaA_checkudata(L, udx, &drawin_class);
381     if(v != drawin->visible)
382     {
383         drawin->visible = v;
384 
385         if(drawin->visible)
386         {
387             drawin_map(L, udx);
388             /* duplicate drawin */
389             lua_pushvalue(L, udx);
390             /* ref it */
391             luaA_object_ref_class(L, -1, &drawin_class);
392         }
393         else
394         {
395             /* Active BMA */
396             client_ignore_enterleave_events();
397             /* Unmap window */
398             drawin_unmap(drawin);
399             /* Active BMA */
400             client_restore_enterleave_events();
401             /* unref it */
402             luaA_object_unref(L, drawin);
403         }
404 
405         luaA_object_emit_signal(L, udx, "property::visible", 0);
406         if(strut_has_value(&drawin->strut))
407         {
408             screen_update_workarea(
409                     screen_getbycoord(drawin->geometry.x, drawin->geometry.y));
410         }
411     }
412 }
413 
414 static drawin_t *
drawin_allocator(lua_State * L)415 drawin_allocator(lua_State *L)
416 {
417     xcb_screen_t *s = globalconf.screen;
418     drawin_t *w = drawin_new(L);
419 
420     w->visible = false;
421 
422     w->opacity = -1;
423     w->cursor = a_strdup("left_ptr");
424     w->geometry.width = 1;
425     w->geometry.height = 1;
426     w->geometry_dirty = false;
427     w->type = _NET_WM_WINDOW_TYPE_NORMAL;
428 
429     drawable_allocator(L, (drawable_refresh_callback *) drawin_refresh_pixmap, w);
430     w->drawable = luaA_object_ref_item(L, -2, -1);
431 
432     w->window = xcb_generate_id(globalconf.connection);
433     xcb_create_window(globalconf.connection, globalconf.default_depth, w->window, s->root,
434                       w->geometry.x, w->geometry.y,
435                       w->geometry.width, w->geometry.height,
436                       w->border_width, XCB_COPY_FROM_PARENT, globalconf.visual->visual_id,
437                       XCB_CW_BORDER_PIXEL | XCB_CW_BIT_GRAVITY
438                       | XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK | XCB_CW_COLORMAP
439                       | XCB_CW_CURSOR,
440                       (const uint32_t [])
441                       {
442                           w->border_color.pixel,
443                           XCB_GRAVITY_NORTH_WEST,
444                           1,
445                           XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT
446                           | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_ENTER_WINDOW
447                           | XCB_EVENT_MASK_LEAVE_WINDOW | XCB_EVENT_MASK_STRUCTURE_NOTIFY
448                           | XCB_EVENT_MASK_POINTER_MOTION | XCB_EVENT_MASK_BUTTON_PRESS
449                           | XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_EXPOSURE
450                           | XCB_EVENT_MASK_PROPERTY_CHANGE,
451                           globalconf.default_cmap,
452                           xcursor_new(globalconf.cursor_ctx, xcursor_font_fromstr(w->cursor))
453                       });
454     xwindow_set_class_instance(w->window);
455     xwindow_set_name_static(w->window, "Awesome drawin");
456 
457     /* Set the right properties */
458     ewmh_update_window_type(w->window, window_translate_type(w->type));
459     ewmh_update_strut(w->window, &w->strut);
460 
461     return w;
462 }
463 
464 /** Create a new drawin.
465  * \param L The Lua VM state.
466  * \return The number of elements pushed on stack.
467  */
468 static int
luaA_drawin_new(lua_State * L)469 luaA_drawin_new(lua_State *L)
470 {
471     luaA_class_new(L, &drawin_class);
472 
473     return 1;
474 }
475 
476 /** Get or set drawin geometry. That's the same as accessing or setting the x,
477  * y, width or height properties of a drawin.
478  *
479  * @param A table with coordinates to modify.
480  * @return A table with drawin coordinates and geometry.
481  * @function geometry
482  */
483 static int
luaA_drawin_geometry(lua_State * L)484 luaA_drawin_geometry(lua_State *L)
485 {
486     drawin_t *drawin = luaA_checkudata(L, 1, &drawin_class);
487 
488     if(lua_gettop(L) == 2)
489     {
490         area_t wingeom;
491 
492         luaA_checktable(L, 2);
493         wingeom.x = round(luaA_getopt_number_range(L, 2, "x", drawin->geometry.x, MIN_X11_COORDINATE, MAX_X11_COORDINATE));
494         wingeom.y = round(luaA_getopt_number_range(L, 2, "y", drawin->geometry.y, MIN_X11_COORDINATE, MAX_X11_COORDINATE));
495         wingeom.width = ceil(luaA_getopt_number_range(L, 2, "width", drawin->geometry.width, MIN_X11_SIZE, MAX_X11_SIZE));
496         wingeom.height = ceil(luaA_getopt_number_range(L, 2, "height", drawin->geometry.height, MIN_X11_SIZE, MAX_X11_SIZE));
497 
498         if(wingeom.width > 0 && wingeom.height > 0)
499             drawin_moveresize(L, 1, wingeom);
500     }
501 
502     return luaA_pusharea(L, drawin->geometry);
503 }
504 
505 
LUA_OBJECT_EXPORT_PROPERTY(drawin,drawin_t,ontop,lua_pushboolean)506 LUA_OBJECT_EXPORT_PROPERTY(drawin, drawin_t, ontop, lua_pushboolean)
507 LUA_OBJECT_EXPORT_PROPERTY(drawin, drawin_t, cursor, lua_pushstring)
508 LUA_OBJECT_EXPORT_PROPERTY(drawin, drawin_t, visible, lua_pushboolean)
509 
510 static int
511 luaA_drawin_set_x(lua_State *L, drawin_t *drawin)
512 {
513     int x = round(luaA_checknumber_range(L, -1, MIN_X11_COORDINATE, MAX_X11_COORDINATE));
514     drawin_moveresize(L, -3, (area_t) { .x = x,
515                                         .y = drawin->geometry.y,
516                                         .width = drawin->geometry.width,
517                                         .height = drawin->geometry.height });
518     return 0;
519 }
520 
521 static int
luaA_drawin_get_x(lua_State * L,drawin_t * drawin)522 luaA_drawin_get_x(lua_State *L, drawin_t *drawin)
523 {
524     lua_pushinteger(L, drawin->geometry.x);
525     return 1;
526 }
527 
528 static int
luaA_drawin_set_y(lua_State * L,drawin_t * drawin)529 luaA_drawin_set_y(lua_State *L, drawin_t *drawin)
530 {
531     int y = round(luaA_checknumber_range(L, -1, MIN_X11_COORDINATE, MAX_X11_COORDINATE));
532     drawin_moveresize(L, -3, (area_t) { .x = drawin->geometry.x,
533                                         .y = y,
534                                         .width = drawin->geometry.width,
535                                         .height = drawin->geometry.height });
536     return 0;
537 }
538 
539 static int
luaA_drawin_get_y(lua_State * L,drawin_t * drawin)540 luaA_drawin_get_y(lua_State *L, drawin_t *drawin)
541 {
542     lua_pushinteger(L, drawin->geometry.y);
543     return 1;
544 }
545 
546 static int
luaA_drawin_set_width(lua_State * L,drawin_t * drawin)547 luaA_drawin_set_width(lua_State *L, drawin_t *drawin)
548 {
549     int width = ceil(luaA_checknumber_range(L, -1, MIN_X11_SIZE, MAX_X11_SIZE));
550     drawin_moveresize(L, -3, (area_t) { .x = drawin->geometry.x,
551                                         .y = drawin->geometry.y,
552                                         .width = width,
553                                         .height = drawin->geometry.height });
554     return 0;
555 }
556 
557 static int
luaA_drawin_get_width(lua_State * L,drawin_t * drawin)558 luaA_drawin_get_width(lua_State *L, drawin_t *drawin)
559 {
560     lua_pushinteger(L, drawin->geometry.width);
561     return 1;
562 }
563 
564 static int
luaA_drawin_set_height(lua_State * L,drawin_t * drawin)565 luaA_drawin_set_height(lua_State *L, drawin_t *drawin)
566 {
567     int height = ceil(luaA_checknumber_range(L, -1, MIN_X11_SIZE, MAX_X11_SIZE));
568     drawin_moveresize(L, -3, (area_t) { .x = drawin->geometry.x,
569                                        .y = drawin->geometry.y,
570                                        .width = drawin->geometry.width,
571                                        .height = height });
572     return 0;
573 }
574 
575 static int
luaA_drawin_get_height(lua_State * L,drawin_t * drawin)576 luaA_drawin_get_height(lua_State *L, drawin_t *drawin)
577 {
578     lua_pushinteger(L, drawin->geometry.height);
579     return 1;
580 }
581 
582 /** Set the drawin on top status.
583  * \param L The Lua VM state.
584  * \param drawin The drawin object.
585  * \return The number of elements pushed on stack.
586  */
587 static int
luaA_drawin_set_ontop(lua_State * L,drawin_t * drawin)588 luaA_drawin_set_ontop(lua_State *L, drawin_t *drawin)
589 {
590     bool b = luaA_checkboolean(L, -1);
591     if(b != drawin->ontop)
592     {
593         drawin->ontop = b;
594         stack_windows();
595         luaA_object_emit_signal(L, -3, "property::ontop", 0);
596     }
597     return 0;
598 }
599 
600 /** Set the drawin cursor.
601  * \param L The Lua VM state.
602  * \param drawin The drawin object.
603  * \return The number of elements pushed on stack.
604  */
605 static int
luaA_drawin_set_cursor(lua_State * L,drawin_t * drawin)606 luaA_drawin_set_cursor(lua_State *L, drawin_t *drawin)
607 {
608     const char *buf = luaL_checkstring(L, -1);
609     if(buf)
610     {
611         uint16_t cursor_font = xcursor_font_fromstr(buf);
612         if(cursor_font)
613         {
614             xcb_cursor_t cursor = xcursor_new(globalconf.cursor_ctx, cursor_font);
615             p_delete(&drawin->cursor);
616             drawin->cursor = a_strdup(buf);
617             xwindow_set_cursor(drawin->window, cursor);
618             luaA_object_emit_signal(L, -3, "property::cursor", 0);
619         }
620     }
621     return 0;
622 }
623 
624 /** Set the drawin visibility.
625  * \param L The Lua VM state.
626  * \param drawin The drawin object.
627  * \return The number of elements pushed on stack.
628  */
629 static int
luaA_drawin_set_visible(lua_State * L,drawin_t * drawin)630 luaA_drawin_set_visible(lua_State *L, drawin_t *drawin)
631 {
632     drawin_set_visible(L, -3, luaA_checkboolean(L, -1));
633     return 0;
634 }
635 
636 /** Get a drawin's drawable
637  * \param L The Lua VM state.
638  * \param drawin The drawin object.
639  * \return The number of elements pushed on stack.
640  */
641 static int
luaA_drawin_get_drawable(lua_State * L,drawin_t * drawin)642 luaA_drawin_get_drawable(lua_State *L, drawin_t *drawin)
643 {
644     luaA_object_push_item(L, -2, drawin->drawable);
645     return 1;
646 }
647 
648 /** Get the drawin's bounding shape.
649  * \param L The Lua VM state.
650  * \param drawin The drawin object.
651  * \return The number of elements pushed on stack.
652  */
653 static int
luaA_drawin_get_shape_bounding(lua_State * L,drawin_t * drawin)654 luaA_drawin_get_shape_bounding(lua_State *L, drawin_t *drawin)
655 {
656     cairo_surface_t *surf = xwindow_get_shape(drawin->window, XCB_SHAPE_SK_BOUNDING);
657     if (!surf)
658         return 0;
659     /* lua has to make sure to free the ref or we have a leak */
660     lua_pushlightuserdata(L, surf);
661     return 1;
662 }
663 
664 /** Set the drawin's bounding shape.
665  * \param L The Lua VM state.
666  * \param drawin The drawin object.
667  * \return The number of elements pushed on stack.
668  */
669 static int
luaA_drawin_set_shape_bounding(lua_State * L,drawin_t * drawin)670 luaA_drawin_set_shape_bounding(lua_State *L, drawin_t *drawin)
671 {
672     cairo_surface_t *surf = NULL;
673     if(!lua_isnil(L, -1))
674         surf = (cairo_surface_t *)lua_touserdata(L, -1);
675 
676     /* The drawin might have been resized to a larger size. Apply that. */
677     drawin_apply_moveresize(drawin);
678 
679     xwindow_set_shape(drawin->window,
680             drawin->geometry.width + 2*drawin->border_width,
681             drawin->geometry.height + 2*drawin->border_width,
682             XCB_SHAPE_SK_BOUNDING, surf, -drawin->border_width);
683     luaA_object_emit_signal(L, -3, "property::shape_bounding", 0);
684     return 0;
685 }
686 
687 /** Get the drawin's clip shape.
688  * \param L The Lua VM state.
689  * \param drawin The drawin object.
690  * \return The number of elements pushed on stack.
691  */
692 static int
luaA_drawin_get_shape_clip(lua_State * L,drawin_t * drawin)693 luaA_drawin_get_shape_clip(lua_State *L, drawin_t *drawin)
694 {
695     cairo_surface_t *surf = xwindow_get_shape(drawin->window, XCB_SHAPE_SK_CLIP);
696     if (!surf)
697         return 0;
698     /* lua has to make sure to free the ref or we have a leak */
699     lua_pushlightuserdata(L, surf);
700     return 1;
701 }
702 
703 /** Set the drawin's clip shape.
704  * \param L The Lua VM state.
705  * \param drawin The drawin object.
706  * \return The number of elements pushed on stack.
707  */
708 static int
luaA_drawin_set_shape_clip(lua_State * L,drawin_t * drawin)709 luaA_drawin_set_shape_clip(lua_State *L, drawin_t *drawin)
710 {
711     cairo_surface_t *surf = NULL;
712     if(!lua_isnil(L, -1))
713         surf = (cairo_surface_t *)lua_touserdata(L, -1);
714 
715     /* The drawin might have been resized to a larger size. Apply that. */
716     drawin_apply_moveresize(drawin);
717 
718     xwindow_set_shape(drawin->window, drawin->geometry.width, drawin->geometry.height,
719             XCB_SHAPE_SK_CLIP, surf, 0);
720     luaA_object_emit_signal(L, -3, "property::shape_clip", 0);
721     return 0;
722 }
723 
724 /** Get the drawin's input shape.
725  * \param L The Lua VM state.
726  * \param drawin The drawin object.
727  * \return The number of elements pushed on stack.
728  */
729 static int
luaA_drawin_get_shape_input(lua_State * L,drawin_t * drawin)730 luaA_drawin_get_shape_input(lua_State *L, drawin_t *drawin)
731 {
732     cairo_surface_t *surf = xwindow_get_shape(drawin->window, XCB_SHAPE_SK_INPUT);
733     if (!surf)
734         return 0;
735     /* lua has to make sure to free the ref or we have a leak */
736     lua_pushlightuserdata(L, surf);
737     return 1;
738 }
739 
740 /** Set the drawin's input shape.
741  * \param L The Lua VM state.
742  * \param drawin The drawin object.
743  * \return The number of elements pushed on stack.
744  */
745 static int
luaA_drawin_set_shape_input(lua_State * L,drawin_t * drawin)746 luaA_drawin_set_shape_input(lua_State *L, drawin_t *drawin)
747 {
748     cairo_surface_t *surf = NULL;
749     if(!lua_isnil(L, -1))
750         surf = (cairo_surface_t *)lua_touserdata(L, -1);
751 
752     /* The drawin might have been resized to a larger size. Apply that. */
753     drawin_apply_moveresize(drawin);
754 
755     xwindow_set_shape(drawin->window,
756             drawin->geometry.width + 2*drawin->border_width,
757             drawin->geometry.height + 2*drawin->border_width,
758             XCB_SHAPE_SK_INPUT, surf, -drawin->border_width);
759     luaA_object_emit_signal(L, -3, "property::shape_input", 0);
760     return 0;
761 }
762 
763 void
drawin_class_setup(lua_State * L)764 drawin_class_setup(lua_State *L)
765 {
766     static const struct luaL_Reg drawin_methods[] =
767     {
768         LUA_CLASS_METHODS(drawin)
769         { "get", luaA_drawin_get },
770         { "__call", luaA_drawin_new },
771         { NULL, NULL }
772     };
773 
774     static const struct luaL_Reg drawin_meta[] =
775     {
776         LUA_OBJECT_META(drawin)
777         LUA_CLASS_META
778         { "geometry", luaA_drawin_geometry },
779         { NULL, NULL },
780     };
781 
782     luaA_class_setup(L, &drawin_class, "drawin", &window_class,
783                      (lua_class_allocator_t) drawin_allocator,
784                      (lua_class_collector_t) drawin_wipe,
785                      NULL,
786                      luaA_class_index_miss_property, luaA_class_newindex_miss_property,
787                      drawin_methods, drawin_meta);
788     luaA_class_add_property(&drawin_class, "drawable",
789                             NULL,
790                             (lua_class_propfunc_t) luaA_drawin_get_drawable,
791                             NULL);
792     luaA_class_add_property(&drawin_class, "visible",
793                             (lua_class_propfunc_t) luaA_drawin_set_visible,
794                             (lua_class_propfunc_t) luaA_drawin_get_visible,
795                             (lua_class_propfunc_t) luaA_drawin_set_visible);
796     luaA_class_add_property(&drawin_class, "ontop",
797                             (lua_class_propfunc_t) luaA_drawin_set_ontop,
798                             (lua_class_propfunc_t) luaA_drawin_get_ontop,
799                             (lua_class_propfunc_t) luaA_drawin_set_ontop);
800     luaA_class_add_property(&drawin_class, "cursor",
801                             (lua_class_propfunc_t) luaA_drawin_set_cursor,
802                             (lua_class_propfunc_t) luaA_drawin_get_cursor,
803                             (lua_class_propfunc_t) luaA_drawin_set_cursor);
804     luaA_class_add_property(&drawin_class, "x",
805                             (lua_class_propfunc_t) luaA_drawin_set_x,
806                             (lua_class_propfunc_t) luaA_drawin_get_x,
807                             (lua_class_propfunc_t) luaA_drawin_set_x);
808     luaA_class_add_property(&drawin_class, "y",
809                             (lua_class_propfunc_t) luaA_drawin_set_y,
810                             (lua_class_propfunc_t) luaA_drawin_get_y,
811                             (lua_class_propfunc_t) luaA_drawin_set_y);
812     luaA_class_add_property(&drawin_class, "width",
813                             (lua_class_propfunc_t) luaA_drawin_set_width,
814                             (lua_class_propfunc_t) luaA_drawin_get_width,
815                             (lua_class_propfunc_t) luaA_drawin_set_width);
816     luaA_class_add_property(&drawin_class, "height",
817                             (lua_class_propfunc_t) luaA_drawin_set_height,
818                             (lua_class_propfunc_t) luaA_drawin_get_height,
819                             (lua_class_propfunc_t) luaA_drawin_set_height);
820     luaA_class_add_property(&drawin_class, "type",
821                             (lua_class_propfunc_t) luaA_window_set_type,
822                             (lua_class_propfunc_t) luaA_window_get_type,
823                             (lua_class_propfunc_t) luaA_window_set_type);
824     luaA_class_add_property(&drawin_class, "shape_bounding",
825                             (lua_class_propfunc_t) luaA_drawin_set_shape_bounding,
826                             (lua_class_propfunc_t) luaA_drawin_get_shape_bounding,
827                             (lua_class_propfunc_t) luaA_drawin_set_shape_bounding);
828     luaA_class_add_property(&drawin_class, "shape_clip",
829                             (lua_class_propfunc_t) luaA_drawin_set_shape_clip,
830                             (lua_class_propfunc_t) luaA_drawin_get_shape_clip,
831                             (lua_class_propfunc_t) luaA_drawin_set_shape_clip);
832     luaA_class_add_property(&drawin_class, "shape_input",
833                             (lua_class_propfunc_t) luaA_drawin_set_shape_input,
834                             (lua_class_propfunc_t) luaA_drawin_get_shape_input,
835                             (lua_class_propfunc_t) luaA_drawin_set_shape_input);
836 }
837 
838 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
839