Browse Source

Add awesome.register_xproperty (FS#1212)

This commits adds awesome.register_xproperty(). This allows lua code to register
arbitrary X11 properties with awesome which will then watch these properties.
Whenever such a property is changed on a client or drawin, we will emit the
xproperty::name signal.

This also adds window:get_xproperty(name) and window:set_xproperty(name, value)
which allows to mess with properties.

Signed-off-by: Uli Schlachter <psychon@znc.in>
Uli Schlachter 5 years ago
parent
commit
62e2dee4ba
9 changed files with 251 additions and 16 deletions
  1. 4
    0
      globalconf.h
  2. 4
    13
      lib/wibox/init.lua.in
  3. 2
    0
      luaa.c
  4. 6
    0
      luadoc/awesome.lua
  5. 11
    0
      luadoc/client.lua
  6. 11
    0
      luadoc/drawin.lua
  7. 98
    0
      objects/window.c
  8. 92
    3
      property.c
  9. 23
    0
      property.h

+ 4
- 0
globalconf.h View File

@@ -55,12 +55,14 @@ typedef struct button_t button_t;
55 55
 typedef struct widget_t widget_t;
56 56
 typedef struct client_t client_t;
57 57
 typedef struct tag tag_t;
58
+typedef struct xproperty xproperty_t;
58 59
 
59 60
 ARRAY_TYPE(button_t *, button)
60 61
 ARRAY_TYPE(tag_t *, tag)
61 62
 ARRAY_TYPE(screen_t, screen)
62 63
 ARRAY_TYPE(client_t *, client)
63 64
 ARRAY_TYPE(drawin_t *, drawin)
65
+ARRAY_TYPE(xproperty_t, xproperty)
64 66
 
65 67
 /** Main configuration structure */
66 68
 typedef struct
@@ -140,6 +142,8 @@ typedef struct
140 142
     bool need_lazy_banning;
141 143
     /** Tag list */
142 144
     tag_array_t tags;
145
+    /** List of registered xproperties */
146
+    xproperty_array_t xproperties;
143 147
 } awesome_t;
144 148
 
145 149
 extern awesome_t globalconf;

+ 4
- 13
lib/wibox/init.lua.in View File

@@ -48,19 +48,10 @@ function wibox:set_fg(c)
48 48
     self._drawable:set_fg(c)
49 49
 end
50 50
 
51
---- Helper function to make wibox:buttons() work as expected
52
-function wibox:buttons(...)
53
-    return self.drawin:buttons(...)
54
-end
55
-
56
---- Helper function to make wibox:struts() work as expected
57
-function wibox:struts(...)
58
-    return self.drawin:struts(...)
59
-end
60
-
61
---- Helper function to make wibox:geometry() work as expected
62
-function wibox:geometry(...)
63
-    return self.drawin:geometry(...)
51
+for _, k in pairs{ "buttons", "struts", "geometry", "get_xproperty", "set_xproperty" } do
52
+    wibox[k] = function(self, ...)
53
+        return self.drawin[k](self.drawin, ...)
54
+    end
64 55
 end
65 56
 
66 57
 local function setup_signals(_wibox)

+ 2
- 0
luaa.c View File

@@ -42,6 +42,7 @@
42 42
 #include "objects/drawable.h"
43 43
 #include "screen.h"
44 44
 #include "event.h"
45
+#include "property.h"
45 46
 #include "selection.h"
46 47
 #include "systray.h"
47 48
 #include "common/xcursor.h"
@@ -573,6 +574,7 @@ luaA_init(xdgHandle* xdg)
573 574
         { "emit_signal", luaA_awesome_emit_signal },
574 575
         { "systray", luaA_systray },
575 576
         { "load_image", luaA_load_image },
577
+        { "register_xproperty", luaA_register_xproperty },
576 578
         { "__index", luaA_awesome_index },
577 579
         { NULL, NULL }
578 580
     };

+ 6
- 0
luadoc/awesome.lua View File

@@ -37,6 +37,12 @@ module("awesome")
37 37
 -- @name load_image
38 38
 -- @class function
39 39
 
40
+--- Register a new xproperty.
41
+-- @param name The name of the X11 property
42
+-- @param type One of "string", "number" or "boolean"
43
+-- @name register_xproperty
44
+-- @class function
45
+
40 46
 --- Add a global signal.
41 47
 -- @param name A string with the event name.
42 48
 -- @param func The function to call.

+ 11
- 0
luadoc/client.lua View File

@@ -111,6 +111,17 @@ module("client")
111 111
 -- @name unmanage
112 112
 -- @class function
113 113
 
114
+--- Change a xproperty.
115
+-- @param name The name of the X11 property
116
+-- @param value The new value for the property
117
+-- @name set_xproperty
118
+-- @class function
119
+
120
+--- Get the value of a xproperty.
121
+-- @param name The name of the X11 property
122
+-- @name get_xproperty
123
+-- @class function
124
+
114 125
 --- Add a signal.
115 126
 -- @param name A signal name.
116 127
 -- @param func A function to call when the signal is emitted.

+ 11
- 0
luadoc/drawin.lua View File

@@ -40,6 +40,17 @@ module("drawin")
40 40
 -- @name geometry
41 41
 -- @class function
42 42
 
43
+--- Change a xproperty.
44
+-- @param name The name of the X11 property
45
+-- @param value The new value for the property
46
+-- @name set_xproperty
47
+-- @class function
48
+
49
+--- Get the value of a xproperty.
50
+-- @param name The name of the X11 property
51
+-- @name get_xproperty
52
+-- @class function
53
+
43 54
 --- Add a signal.
44 55
 -- @param name A signal name.
45 56
 -- @param func A function to call when the signal is emitted.

+ 98
- 0
objects/window.c View File

@@ -23,6 +23,7 @@
23 23
 #include "xwindow.h"
24 24
 #include "ewmh.h"
25 25
 #include "screen.h"
26
+#include "property.h"
26 27
 #include "objects/window.h"
27 28
 #include "common/atoms.h"
28 29
 #include "common/luaobject.h"
@@ -296,6 +297,101 @@ luaA_window_set_type(lua_State *L, window_t *w)
296 297
     return 0;
297 298
 }
298 299
 
300
+static xproperty_t *
301
+luaA_find_xproperty(lua_State *L, int idx)
302
+{
303
+    const char *name = luaL_checkstring(L, idx);
304
+    foreach(prop, globalconf.xproperties)
305
+        if (A_STREQ(prop->name, name))
306
+            return prop;
307
+    luaL_argerror(L, idx, "Unknown xproperty");
308
+    return NULL;
309
+}
310
+
311
+/** Set an xproperty.
312
+ * \param L The Lua VM state.
313
+ * \return The number of elements pushed on stack.
314
+ */
315
+static int
316
+luaA_window_set_xproperty(lua_State *L)
317
+{
318
+    window_t *w = luaA_checkudata(L, 1, &window_class);
319
+    xproperty_t *prop = luaA_find_xproperty(L, 2);
320
+    xcb_atom_t type;
321
+    uint8_t format;
322
+    size_t len;
323
+    uint32_t number;
324
+    const void *data;
325
+
326
+    if(lua_isnil(L, 3))
327
+    {
328
+        xcb_delete_property(globalconf.connection, w->window, prop->atom);
329
+    } else {
330
+        if(prop->type == PROP_STRING)
331
+        {
332
+            data = luaL_checklstring(L, 3, &len);
333
+            type = UTF8_STRING;
334
+            format = 8;
335
+        } else if(prop->type == PROP_NUMBER || prop->type == PROP_BOOLEAN)
336
+        {
337
+            if (prop->type == PROP_NUMBER)
338
+                number = luaL_checkinteger(L, 3);
339
+            else
340
+                number = luaA_checkboolean(L, 3);
341
+            data = &number;
342
+            len = 1;
343
+            type = XCB_ATOM_CARDINAL;
344
+            format = 32;
345
+        } else
346
+            fatal("Got an xproperty with invalid type");
347
+
348
+        xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE, w->window,
349
+                            prop->atom, type, format, len, data);
350
+    }
351
+    return 0;
352
+}
353
+
354
+/** Get an xproperty.
355
+ * \param L The Lua VM state.
356
+ * \return The number of elements pushed on stack.
357
+ */
358
+static int
359
+luaA_window_get_xproperty(lua_State *L)
360
+{
361
+    window_t *w = luaA_checkudata(L, 1, &window_class);
362
+    xproperty_t *prop = luaA_find_xproperty(L, 2);
363
+    xcb_atom_t type;
364
+    void *data;
365
+    xcb_get_property_reply_t *reply;
366
+
367
+    type = prop->type == PROP_STRING ? UTF8_STRING : XCB_ATOM_CARDINAL;
368
+    reply = xcb_get_property_reply(globalconf.connection,
369
+            xcb_get_property_unchecked(globalconf.connection, false, w->window,
370
+                prop->atom, type, 0, 4), NULL);
371
+    if(!reply)
372
+        return 0;
373
+
374
+    data = xcb_get_property_value(reply);
375
+
376
+    if(prop->type == PROP_STRING)
377
+        lua_pushlstring(L, data, reply->value_len);
378
+    else
379
+    {
380
+        if(reply->value_len <= 0)
381
+        {
382
+            p_delete(&reply);
383
+            return 0;
384
+        }
385
+        if(prop->type == PROP_NUMBER)
386
+            lua_pushinteger(L, *(uint32_t *) data);
387
+        else
388
+            lua_pushboolean(L, *(uint32_t *) data);
389
+    }
390
+
391
+    p_delete(&reply);
392
+    return 1;
393
+}
394
+
299 395
 /** Translate a window_type_t into the corresponding EWMH atom.
300 396
  * @param type The type to return.
301 397
  * @return The EWMH atom for this type.
@@ -360,6 +456,8 @@ window_class_setup(lua_State *L)
360 456
     {
361 457
         { "struts", luaA_window_struts },
362 458
         { "buttons", luaA_window_buttons },
459
+        { "set_xproperty", luaA_window_set_xproperty },
460
+        { "get_xproperty", luaA_window_get_xproperty },
363 461
         { NULL, NULL }
364 462
     };
365 463
 

+ 92
- 3
property.c View File

@@ -378,12 +378,41 @@ property_handle_xrootpmap_id(uint8_t state,
378 378
     return 0;
379 379
 }
380 380
 
381
+/** The property notify event handler handling xproperties.
382
+ * \param ev The event.
383
+ */
384
+static void
385
+property_handle_propertynotify_xproperty(xcb_property_notify_event_t *ev)
386
+{
387
+    xproperty_t *prop;
388
+    xproperty_t lookup = { .atom = ev->atom };
389
+    buffer_t buf;
390
+    void *obj;
391
+
392
+    prop = xproperty_array_lookup(&globalconf.xproperties, &lookup);
393
+    if(!prop)
394
+        /* Property is not registered */
395
+        return;
396
+
397
+    obj = client_getbywin(ev->window);
398
+    if(!obj)
399
+        obj = drawin_getbywin(ev->window);
400
+    if(!obj)
401
+        return;
402
+
403
+    /* Get us the name of the property */
404
+    buffer_inita(&buf, a_strlen(prop->name) + a_strlen("xproperty::") + 1);
405
+    buffer_addf(&buf, "xproperty::%s", prop->name);
406
+
407
+    /* And emit the right signal */
408
+    luaA_object_push(globalconf.L, obj);
409
+    luaA_object_emit_signal(globalconf.L, -1, buf.s, 0);
410
+    lua_pop(globalconf.L, 1);
411
+    buffer_wipe(&buf);
412
+}
381 413
 
382 414
 /** The property notify event handler.
383
- * \param data Unused data.
384
- * \param connection The connection to the X server.
385 415
  * \param ev The event.
386
- * \return Status code, 0 if everything's fine.
387 416
  */
388 417
 void
389 418
 property_handle_propertynotify(xcb_property_notify_event_t *ev)
@@ -393,6 +422,8 @@ property_handle_propertynotify(xcb_property_notify_event_t *ev)
393 422
 
394 423
     globalconf.timestamp = ev->time;
395 424
 
425
+    property_handle_propertynotify_xproperty(ev);
426
+
396 427
     /* Find the correct event handler */
397 428
 #define HANDLE(atom_, cb) \
398 429
     if (ev->atom == atom_) \
@@ -436,4 +467,62 @@ property_handle_propertynotify(xcb_property_notify_event_t *ev)
436 467
     (*handler)(ev->state, ev->window);
437 468
 }
438 469
 
470
+/** Register a new xproperty.
471
+ * \param L The Lua VM state.
472
+ * \return The number of elements pushed on stack.
473
+ * \luastack
474
+ * \lparam The name of the X11 property
475
+ * \lparam One of "string", "number" or "boolean"
476
+ */
477
+int
478
+luaA_register_xproperty(lua_State *L)
479
+{
480
+    const char *name;
481
+    struct xproperty property;
482
+    struct xproperty *found;
483
+    const char *const args[] = { "string", "number", "boolean" };
484
+    xcb_intern_atom_reply_t *atom_r;
485
+    int type;
486
+
487
+    name = luaL_checkstring(L, 1);
488
+    type = luaL_checkoption(L, 2, NULL, args);
489
+    if (type == 0)
490
+        property.type = PROP_STRING;
491
+    else if (type == 1)
492
+        property.type = PROP_NUMBER;
493
+    else
494
+        property.type = PROP_BOOLEAN;
495
+
496
+    atom_r = xcb_intern_atom_reply(globalconf.connection,
497
+                                   xcb_intern_atom_unchecked(globalconf.connection, false,
498
+                                                             a_strlen(name), name),
499
+                                   NULL);
500
+    if(!atom_r)
501
+        return 0;
502
+
503
+    property.atom = atom_r->atom;
504
+    p_delete(&atom_r);
505
+
506
+    found = xproperty_array_lookup(&globalconf.xproperties, &property);
507
+    if(found)
508
+    {
509
+        /* Property already registered */
510
+        if(found->type != property.type)
511
+            return luaL_error(L, "xproperty '%s' already registered with different type", name);
512
+    }
513
+    else
514
+    {
515
+        buffer_t buf;
516
+        buffer_inita(&buf, a_strlen(name) + a_strlen("xproperty::") + 1);
517
+        buffer_addf(&buf, "xproperty::%s", name);
518
+
519
+        property.name = a_strdup(name);
520
+        xproperty_array_insert(&globalconf.xproperties, property);
521
+        signal_add(&window_class.signals, buf.s);
522
+        buffer_wipe(&buf);
523
+    }
524
+
525
+    return 0;
526
+}
527
+
439 528
 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

+ 23
- 0
property.h View File

@@ -46,6 +46,29 @@ PROPERTY(net_wm_icon);
46 46
 #undef PROPERTY
47 47
 
48 48
 void property_handle_propertynotify(xcb_property_notify_event_t *ev);
49
+int luaA_register_xproperty(lua_State *L);
50
+
51
+struct xproperty {
52
+    xcb_atom_t atom;
53
+    const char *name;
54
+    enum {
55
+        /* UTF8_STRING */
56
+        PROP_STRING,
57
+        /* CARDINAL */
58
+        PROP_NUMBER,
59
+        /* CARDINAL with values 0 and 1 (or "0 and != 0") */
60
+        PROP_BOOLEAN
61
+    } type;
62
+};
63
+
64
+static inline int
65
+xproperty_cmp(const void *a, const void *b)
66
+{
67
+    const xproperty_t *x = a, *y = b;
68
+    return x->atom - y->atom;
69
+}
70
+
71
+BARRAY_FUNCS(xproperty_t, xproperty, DO_NOTHING, xproperty_cmp)
49 72
 
50 73
 #endif
51 74
 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

Loading…
Cancel
Save