1 /*
2 * window.c - window object
3 *
4 * Copyright © 2009 Julien Danjou <julien@danjou.info>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 *
20 */
21
22 /** Handling of X properties.
23 *
24 * This can not be used as a standalone class, but is instead referenced
25 * explicitely in the classes, where it can be used. In the respective
26 * classes,it then can be used via `classname:get_xproperty(...)` etc.
27 * @classmod xproperties
28 */
29
30 /**
31 * @signal property::border_color
32 */
33
34 /**
35 * @signal property::border_width
36 */
37
38 /**
39 * @signal property::buttons
40 */
41
42 /**
43 * @signal property::opacity
44 */
45
46 /**
47 * @signal property::struts
48 */
49
50 /**
51 * @signal property::type
52 */
53
54 #include "objects/window.h"
55 #include "common/atoms.h"
56 #include "common/xutil.h"
57 #include "ewmh.h"
58 #include "objects/screen.h"
59 #include "property.h"
60 #include "xwindow.h"
61
62 lua_class_t window_class;
LUA_CLASS_FUNCS(window,window_class)63 LUA_CLASS_FUNCS(window, window_class)
64
65 static xcb_window_t
66 window_get(window_t *window)
67 {
68 if (window->frame_window != XCB_NONE)
69 return window->frame_window;
70 return window->window;
71 }
72
73 static void
window_wipe(window_t * window)74 window_wipe(window_t *window)
75 {
76 button_array_wipe(&window->buttons);
77 }
78
79 /** Get or set mouse buttons bindings on a window.
80 * \param L The Lua VM state.
81 * \return The number of elements pushed on the stack.
82 */
83 static int
luaA_window_buttons(lua_State * L)84 luaA_window_buttons(lua_State *L)
85 {
86 window_t *window = luaA_checkudata(L, 1, &window_class);
87
88 if(lua_gettop(L) == 2)
89 {
90 luaA_button_array_set(L, 1, 2, &window->buttons);
91 luaA_object_emit_signal(L, 1, "property::buttons", 0);
92 xwindow_buttons_grab(window->window, &window->buttons);
93 }
94
95 return luaA_button_array_get(L, 1, &window->buttons);
96 }
97
98 /** Return window struts (reserved space at the edge of the screen).
99 * \param L The Lua VM state.
100 * \return The number of elements pushed on stack.
101 */
102 static int
luaA_window_struts(lua_State * L)103 luaA_window_struts(lua_State *L)
104 {
105 window_t *window = luaA_checkudata(L, 1, &window_class);
106
107 if(lua_gettop(L) == 2)
108 {
109 luaA_tostrut(L, 2, &window->strut);
110 ewmh_update_strut(window->window, &window->strut);
111 luaA_object_emit_signal(L, 1, "property::struts", 0);
112 /* We don't know the correct screen, update them all */
113 foreach(s, globalconf.screens)
114 screen_update_workarea(*s);
115 }
116
117 return luaA_pushstrut(L, window->strut);
118 }
119
120 /** Set a window opacity.
121 * \param L The Lua VM state.
122 * \param idx The index of the window on the stack.
123 * \param opacity The opacity value.
124 */
125 void
window_set_opacity(lua_State * L,int idx,double opacity)126 window_set_opacity(lua_State *L, int idx, double opacity)
127 {
128 window_t *window = luaA_checkudata(L, idx, &window_class);
129
130 if(window->opacity != opacity)
131 {
132 window->opacity = opacity;
133 xwindow_set_opacity(window_get(window), opacity);
134 luaA_object_emit_signal(L, idx, "property::opacity", 0);
135 }
136 }
137
138 /** Set a window opacity.
139 * \param L The Lua VM state.
140 * \param window The window object.
141 * \return The number of elements pushed on stack.
142 */
143 static int
luaA_window_set_opacity(lua_State * L,window_t * window)144 luaA_window_set_opacity(lua_State *L, window_t *window)
145 {
146 if(lua_isnil(L, -1))
147 window_set_opacity(L, -3, -1);
148 else
149 {
150 double d = luaL_checknumber(L, -1);
151 if(d >= 0 && d <= 1)
152 window_set_opacity(L, -3, d);
153 }
154 return 0;
155 }
156
157 /** Get the window opacity.
158 * \param L The Lua VM state.
159 * \param window The window object.
160 * \return The number of elements pushed on stack.
161 */
162 static int
luaA_window_get_opacity(lua_State * L,window_t * window)163 luaA_window_get_opacity(lua_State *L, window_t *window)
164 {
165 if(window->opacity >= 0)
166 lua_pushnumber(L, window->opacity);
167 else
168 /* Let's always return some "good" value */
169 lua_pushnumber(L, 1);
170 return 1;
171 }
172
173 void
window_border_refresh(window_t * window)174 window_border_refresh(window_t *window)
175 {
176 if(!window->border_need_update)
177 return;
178 window->border_need_update = false;
179 xwindow_set_border_color(window_get(window), &window->border_color);
180 if(window->window)
181 xcb_configure_window(globalconf.connection, window_get(window),
182 XCB_CONFIG_WINDOW_BORDER_WIDTH,
183 (uint32_t[]) { window->border_width });
184 }
185
186 /** Set the window border color.
187 * \param L The Lua VM state.
188 * \param window The window object.
189 * \return The number of elements pushed on stack.
190 */
191 static int
luaA_window_set_border_color(lua_State * L,window_t * window)192 luaA_window_set_border_color(lua_State *L, window_t *window)
193 {
194 size_t len;
195 const char *color_name = luaL_checklstring(L, -1, &len);
196
197 if(color_name &&
198 color_init_reply(color_init_unchecked(&window->border_color, color_name, len, globalconf.visual)))
199 {
200 window->border_need_update = true;
201 luaA_object_emit_signal(L, -3, "property::border_color", 0);
202 }
203
204 return 0;
205 }
206
207 /** Set a window border width.
208 * \param L The Lua VM state.
209 * \param idx The window index.
210 * \param width The border width.
211 */
212 void
window_set_border_width(lua_State * L,int idx,int width)213 window_set_border_width(lua_State *L, int idx, int width)
214 {
215 window_t *window = luaA_checkudata(L, idx, &window_class);
216 uint16_t old_width = window->border_width;
217
218 if(width == window->border_width || width < 0)
219 return;
220
221 window->border_need_update = true;
222 window->border_width = width;
223
224 if(window->border_width_callback)
225 (*window->border_width_callback)(window, old_width, width);
226
227 luaA_object_emit_signal(L, idx, "property::border_width", 0);
228 }
229
230 /** Get the window type.
231 * \param L The Lua VM state.
232 * \param window The window object.
233 * \return The number of elements pushed on stack.
234 */
235 int
luaA_window_get_type(lua_State * L,window_t * w)236 luaA_window_get_type(lua_State *L, window_t *w)
237 {
238 switch(w->type)
239 {
240 case WINDOW_TYPE_DESKTOP:
241 lua_pushliteral(L, "desktop");
242 break;
243 case WINDOW_TYPE_DOCK:
244 lua_pushliteral(L, "dock");
245 break;
246 case WINDOW_TYPE_SPLASH:
247 lua_pushliteral(L, "splash");
248 break;
249 case WINDOW_TYPE_DIALOG:
250 lua_pushliteral(L, "dialog");
251 break;
252 case WINDOW_TYPE_MENU:
253 lua_pushliteral(L, "menu");
254 break;
255 case WINDOW_TYPE_TOOLBAR:
256 lua_pushliteral(L, "toolbar");
257 break;
258 case WINDOW_TYPE_UTILITY:
259 lua_pushliteral(L, "utility");
260 break;
261 case WINDOW_TYPE_DROPDOWN_MENU:
262 lua_pushliteral(L, "dropdown_menu");
263 break;
264 case WINDOW_TYPE_POPUP_MENU:
265 lua_pushliteral(L, "popup_menu");
266 break;
267 case WINDOW_TYPE_TOOLTIP:
268 lua_pushliteral(L, "tooltip");
269 break;
270 case WINDOW_TYPE_NOTIFICATION:
271 lua_pushliteral(L, "notification");
272 break;
273 case WINDOW_TYPE_COMBO:
274 lua_pushliteral(L, "combo");
275 break;
276 case WINDOW_TYPE_DND:
277 lua_pushliteral(L, "dnd");
278 break;
279 case WINDOW_TYPE_NORMAL:
280 lua_pushliteral(L, "normal");
281 break;
282 default:
283 return 0;
284 }
285 return 1;
286 }
287
288 /** Set the window type.
289 * \param L The Lua VM state.
290 * \param window The window object.
291 * \return The number of elements pushed on stack.
292 */
293 int
luaA_window_set_type(lua_State * L,window_t * w)294 luaA_window_set_type(lua_State *L, window_t *w)
295 {
296 window_type_t type;
297 const char *buf = luaL_checkstring(L, -1);
298
299 if (A_STREQ(buf, "desktop"))
300 type = WINDOW_TYPE_DESKTOP;
301 else if(A_STREQ(buf, "dock"))
302 type = WINDOW_TYPE_DOCK;
303 else if(A_STREQ(buf, "splash"))
304 type = WINDOW_TYPE_SPLASH;
305 else if(A_STREQ(buf, "dialog"))
306 type = WINDOW_TYPE_DIALOG;
307 else if(A_STREQ(buf, "menu"))
308 type = WINDOW_TYPE_MENU;
309 else if(A_STREQ(buf, "toolbar"))
310 type = WINDOW_TYPE_TOOLBAR;
311 else if(A_STREQ(buf, "utility"))
312 type = WINDOW_TYPE_UTILITY;
313 else if(A_STREQ(buf, "dropdown_menu"))
314 type = WINDOW_TYPE_DROPDOWN_MENU;
315 else if(A_STREQ(buf, "popup_menu"))
316 type = WINDOW_TYPE_POPUP_MENU;
317 else if(A_STREQ(buf, "tooltip"))
318 type = WINDOW_TYPE_TOOLTIP;
319 else if(A_STREQ(buf, "notification"))
320 type = WINDOW_TYPE_NOTIFICATION;
321 else if(A_STREQ(buf, "combo"))
322 type = WINDOW_TYPE_COMBO;
323 else if(A_STREQ(buf, "dnd"))
324 type = WINDOW_TYPE_DND;
325 else if(A_STREQ(buf, "normal"))
326 type = WINDOW_TYPE_NORMAL;
327 else
328 {
329 luaA_warn(L, "Unknown window type '%s'", buf);
330 return 0;
331 }
332
333 if(w->type != type)
334 {
335 w->type = type;
336 if(w->window != XCB_WINDOW_NONE)
337 ewmh_update_window_type(w->window, window_translate_type(w->type));
338 luaA_object_emit_signal(L, -3, "property::type", 0);
339 }
340
341 return 0;
342 }
343
344 static xproperty_t *
luaA_find_xproperty(lua_State * L,int idx)345 luaA_find_xproperty(lua_State *L, int idx)
346 {
347 const char *name = luaL_checkstring(L, idx);
348 foreach(prop, globalconf.xproperties)
349 if (A_STREQ(prop->name, name))
350 return prop;
351 luaL_argerror(L, idx, "Unknown xproperty");
352 return NULL;
353 }
354
355 int
window_set_xproperty(lua_State * L,xcb_window_t window,int prop_idx,int value_idx)356 window_set_xproperty(lua_State *L, xcb_window_t window, int prop_idx, int value_idx)
357 {
358 xproperty_t *prop = luaA_find_xproperty(L, prop_idx);
359 xcb_atom_t type;
360 size_t len;
361 uint32_t number;
362 const void *data;
363
364 if(lua_isnil(L, value_idx))
365 {
366 xcb_delete_property(globalconf.connection, window, prop->atom);
367 } else {
368 uint8_t format;
369 if(prop->type == PROP_STRING)
370 {
371 data = luaL_checklstring(L, value_idx, &len);
372 type = UTF8_STRING;
373 format = 8;
374 } else if(prop->type == PROP_NUMBER || prop->type == PROP_BOOLEAN)
375 {
376 if (prop->type == PROP_NUMBER)
377 number = luaA_checkinteger_range(L, value_idx, 0, UINT32_MAX);
378 else
379 number = luaA_checkboolean(L, value_idx);
380 data = &number;
381 len = 1;
382 type = XCB_ATOM_CARDINAL;
383 format = 32;
384 } else
385 fatal("Got an xproperty with invalid type");
386
387 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE, window,
388 prop->atom, type, format, len, data);
389 }
390 return 0;
391 }
392
393 int
window_get_xproperty(lua_State * L,xcb_window_t window,int prop_idx)394 window_get_xproperty(lua_State *L, xcb_window_t window, int prop_idx)
395 {
396 xproperty_t *prop = luaA_find_xproperty(L, prop_idx);
397 xcb_atom_t type;
398 void *data;
399 xcb_get_property_reply_t *reply;
400 uint32_t length;
401
402 type = prop->type == PROP_STRING ? UTF8_STRING : XCB_ATOM_CARDINAL;
403 length = prop->type == PROP_STRING ? UINT32_MAX : 1;
404 reply = xcb_get_property_reply(globalconf.connection,
405 xcb_get_property_unchecked(globalconf.connection, false, window,
406 prop->atom, type, 0, length), NULL);
407 if(!reply)
408 return 0;
409
410 data = xcb_get_property_value(reply);
411
412 if(prop->type == PROP_STRING)
413 lua_pushlstring(L, data, reply->value_len);
414 else
415 {
416 if(reply->value_len <= 0)
417 {
418 p_delete(&reply);
419 return 0;
420 }
421 if(prop->type == PROP_NUMBER)
422 lua_pushinteger(L, *(uint32_t *) data);
423 else
424 lua_pushboolean(L, *(uint32_t *) data);
425 }
426
427 p_delete(&reply);
428 return 1;
429 }
430
431 /** Change a xproperty.
432 *
433 * @param name The name of the X11 property
434 * @param value The new value for the property
435 * @function set_xproperty
436 */
437 static int
luaA_window_set_xproperty(lua_State * L)438 luaA_window_set_xproperty(lua_State *L)
439 {
440 window_t *w = luaA_checkudata(L, 1, &window_class);
441 return window_set_xproperty(L, w->window, 2, 3);
442 }
443
444 /** Get the value of a xproperty.
445 *
446 * @param name The name of the X11 property
447 * @function get_xproperty
448 */
449 static int
luaA_window_get_xproperty(lua_State * L)450 luaA_window_get_xproperty(lua_State *L)
451 {
452 window_t *w = luaA_checkudata(L, 1, &window_class);
453 return window_get_xproperty(L, w->window, 2);
454 }
455
456 /* Translate a window_type_t into the corresponding EWMH atom.
457 * @param type The type to return.
458 * @return The EWMH atom for this type.
459 */
460 uint32_t
window_translate_type(window_type_t type)461 window_translate_type(window_type_t type)
462 {
463 switch(type)
464 {
465 case WINDOW_TYPE_NORMAL:
466 return _NET_WM_WINDOW_TYPE_NORMAL;
467 case WINDOW_TYPE_DESKTOP:
468 return _NET_WM_WINDOW_TYPE_DESKTOP;
469 case WINDOW_TYPE_DOCK:
470 return _NET_WM_WINDOW_TYPE_DOCK;
471 case WINDOW_TYPE_SPLASH:
472 return _NET_WM_WINDOW_TYPE_SPLASH;
473 case WINDOW_TYPE_DIALOG:
474 return _NET_WM_WINDOW_TYPE_DIALOG;
475 case WINDOW_TYPE_MENU:
476 return _NET_WM_WINDOW_TYPE_MENU;
477 case WINDOW_TYPE_TOOLBAR:
478 return _NET_WM_WINDOW_TYPE_TOOLBAR;
479 case WINDOW_TYPE_UTILITY:
480 return _NET_WM_WINDOW_TYPE_UTILITY;
481 case WINDOW_TYPE_DROPDOWN_MENU:
482 return _NET_WM_WINDOW_TYPE_DROPDOWN_MENU;
483 case WINDOW_TYPE_POPUP_MENU:
484 return _NET_WM_WINDOW_TYPE_POPUP_MENU;
485 case WINDOW_TYPE_TOOLTIP:
486 return _NET_WM_WINDOW_TYPE_TOOLTIP;
487 case WINDOW_TYPE_NOTIFICATION:
488 return _NET_WM_WINDOW_TYPE_NOTIFICATION;
489 case WINDOW_TYPE_COMBO:
490 return _NET_WM_WINDOW_TYPE_COMBO;
491 case WINDOW_TYPE_DND:
492 return _NET_WM_WINDOW_TYPE_DND;
493 }
494 return _NET_WM_WINDOW_TYPE_NORMAL;
495 }
496
497 static int
luaA_window_set_border_width(lua_State * L,window_t * c)498 luaA_window_set_border_width(lua_State *L, window_t *c)
499 {
500 window_set_border_width(L, -3, round(luaA_checknumber_range(L, -1, 0, MAX_X11_SIZE)));
501 return 0;
502 }
503
LUA_OBJECT_EXPORT_PROPERTY(window,window_t,window,lua_pushinteger)504 LUA_OBJECT_EXPORT_PROPERTY(window, window_t, window, lua_pushinteger)
505 LUA_OBJECT_EXPORT_PROPERTY(window, window_t, border_color, luaA_pushcolor)
506 LUA_OBJECT_EXPORT_PROPERTY(window, window_t, border_width, lua_pushinteger)
507
508 void
509 window_class_setup(lua_State *L)
510 {
511 static const struct luaL_Reg window_methods[] =
512 {
513 { NULL, NULL }
514 };
515
516 static const struct luaL_Reg window_meta[] =
517 {
518 { "struts", luaA_window_struts },
519 { "buttons", luaA_window_buttons },
520 { "set_xproperty", luaA_window_set_xproperty },
521 { "get_xproperty", luaA_window_get_xproperty },
522 { NULL, NULL }
523 };
524
525 luaA_class_setup(L, &window_class, "window", NULL,
526 NULL, (lua_class_collector_t) window_wipe, NULL,
527 luaA_class_index_miss_property, luaA_class_newindex_miss_property,
528 window_methods, window_meta);
529
530 luaA_class_add_property(&window_class, "window",
531 NULL,
532 (lua_class_propfunc_t) luaA_window_get_window,
533 NULL);
534 luaA_class_add_property(&window_class, "opacity",
535 (lua_class_propfunc_t) luaA_window_set_opacity,
536 (lua_class_propfunc_t) luaA_window_get_opacity,
537 (lua_class_propfunc_t) luaA_window_set_opacity);
538 luaA_class_add_property(&window_class, "border_color",
539 (lua_class_propfunc_t) luaA_window_set_border_color,
540 (lua_class_propfunc_t) luaA_window_get_border_color,
541 (lua_class_propfunc_t) luaA_window_set_border_color);
542 luaA_class_add_property(&window_class, "border_width",
543 (lua_class_propfunc_t) luaA_window_set_border_width,
544 (lua_class_propfunc_t) luaA_window_get_border_width,
545 (lua_class_propfunc_t) luaA_window_set_border_width);
546 }
547
548 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
549