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>
This commit is contained in:
Uli Schlachter 2014-03-07 13:49:24 +01:00
parent 2b1febeabe
commit 62e2dee4ba
9 changed files with 251 additions and 16 deletions

View File

@ -55,12 +55,14 @@ typedef struct button_t button_t;
typedef struct widget_t widget_t;
typedef struct client_t client_t;
typedef struct tag tag_t;
typedef struct xproperty xproperty_t;
ARRAY_TYPE(button_t *, button)
ARRAY_TYPE(tag_t *, tag)
ARRAY_TYPE(screen_t, screen)
ARRAY_TYPE(client_t *, client)
ARRAY_TYPE(drawin_t *, drawin)
ARRAY_TYPE(xproperty_t, xproperty)
/** Main configuration structure */
typedef struct
@ -140,6 +142,8 @@ typedef struct
bool need_lazy_banning;
/** Tag list */
tag_array_t tags;
/** List of registered xproperties */
xproperty_array_t xproperties;
} awesome_t;
extern awesome_t globalconf;

View File

@ -48,19 +48,10 @@ function wibox:set_fg(c)
self._drawable:set_fg(c)
end
--- Helper function to make wibox:buttons() work as expected
function wibox:buttons(...)
return self.drawin:buttons(...)
for _, k in pairs{ "buttons", "struts", "geometry", "get_xproperty", "set_xproperty" } do
wibox[k] = function(self, ...)
return self.drawin[k](self.drawin, ...)
end
--- Helper function to make wibox:struts() work as expected
function wibox:struts(...)
return self.drawin:struts(...)
end
--- Helper function to make wibox:geometry() work as expected
function wibox:geometry(...)
return self.drawin:geometry(...)
end
local function setup_signals(_wibox)

2
luaa.c
View File

@ -42,6 +42,7 @@
#include "objects/drawable.h"
#include "screen.h"
#include "event.h"
#include "property.h"
#include "selection.h"
#include "systray.h"
#include "common/xcursor.h"
@ -573,6 +574,7 @@ luaA_init(xdgHandle* xdg)
{ "emit_signal", luaA_awesome_emit_signal },
{ "systray", luaA_systray },
{ "load_image", luaA_load_image },
{ "register_xproperty", luaA_register_xproperty },
{ "__index", luaA_awesome_index },
{ NULL, NULL }
};

View File

@ -37,6 +37,12 @@ module("awesome")
-- @name load_image
-- @class function
--- Register a new xproperty.
-- @param name The name of the X11 property
-- @param type One of "string", "number" or "boolean"
-- @name register_xproperty
-- @class function
--- Add a global signal.
-- @param name A string with the event name.
-- @param func The function to call.

View File

@ -111,6 +111,17 @@ module("client")
-- @name unmanage
-- @class function
--- Change a xproperty.
-- @param name The name of the X11 property
-- @param value The new value for the property
-- @name set_xproperty
-- @class function
--- Get the value of a xproperty.
-- @param name The name of the X11 property
-- @name get_xproperty
-- @class function
--- Add a signal.
-- @param name A signal name.
-- @param func A function to call when the signal is emitted.

View File

@ -40,6 +40,17 @@ module("drawin")
-- @name geometry
-- @class function
--- Change a xproperty.
-- @param name The name of the X11 property
-- @param value The new value for the property
-- @name set_xproperty
-- @class function
--- Get the value of a xproperty.
-- @param name The name of the X11 property
-- @name get_xproperty
-- @class function
--- Add a signal.
-- @param name A signal name.
-- @param func A function to call when the signal is emitted.

View File

@ -23,6 +23,7 @@
#include "xwindow.h"
#include "ewmh.h"
#include "screen.h"
#include "property.h"
#include "objects/window.h"
#include "common/atoms.h"
#include "common/luaobject.h"
@ -296,6 +297,101 @@ luaA_window_set_type(lua_State *L, window_t *w)
return 0;
}
static xproperty_t *
luaA_find_xproperty(lua_State *L, int idx)
{
const char *name = luaL_checkstring(L, idx);
foreach(prop, globalconf.xproperties)
if (A_STREQ(prop->name, name))
return prop;
luaL_argerror(L, idx, "Unknown xproperty");
return NULL;
}
/** Set an xproperty.
* \param L The Lua VM state.
* \return The number of elements pushed on stack.
*/
static int
luaA_window_set_xproperty(lua_State *L)
{
window_t *w = luaA_checkudata(L, 1, &window_class);
xproperty_t *prop = luaA_find_xproperty(L, 2);
xcb_atom_t type;
uint8_t format;
size_t len;
uint32_t number;
const void *data;
if(lua_isnil(L, 3))
{
xcb_delete_property(globalconf.connection, w->window, prop->atom);
} else {
if(prop->type == PROP_STRING)
{
data = luaL_checklstring(L, 3, &len);
type = UTF8_STRING;
format = 8;
} else if(prop->type == PROP_NUMBER || prop->type == PROP_BOOLEAN)
{
if (prop->type == PROP_NUMBER)
number = luaL_checkinteger(L, 3);
else
number = luaA_checkboolean(L, 3);
data = &number;
len = 1;
type = XCB_ATOM_CARDINAL;
format = 32;
} else
fatal("Got an xproperty with invalid type");
xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE, w->window,
prop->atom, type, format, len, data);
}
return 0;
}
/** Get an xproperty.
* \param L The Lua VM state.
* \return The number of elements pushed on stack.
*/
static int
luaA_window_get_xproperty(lua_State *L)
{
window_t *w = luaA_checkudata(L, 1, &window_class);
xproperty_t *prop = luaA_find_xproperty(L, 2);
xcb_atom_t type;
void *data;
xcb_get_property_reply_t *reply;
type = prop->type == PROP_STRING ? UTF8_STRING : XCB_ATOM_CARDINAL;
reply = xcb_get_property_reply(globalconf.connection,
xcb_get_property_unchecked(globalconf.connection, false, w->window,
prop->atom, type, 0, 4), NULL);
if(!reply)
return 0;
data = xcb_get_property_value(reply);
if(prop->type == PROP_STRING)
lua_pushlstring(L, data, reply->value_len);
else
{
if(reply->value_len <= 0)
{
p_delete(&reply);
return 0;
}
if(prop->type == PROP_NUMBER)
lua_pushinteger(L, *(uint32_t *) data);
else
lua_pushboolean(L, *(uint32_t *) data);
}
p_delete(&reply);
return 1;
}
/** Translate a window_type_t into the corresponding EWMH atom.
* @param type The type to return.
* @return The EWMH atom for this type.
@ -360,6 +456,8 @@ window_class_setup(lua_State *L)
{
{ "struts", luaA_window_struts },
{ "buttons", luaA_window_buttons },
{ "set_xproperty", luaA_window_set_xproperty },
{ "get_xproperty", luaA_window_get_xproperty },
{ NULL, NULL }
};

View File

@ -378,12 +378,41 @@ property_handle_xrootpmap_id(uint8_t state,
return 0;
}
/** The property notify event handler handling xproperties.
* \param ev The event.
*/
static void
property_handle_propertynotify_xproperty(xcb_property_notify_event_t *ev)
{
xproperty_t *prop;
xproperty_t lookup = { .atom = ev->atom };
buffer_t buf;
void *obj;
prop = xproperty_array_lookup(&globalconf.xproperties, &lookup);
if(!prop)
/* Property is not registered */
return;
obj = client_getbywin(ev->window);
if(!obj)
obj = drawin_getbywin(ev->window);
if(!obj)
return;
/* Get us the name of the property */
buffer_inita(&buf, a_strlen(prop->name) + a_strlen("xproperty::") + 1);
buffer_addf(&buf, "xproperty::%s", prop->name);
/* And emit the right signal */
luaA_object_push(globalconf.L, obj);
luaA_object_emit_signal(globalconf.L, -1, buf.s, 0);
lua_pop(globalconf.L, 1);
buffer_wipe(&buf);
}
/** The property notify event handler.
* \param data Unused data.
* \param connection The connection to the X server.
* \param ev The event.
* \return Status code, 0 if everything's fine.
*/
void
property_handle_propertynotify(xcb_property_notify_event_t *ev)
@ -393,6 +422,8 @@ property_handle_propertynotify(xcb_property_notify_event_t *ev)
globalconf.timestamp = ev->time;
property_handle_propertynotify_xproperty(ev);
/* Find the correct event handler */
#define HANDLE(atom_, cb) \
if (ev->atom == atom_) \
@ -436,4 +467,62 @@ property_handle_propertynotify(xcb_property_notify_event_t *ev)
(*handler)(ev->state, ev->window);
}
/** Register a new xproperty.
* \param L The Lua VM state.
* \return The number of elements pushed on stack.
* \luastack
* \lparam The name of the X11 property
* \lparam One of "string", "number" or "boolean"
*/
int
luaA_register_xproperty(lua_State *L)
{
const char *name;
struct xproperty property;
struct xproperty *found;
const char *const args[] = { "string", "number", "boolean" };
xcb_intern_atom_reply_t *atom_r;
int type;
name = luaL_checkstring(L, 1);
type = luaL_checkoption(L, 2, NULL, args);
if (type == 0)
property.type = PROP_STRING;
else if (type == 1)
property.type = PROP_NUMBER;
else
property.type = PROP_BOOLEAN;
atom_r = xcb_intern_atom_reply(globalconf.connection,
xcb_intern_atom_unchecked(globalconf.connection, false,
a_strlen(name), name),
NULL);
if(!atom_r)
return 0;
property.atom = atom_r->atom;
p_delete(&atom_r);
found = xproperty_array_lookup(&globalconf.xproperties, &property);
if(found)
{
/* Property already registered */
if(found->type != property.type)
return luaL_error(L, "xproperty '%s' already registered with different type", name);
}
else
{
buffer_t buf;
buffer_inita(&buf, a_strlen(name) + a_strlen("xproperty::") + 1);
buffer_addf(&buf, "xproperty::%s", name);
property.name = a_strdup(name);
xproperty_array_insert(&globalconf.xproperties, property);
signal_add(&window_class.signals, buf.s);
buffer_wipe(&buf);
}
return 0;
}
// vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -46,6 +46,29 @@ PROPERTY(net_wm_icon);
#undef PROPERTY
void property_handle_propertynotify(xcb_property_notify_event_t *ev);
int luaA_register_xproperty(lua_State *L);
struct xproperty {
xcb_atom_t atom;
const char *name;
enum {
/* UTF8_STRING */
PROP_STRING,
/* CARDINAL */
PROP_NUMBER,
/* CARDINAL with values 0 and 1 (or "0 and != 0") */
PROP_BOOLEAN
} type;
};
static inline int
xproperty_cmp(const void *a, const void *b)
{
const xproperty_t *x = a, *y = b;
return x->atom - y->atom;
}
BARRAY_FUNCS(xproperty_t, xproperty, DO_NOTHING, xproperty_cmp)
#endif
// vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80