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:
parent
2b1febeabe
commit
62e2dee4ba
|
@ -55,12 +55,14 @@ typedef struct button_t button_t;
|
||||||
typedef struct widget_t widget_t;
|
typedef struct widget_t widget_t;
|
||||||
typedef struct client_t client_t;
|
typedef struct client_t client_t;
|
||||||
typedef struct tag tag_t;
|
typedef struct tag tag_t;
|
||||||
|
typedef struct xproperty xproperty_t;
|
||||||
|
|
||||||
ARRAY_TYPE(button_t *, button)
|
ARRAY_TYPE(button_t *, button)
|
||||||
ARRAY_TYPE(tag_t *, tag)
|
ARRAY_TYPE(tag_t *, tag)
|
||||||
ARRAY_TYPE(screen_t, screen)
|
ARRAY_TYPE(screen_t, screen)
|
||||||
ARRAY_TYPE(client_t *, client)
|
ARRAY_TYPE(client_t *, client)
|
||||||
ARRAY_TYPE(drawin_t *, drawin)
|
ARRAY_TYPE(drawin_t *, drawin)
|
||||||
|
ARRAY_TYPE(xproperty_t, xproperty)
|
||||||
|
|
||||||
/** Main configuration structure */
|
/** Main configuration structure */
|
||||||
typedef struct
|
typedef struct
|
||||||
|
@ -140,6 +142,8 @@ typedef struct
|
||||||
bool need_lazy_banning;
|
bool need_lazy_banning;
|
||||||
/** Tag list */
|
/** Tag list */
|
||||||
tag_array_t tags;
|
tag_array_t tags;
|
||||||
|
/** List of registered xproperties */
|
||||||
|
xproperty_array_t xproperties;
|
||||||
} awesome_t;
|
} awesome_t;
|
||||||
|
|
||||||
extern awesome_t globalconf;
|
extern awesome_t globalconf;
|
||||||
|
|
|
@ -48,19 +48,10 @@ function wibox:set_fg(c)
|
||||||
self._drawable:set_fg(c)
|
self._drawable:set_fg(c)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Helper function to make wibox:buttons() work as expected
|
for _, k in pairs{ "buttons", "struts", "geometry", "get_xproperty", "set_xproperty" } do
|
||||||
function wibox:buttons(...)
|
wibox[k] = function(self, ...)
|
||||||
return self.drawin:buttons(...)
|
return self.drawin[k](self.drawin, ...)
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
local function setup_signals(_wibox)
|
local function setup_signals(_wibox)
|
||||||
|
|
2
luaa.c
2
luaa.c
|
@ -42,6 +42,7 @@
|
||||||
#include "objects/drawable.h"
|
#include "objects/drawable.h"
|
||||||
#include "screen.h"
|
#include "screen.h"
|
||||||
#include "event.h"
|
#include "event.h"
|
||||||
|
#include "property.h"
|
||||||
#include "selection.h"
|
#include "selection.h"
|
||||||
#include "systray.h"
|
#include "systray.h"
|
||||||
#include "common/xcursor.h"
|
#include "common/xcursor.h"
|
||||||
|
@ -573,6 +574,7 @@ luaA_init(xdgHandle* xdg)
|
||||||
{ "emit_signal", luaA_awesome_emit_signal },
|
{ "emit_signal", luaA_awesome_emit_signal },
|
||||||
{ "systray", luaA_systray },
|
{ "systray", luaA_systray },
|
||||||
{ "load_image", luaA_load_image },
|
{ "load_image", luaA_load_image },
|
||||||
|
{ "register_xproperty", luaA_register_xproperty },
|
||||||
{ "__index", luaA_awesome_index },
|
{ "__index", luaA_awesome_index },
|
||||||
{ NULL, NULL }
|
{ NULL, NULL }
|
||||||
};
|
};
|
||||||
|
|
|
@ -37,6 +37,12 @@ module("awesome")
|
||||||
-- @name load_image
|
-- @name load_image
|
||||||
-- @class function
|
-- @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.
|
--- Add a global signal.
|
||||||
-- @param name A string with the event name.
|
-- @param name A string with the event name.
|
||||||
-- @param func The function to call.
|
-- @param func The function to call.
|
||||||
|
|
|
@ -111,6 +111,17 @@ module("client")
|
||||||
-- @name unmanage
|
-- @name unmanage
|
||||||
-- @class function
|
-- @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.
|
--- Add a signal.
|
||||||
-- @param name A signal name.
|
-- @param name A signal name.
|
||||||
-- @param func A function to call when the signal is emitted.
|
-- @param func A function to call when the signal is emitted.
|
||||||
|
|
|
@ -40,6 +40,17 @@ module("drawin")
|
||||||
-- @name geometry
|
-- @name geometry
|
||||||
-- @class function
|
-- @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.
|
--- Add a signal.
|
||||||
-- @param name A signal name.
|
-- @param name A signal name.
|
||||||
-- @param func A function to call when the signal is emitted.
|
-- @param func A function to call when the signal is emitted.
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include "xwindow.h"
|
#include "xwindow.h"
|
||||||
#include "ewmh.h"
|
#include "ewmh.h"
|
||||||
#include "screen.h"
|
#include "screen.h"
|
||||||
|
#include "property.h"
|
||||||
#include "objects/window.h"
|
#include "objects/window.h"
|
||||||
#include "common/atoms.h"
|
#include "common/atoms.h"
|
||||||
#include "common/luaobject.h"
|
#include "common/luaobject.h"
|
||||||
|
@ -296,6 +297,101 @@ luaA_window_set_type(lua_State *L, window_t *w)
|
||||||
return 0;
|
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.
|
/** Translate a window_type_t into the corresponding EWMH atom.
|
||||||
* @param type The type to return.
|
* @param type The type to return.
|
||||||
* @return The EWMH atom for this type.
|
* @return The EWMH atom for this type.
|
||||||
|
@ -360,6 +456,8 @@ window_class_setup(lua_State *L)
|
||||||
{
|
{
|
||||||
{ "struts", luaA_window_struts },
|
{ "struts", luaA_window_struts },
|
||||||
{ "buttons", luaA_window_buttons },
|
{ "buttons", luaA_window_buttons },
|
||||||
|
{ "set_xproperty", luaA_window_set_xproperty },
|
||||||
|
{ "get_xproperty", luaA_window_get_xproperty },
|
||||||
{ NULL, NULL }
|
{ NULL, NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
95
property.c
95
property.c
|
@ -378,12 +378,41 @@ property_handle_xrootpmap_id(uint8_t state,
|
||||||
return 0;
|
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.
|
/** The property notify event handler.
|
||||||
* \param data Unused data.
|
|
||||||
* \param connection The connection to the X server.
|
|
||||||
* \param ev The event.
|
* \param ev The event.
|
||||||
* \return Status code, 0 if everything's fine.
|
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
property_handle_propertynotify(xcb_property_notify_event_t *ev)
|
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;
|
globalconf.timestamp = ev->time;
|
||||||
|
|
||||||
|
property_handle_propertynotify_xproperty(ev);
|
||||||
|
|
||||||
/* Find the correct event handler */
|
/* Find the correct event handler */
|
||||||
#define HANDLE(atom_, cb) \
|
#define HANDLE(atom_, cb) \
|
||||||
if (ev->atom == atom_) \
|
if (ev->atom == atom_) \
|
||||||
|
@ -436,4 +467,62 @@ property_handle_propertynotify(xcb_property_notify_event_t *ev)
|
||||||
(*handler)(ev->state, ev->window);
|
(*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
|
// vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
||||||
|
|
23
property.h
23
property.h
|
@ -46,6 +46,29 @@ PROPERTY(net_wm_icon);
|
||||||
#undef PROPERTY
|
#undef PROPERTY
|
||||||
|
|
||||||
void property_handle_propertynotify(xcb_property_notify_event_t *ev);
|
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
|
#endif
|
||||||
// vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
// vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
||||||
|
|
Loading…
Reference in New Issue