diff --git a/LuaGObject/class.lua b/LuaGObject/class.lua index 6cb34b3f..e77d2ac8 100644 --- a/LuaGObject/class.lua +++ b/LuaGObject/class.lua @@ -360,8 +360,7 @@ function class.class_mt:derive(typename, ifaces) iface_addr = core.record.query(iface_addr, "addr") or iface_addr local iface_struct = core.record.new(iface._class, iface_addr) - -- Iterate through all interface overrides and assign to the - -- virtual callbacks. + -- Iterate through all interface overrides and assign to the virtual callbacks. for name, addr in pairs(override) do iface_struct[name] = addr end @@ -382,8 +381,7 @@ end class.derived_mt = class.class_mt:clone('derived', {}) --- Support for 'priv' pseudomember, holding table with user --- implementation data. +-- Support for 'priv' pseudomember, holding table with user implementation data. function class.derived_mt:_element(instance, symbol) -- Special handling of 'priv' attribute. if instance and symbol == 'priv' then return symbol, '_priv' end diff --git a/LuaGObject/component.lua b/LuaGObject/component.lua index bbc2e013..99c9fd1b 100644 --- a/LuaGObject/component.lua +++ b/LuaGObject/component.lua @@ -273,7 +273,7 @@ function component.create(info, mt, name) local gtype if core.gi.isinfo(info) then gtype = info.gtype - name = info.name + name = info.fullname else gtype = info and core.gtype(info) end diff --git a/LuaGObject/core.c b/LuaGObject/core.c index 70d0bae9..485eb7aa 100644 --- a/LuaGObject/core.c +++ b/LuaGObject/core.c @@ -1,21 +1,17 @@ /* - * Dynamic Lua binding to GObject using dynamic gobject-introspection. - * - * Copyright (c) 2010, 2011, 2012, 2013 Pavel Holejsovsky - * Licensed under the MIT license: - * http://www.opensource.org/licenses/mit-license.php - * - * Core C utility API. - */ +Dynamic Lua binding to GObject using dynamic gobject-introspection. +Copyright(c) 2010, 2011, 2012, 2013 Pavel Holejsovsky +Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php +Core C utility API. +*/ #include #include "lua_gobject.h" -/* GLib 2.32 deprecated GStaticRecMutex in favor of GRecMutex. For - older GLib versions, use still older version. */ +/* GLib 2.32 deprecated GStaticRecMutex in favor of GRecMutex. For older GLib versions, use still older version. */ #if !GLIB_CHECK_VERSION(2, 32, 0) #define GRecMutex GStaticRecMutex -#define G_REC_MUTEX_INIT = G_STATIC_REC_MUTEX_INIT +#define G_REC_MUTEX_INIT = G_STATIC_REC_MUTEX_INIT #define g_rec_mutex_init g_static_rec_mutex_init #define g_rec_mutex_lock g_static_rec_mutex_lock #define g_rec_mutex_unlock g_static_rec_mutex_unlock @@ -24,493 +20,465 @@ #define G_REC_MUTEX_INIT #endif -/* GLib 2.30 changed g_atomic_int_add() to return the new value. For - older GLib versions, use g_atomic_int_exchange_and_add() which did. */ +/* GLib 2.30 changed g_atomic_int_add() to return the new value. For older GLib versions, use g_atomic_int_exchange_and_add() which did. */ #if !GLIB_CHECK_VERSION(2, 30, 0) #ifdef g_atomic_int_add #undef g_atomic_int_add #endif #define g_atomic_int_add(atomic, val) \ - (g_atomic_int_exchange_and_add ((atomic), (val))) + (g_atomic_int_exchange_and_add((atomic),(val))) #endif volatile gint global_state_id = 0; #ifndef NDEBUG -const char *lua_gobject_sd (lua_State *L) -{ - int i; - static gchar *msg = 0; - g_free (msg); - msg = g_strdup (""); - int top = lua_gettop (L); - for (i = 1; i <= top; i++) - { - int t = lua_type (L, i); - gchar *item, *nmsg; - switch (t) - { - case LUA_TSTRING: - item = g_strdup_printf ("`%s'", lua_tostring (L, i)); - break; - - case LUA_TBOOLEAN: - item = g_strdup_printf (lua_toboolean (L, i) ? "true" : "false"); - break; - - case LUA_TNUMBER: - if (lua_isinteger(L, i)) - item = g_strdup_printf (LUA_GOBJECT_LUAINT_FORMAT, lua_tointeger (L, i)); - else - item = g_strdup_printf ("%g", lua_tonumber (L, i)); - break; - - default: - item = g_strdup_printf ("%s(%p)", lua_typename (L, t), - lua_topointer (L, i)); - break; +const char *lua_gobject_sd(lua_State *L) +{ + int i; + static gchar *msg = 0; + g_free(msg); + msg = g_strdup(""); + int top = lua_gettop(L); + for (i = 1; i <= top; i++) { + int t = lua_type(L, i); + gchar *item, *nmsg; + switch (t) { + case LUA_TSTRING: + item = g_strdup_printf("`%s'", lua_tostring(L, i)); + break; + + case LUA_TBOOLEAN: + item = g_strdup_printf(lua_toboolean(L, i) ? "true" : "false"); + break; + + case LUA_TNUMBER: + if (lua_isinteger(L, i)) + item = g_strdup_printf(LUA_GOBJECT_LUAINT_FORMAT, lua_tointeger(L, i)); + else + item = g_strdup_printf("%g", lua_tonumber(L, i)); + break; + + default: + item = g_strdup_printf("%s(%p)", lua_typename(L, t), + lua_topointer(L, i)); + break; + } + nmsg = g_strconcat(msg, " ", item, NULL); + g_free(msg); + g_free(item); + msg = nmsg; } - nmsg = g_strconcat (msg, " ", item, NULL); - g_free (msg); - g_free (item); - msg = nmsg; - } - return msg; + return msg; } #endif -/* lightuserdata of this address is a key in LUA_REGISTRYINDEX table - to repo table. */ +/* lightuserdata of this address is a key in LUA_REGISTRYINDEX table to repo table. */ static int repo; -/* lightuserdata of this address is a key in LUA_REGISTRYINDEX table - to index table mapping lightuserdata-gtype -> repotable. */ +/* lightuserdata of this address is a key in LUA_REGISTRYINDEX table to index table mapping lightuserdata-gtype -> repotable. */ static int repo_index; void * -lua_gobject_udata_test (lua_State *L, int narg, const char *name) -{ - void *udata = NULL; - luaL_checkstack (L, 2, ""); - lua_gobject_makeabs (L, narg); - if (lua_getmetatable (L, narg)) - { - luaL_getmetatable (L, name); - if (lua_equal (L, -1, -2)) - udata = lua_touserdata (L, narg); - lua_pop (L, 2); - } - return udata; +lua_gobject_udata_test(lua_State *L, int narg, const char *name) +{ + void *udata = NULL; + luaL_checkstack(L, 2, ""); + lua_gobject_makeabs(L, narg); + if (lua_getmetatable(L, narg)) { + luaL_getmetatable(L, name); + if (lua_equal(L, -1, -2)) + udata = lua_touserdata(L, narg); + lua_pop(L, 2); + } + return udata; } void -lua_gobject_cache_create (lua_State *L, gpointer key, const char *mode) -{ - lua_pushlightuserdata (L, key); - lua_newtable (L); - if (mode) - { - lua_newtable (L); - lua_pushstring (L, mode); - lua_setfield (L, -2, "__mode"); - lua_setmetatable (L, -2); - } - lua_rawset (L, LUA_REGISTRYINDEX); +lua_gobject_cache_create(lua_State *L, gpointer key, const char *mode) +{ + lua_pushlightuserdata(L, key); + lua_newtable(L); + if (mode) { + lua_newtable(L); + lua_pushstring(L, mode); + lua_setfield(L, -2, "__mode"); + lua_setmetatable(L, -2); + } + lua_rawset(L, LUA_REGISTRYINDEX); } int -lua_gobject_type_get_name (lua_State *L, GIBaseInfo *info) -{ - GSList *list = NULL, *i; - int n = 1; - lua_pushstring (L, gi_base_info_get_namespace (info)); - - if (GI_IS_CALLBACK_INFO (info)) - /* Avoid duplicate name for callbacks. */ - info = gi_base_info_get_container (info); - - /* Add names on the whole path, but in reverse order. */ - for (; info != NULL; info = gi_base_info_get_container (info)) - if (!GI_IS_TYPE_INFO (info)) - list = g_slist_prepend (list, info); - - for (i = list; i != NULL; i = g_slist_next (i)) - { - if (GI_IS_TYPE_INFO (i->data)) - { - lua_pushstring (L, "."); - lua_pushstring (L, gi_base_info_get_name (i->data)); - n += 2; +lua_gobject_type_get_name(lua_State *L, GIBaseInfo *info) +{ + GSList *list = NULL, *i; + int n = 1; + lua_pushstring(L, gi_base_info_get_namespace(info)); + + if (GI_IS_CALLBACK_INFO(info)) + /* Avoid duplicate name for callbacks. */ + info = gi_base_info_get_container(info); + + /* Add names on the whole path, but in reverse order. */ + for (; info != NULL; info = gi_base_info_get_container(info)) + if (!GI_IS_TYPE_INFO(info)) + list = g_slist_prepend(list, info); + + for (i = list; i != NULL; i = g_slist_next(i)) { + if (!GI_IS_TYPE_INFO(i->data)) { + lua_pushstring(L, "."); + lua_pushstring(L, gi_base_info_get_name(i->data)); + n += 2; + } } - } - g_slist_free (list); - return n; + g_slist_free(list); + return n; } void -lua_gobject_type_get_repotype (lua_State *L, GType gtype, GIBaseInfo *info) +lua_gobject_type_get_repotype(lua_State *L, GType gtype, GIBaseInfo *info) { - luaL_checkstack (L, 4, ""); - - /* Get repo-index table. */ - lua_pushlightuserdata (L, &repo_index); - lua_rawget (L, LUA_REGISTRYINDEX); - - /* Prepare gtype, if not given directly. */ - if (gtype == G_TYPE_INVALID && info && GI_IS_REGISTERED_TYPE_INFO (info)) - { - gtype = gi_registered_type_info_get_g_type (GI_REGISTERED_TYPE_INFO (info)); - if (gtype == G_TYPE_NONE) - gtype = G_TYPE_INVALID; - } - - /* First of all, check direct indexing of repo-index by gtype, - should be fastest. */ - if (gtype != G_TYPE_INVALID) - { - lua_pushlightuserdata (L, (gpointer) gtype); - lua_rawget (L, -2); - } - else - lua_pushnil (L); - - if (lua_isnil (L, -1)) - { - /* Not indexed yet. Try to lookup by name - this works when - lazy-loaded repo tables are not loaded yet. */ - if (!info && gtype != G_TYPE_INVALID) - { - info = gi_repository_find_by_gtype (lua_gobject_gi_get_repository (), gtype); - lua_gobject_gi_info_new (L, info); + luaL_checkstack(L, 4, ""); + + /* Get repo-index table. */ + lua_pushlightuserdata(L, &repo_index); + lua_rawget(L, LUA_REGISTRYINDEX); + + /* Prepare gtype, if not given directly. */ + if (gtype == G_TYPE_INVALID && info + && GI_IS_REGISTERED_TYPE_INFO(info)) { + gtype = gi_registered_type_info_get_g_type( + GI_REGISTERED_TYPE_INFO(info)); + if (gtype == G_TYPE_NONE) + gtype = G_TYPE_INVALID; } - else - /* Keep stack balanced as in the previous 'if' branch. */ - lua_pushnil (L); - - if (info) - { - lua_pushlightuserdata (L, &repo); - lua_rawget (L, LUA_REGISTRYINDEX); - lua_getfield (L, -1, gi_base_info_get_namespace (info)); - lua_getfield (L, -1, gi_base_info_get_name (info)); - lua_replace (L, -5); - lua_pop (L, 3); + + /* First of all, check direct indexing of repo-index by gtype, should be fastest. */ + if (gtype != G_TYPE_INVALID) { + lua_pushlightuserdata(L,(gpointer) gtype); + lua_rawget(L, -2); + } + else + lua_pushnil(L); + + if (lua_isnil(L, -1)) { + /* Not indexed yet. Try to lookup by name - this works when lazy-loaded repo tables are not loaded yet. */ + if (!info && gtype != G_TYPE_INVALID) { + info = gi_repository_find_by_gtype(lua_gobject_gi_get_repository(), + gtype); + lua_gobject_gi_info_new(L, info); + } + else + /* Keep stack balanced as in the previous 'if' branch. */ + lua_pushnil(L); + + if (info) { + lua_pushlightuserdata(L, &repo); + lua_rawget(L, LUA_REGISTRYINDEX); + lua_getfield(L, -1, gi_base_info_get_namespace(info)); + lua_getfield(L, -1, gi_base_info_get_name(info)); + lua_replace(L, -5); + lua_pop(L, 3); + } + else + lua_pop(L, 1); } - else - lua_pop (L, 1); - } - lua_replace (L, -2); + lua_replace(L, -2); } GType -lua_gobject_type_get_gtype (lua_State *L, int narg) +lua_gobject_type_get_gtype(lua_State *L, int narg) { - /* Handle simple cases natively, forward to Lua implementation for - the rest. */ - switch (lua_type (L, narg)) - { - case LUA_TNIL: - case LUA_TNONE: - return G_TYPE_INVALID; - - case LUA_TNUMBER: - return lua_tointeger (L, narg); - - case LUA_TLIGHTUSERDATA: - return (GType) lua_touserdata (L, narg); - - case LUA_TSTRING: - return g_type_from_name (lua_tostring (L, narg)); - - case LUA_TTABLE: - { - GType gtype; - lua_gobject_makeabs (L, narg); - lua_pushstring (L, "_gtype"); - lua_rawget (L, narg); - gtype = lua_gobject_type_get_gtype (L, -1); - lua_pop (L, 1); - return gtype; - } - - default: - return luaL_error (L, "GType expected, got %s", - lua_typename (L, lua_type (L, narg))); - } + /* Handle simple cases natively, forward to Lua implementation for the rest. */ + switch (lua_type(L, narg)) { + case LUA_TNIL: + case LUA_TNONE: + return G_TYPE_INVALID; + + case LUA_TNUMBER: + return lua_tointeger(L, narg); + + case LUA_TLIGHTUSERDATA: + return(GType) lua_touserdata(L, narg); + + case LUA_TSTRING: + return g_type_from_name(lua_tostring(L, narg)); + + case LUA_TTABLE: { + GType gtype; + lua_gobject_makeabs(L, narg); + lua_pushstring(L, "_gtype"); + lua_rawget(L, narg); + gtype = lua_gobject_type_get_gtype(L, -1); + lua_pop(L, 1); + return gtype; + } + + default: + return luaL_error(L, "GType expected, got %s", + lua_typename(L, lua_type(L, narg))); + } } -typedef struct _Guard -{ - gpointer data; - GDestroyNotify destroy; +typedef struct _Guard { + gpointer data; + GDestroyNotify destroy; } Guard; #define UD_GUARD "lua_gobject.guard" static int -guard_gc (lua_State *L) +guard_gc(lua_State *L) { - Guard *guard = lua_touserdata (L, 1); - if (guard->data != NULL) - guard->destroy (guard->data); - return 0; + Guard *guard = lua_touserdata(L, 1); + if (guard->data != NULL) + guard->destroy(guard->data); + return 0; } gpointer * -lua_gobject_guard_create (lua_State *L, GDestroyNotify destroy) -{ - Guard *guard = lua_newuserdata (L, sizeof (Guard)); - g_assert (destroy != NULL); - luaL_getmetatable (L, UD_GUARD); - lua_setmetatable (L, -2); - guard->data = NULL; - guard->destroy = destroy; - return &guard->data; +lua_gobject_guard_create(lua_State *L, GDestroyNotify destroy) +{ + Guard *guard = lua_newuserdata(L, sizeof(Guard)); + g_assert(destroy != NULL); + luaL_getmetatable(L, UD_GUARD); + lua_setmetatable(L, -2); + guard->data = NULL; + guard->destroy = destroy; + return &guard->data; } /* Converts any allowed GType kind to lightuserdata form. */ static int -core_gtype (lua_State *L) +core_gtype(lua_State *L) { - lua_pushlightuserdata (L, (gpointer) lua_gobject_type_get_gtype (L, 1)); - return 1; + lua_pushlightuserdata(L,(gpointer) lua_gobject_type_get_gtype(L, 1)); + return 1; } /* Converts either GType or gi.info into repotype table. */ static int -core_repotype (lua_State *L) -{ - GType gtype = G_TYPE_INVALID; - GIBaseInfo **info = lua_gobject_udata_test (L, 1, LUA_GOBJECT_GI_INFO); - if (!info) - gtype = lua_gobject_type_get_gtype (L, 1); - lua_gobject_type_get_repotype (L, gtype, info ? *info : NULL); - return 1; +core_repotype(lua_State *L) +{ + GType gtype = G_TYPE_INVALID; + GIBaseInfo **info = lua_gobject_udata_test(L, 1, LUA_GOBJECT_GI_INFO); + if (!info) + gtype = lua_gobject_type_get_gtype(L, 1); + lua_gobject_type_get_repotype(L, gtype, info ? *info : NULL); + return 1; } /* Instantiate constant from given gi_info. */ static int -core_constant (lua_State *L) -{ - /* Get typeinfo of the constant. */ - GIArgument val; - GIConstantInfo *ci = *(GIConstantInfo **) luaL_checkudata (L, 1, LUA_GOBJECT_GI_INFO); - GITypeInfo *ti = gi_constant_info_get_type_info (ci); - lua_gobject_gi_info_new (L, GI_BASE_INFO (ti)); - gi_constant_info_get_value (ci, &val); - lua_gobject_marshal_2lua (L, ti, NULL, GI_DIRECTION_IN, GI_TRANSFER_NOTHING, &val, - 0, NULL, NULL); - return 1; +core_constant(lua_State *L) +{ + /* Get typeinfo of the constant. */ + GIArgument val; + GIConstantInfo *ci = *(GIConstantInfo **) luaL_checkudata(L, 1, + LUA_GOBJECT_GI_INFO); + GITypeInfo *ti = gi_constant_info_get_type_info(ci); + lua_gobject_gi_info_new(L, GI_BASE_INFO(ti)); + gi_constant_info_get_value(ci, &val); + lua_gobject_marshal_2lua(L, ti, NULL, GI_DIRECTION_IN, + GI_TRANSFER_NOTHING, &val, 0, NULL, NULL); + return 1; } typedef struct _LgiStateMutex { - /* Pointer to either local state lock (next member of this - structure) or to global package lock. */ - GRecMutex *mutex; - GRecMutex state_mutex; + /* Pointer to either local state lock(next member of this structure) or to global package lock. */ + GRecMutex *mutex; + GRecMutex state_mutex; } LgiStateMutex; -/* Global package lock (the one used for - gdk_threads_enter/clutter_threads_enter) */ +/* Global package lock(the one used for gdk_threads_enter/clutter_threads_enter) */ static GRecMutex package_mutex G_REC_MUTEX_INIT; /* GC method for GRecMutex structure, which lives inside lua_State. */ static int -call_mutex_gc (lua_State* L) +call_mutex_gc(lua_State* L) { - LgiStateMutex *mutex = lua_touserdata (L, 1); - g_rec_mutex_unlock (mutex->mutex); - g_rec_mutex_clear (&mutex->state_mutex); - return 0; + LgiStateMutex *mutex = lua_touserdata(L, 1); + g_rec_mutex_unlock(mutex->mutex); + g_rec_mutex_clear(&mutex->state_mutex); + return 0; } /* MT for CallMutex. */ static int call_mutex_mt; -/* lightuserdata of address of this member is key to LUA_REGISTRYINDEX - where CallMutex instance for this state resides. */ +/* lightuserdata of address of this member is key to LUA_REGISTRYINDEX where CallMutex instance for this state resides. */ static int call_mutex; gpointer -lua_gobject_state_get_lock (lua_State *L) -{ - gpointer state_lock; - lua_pushlightuserdata (L, &call_mutex); - lua_gettable (L, LUA_REGISTRYINDEX); - state_lock = lua_touserdata (L, -1); - lua_pop (L, 1); - return state_lock; +lua_gobject_state_get_lock(lua_State *L) +{ + gpointer state_lock; + lua_pushlightuserdata(L, &call_mutex); + lua_gettable(L, LUA_REGISTRYINDEX); + state_lock = lua_touserdata(L, -1); + lua_pop(L, 1); + return state_lock; } void -lua_gobject_state_enter (gpointer state_lock) +lua_gobject_state_enter(gpointer state_lock) { - LgiStateMutex *mutex = state_lock; - GRecMutex *wait_on; - - /* There is a complication with lock switching. During the wait for - the lock, someone could call core.registerlock() and thus change - the lock protecting the state. Accomodate for this situation. */ - for (;;) - { - wait_on = g_atomic_pointer_get (&mutex->mutex); - g_rec_mutex_lock (wait_on); - if (wait_on == mutex->mutex) - break; - - /* The lock is changed, unlock this one and wait again. */ - g_rec_mutex_unlock (wait_on); - } + LgiStateMutex *mutex = state_lock; + GRecMutex *wait_on; + + /* There is a complication with lock switching. During the wait for the lock, someone could call core.registerlock() and thus change the lock protecting the state. Accomodate for this situation. */ + for (;;) { + wait_on = g_atomic_pointer_get(&mutex->mutex); + g_rec_mutex_lock(wait_on); + if (wait_on == mutex->mutex) + break; + + /* The lock is changed, unlock this one and wait again. */ + g_rec_mutex_unlock(wait_on); + } } void -lua_gobject_state_leave (gpointer state_lock) +lua_gobject_state_leave(gpointer state_lock) { - /* Get pointer to the call mutex belonging to this state. */ - LgiStateMutex *mutex = state_lock; - g_rec_mutex_unlock (mutex->mutex); + /* Get pointer to the call mutex belonging to this state. */ + LgiStateMutex *mutex = state_lock; + g_rec_mutex_unlock(mutex->mutex); } static const char* log_levels[] = { - "ERROR", "CRITICAL", "WARNING", "MESSAGE", "INFO", "DEBUG", "???", NULL + "ERROR", "CRITICAL", "WARNING", "MESSAGE", "INFO", "DEBUG", "???", NULL }; static int -core_log (lua_State *L) +core_log(lua_State *L) { - const char *domain = luaL_checkstring (L, 1); - int level = 1 << (luaL_checkoption (L, 2, log_levels[5], log_levels) + 2); - const char *message = luaL_checkstring (L, 3); + const char *domain = luaL_checkstring(L, 1); + int level = 1 <<(luaL_checkoption(L, 2, log_levels[5], log_levels) + 2); + const char *message = luaL_checkstring(L, 3); #if GLIB_CHECK_VERSION(2, 50, 0) - /* TODO: We can include more debug information such as lua line numbers */ - g_log_structured (domain, level, "MESSAGE", "%s", message); + /* TODO: We can include more debug information such as lua line numbers */ + g_log_structured(domain, level, "MESSAGE", "%s", message); #else - g_log (domain, level, "%s", message); + g_log(domain, level, "%s", message); #endif - return 0; + return 0; } static int -core_yield (lua_State *L) -{ - /* Perform yield with unlocked mutex; this might force another - threads waiting on the mutex to perform what they need to do - (i.e. enter Lua with callbacks). */ - gpointer state_lock = lua_gobject_state_get_lock (L); - lua_gobject_state_leave (state_lock); - g_thread_yield (); - lua_gobject_state_enter (state_lock); - return 0; +core_yield(lua_State *L) +{ + /* Perform yield with unlocked mutex; this might force another threads waiting on the mutex to perform what they need to do (i.e. enter Lua with callbacks). */ + gpointer state_lock = lua_gobject_state_get_lock(L); + lua_gobject_state_leave(state_lock); + g_thread_yield(); + lua_gobject_state_enter(state_lock); + return 0; } static void -package_lock_enter (void) +package_lock_enter(void) { - g_rec_mutex_lock (&package_mutex); + g_rec_mutex_lock(&package_mutex); } static void -package_lock_leave (void) +package_lock_leave(void) { - g_rec_mutex_unlock (&package_mutex); + g_rec_mutex_unlock(&package_mutex); } static gpointer package_lock_register[8] = { NULL }; static int -core_registerlock (lua_State *L) -{ - void (*set_lock_functions)(GCallback, GCallback); - LgiStateMutex *mutex; - GRecMutex *wait_on; - unsigned i; - - /* Get registration function. */ - luaL_checktype (L, 1, LUA_TLIGHTUSERDATA); - set_lock_functions = lua_touserdata (L, 1); - luaL_argcheck (L, set_lock_functions != NULL, 1, "NULL function"); - - /* Check, whether this package was already registered. */ - for (i = 0; i < G_N_ELEMENTS (package_lock_register) && - package_lock_register[i] != set_lock_functions; i++) - { - if (package_lock_register[i] == NULL) - { - /* Register our package lock functions. */ - package_lock_register[i] = set_lock_functions; - set_lock_functions (package_lock_enter, package_lock_leave); - break; +core_registerlock(lua_State *L) +{ + void(*set_lock_functions)(GCallback, GCallback); + LgiStateMutex *mutex; + GRecMutex *wait_on; + unsigned i; + + /* Get registration function. */ + luaL_checktype(L, 1, LUA_TLIGHTUSERDATA); + set_lock_functions = lua_touserdata(L, 1); + luaL_argcheck(L, set_lock_functions != NULL, 1, "NULL function"); + + /* Check, whether this package was already registered. */ + for (i = 0; i < G_N_ELEMENTS(package_lock_register) && + package_lock_register[i] != set_lock_functions; i++) { + if (package_lock_register[i] == NULL) { + /* Register our package lock functions. */ + package_lock_register[i] = set_lock_functions; + set_lock_functions(package_lock_enter, package_lock_leave); + break; + } + } + + /* Switch our statelock to actually use packagelock. */ + lua_pushlightuserdata(L, &call_mutex); + lua_rawget(L, LUA_REGISTRYINDEX); + mutex = lua_touserdata(L, -1); + wait_on = g_atomic_pointer_get(&mutex->mutex); + if (wait_on != &package_mutex) { + g_rec_mutex_lock(&package_mutex); + g_atomic_pointer_set(&mutex->mutex, &package_mutex); + g_rec_mutex_unlock(wait_on); } - } - - /* Switch our statelock to actually use packagelock. */ - lua_pushlightuserdata (L, &call_mutex); - lua_rawget (L, LUA_REGISTRYINDEX); - mutex = lua_touserdata (L, -1); - wait_on = g_atomic_pointer_get (&mutex->mutex); - if (wait_on != &package_mutex) - { - g_rec_mutex_lock (&package_mutex); - g_atomic_pointer_set (&mutex->mutex, &package_mutex); - g_rec_mutex_unlock (wait_on); - } - return 0; + return 0; } static int -core_band (lua_State *L) +core_band(lua_State *L) { - lua_pushinteger (L, (lua_gobject_Unsigned)luaL_checkinteger (L, 1) - & (lua_gobject_Unsigned)luaL_checkinteger (L, 2)); - return 1; + lua_pushinteger(L,(lua_gobject_Unsigned)luaL_checkinteger(L, 1) + &(lua_gobject_Unsigned)luaL_checkinteger(L, 2)); + return 1; } static int -core_bor (lua_State *L) +core_bor(lua_State *L) { - lua_pushinteger (L, (lua_gobject_Unsigned)luaL_checkinteger (L, 1) - | (lua_gobject_Unsigned)luaL_checkinteger (L, 2)); - return 1; + lua_pushinteger(L,(lua_gobject_Unsigned)luaL_checkinteger(L, 1) + | (lua_gobject_Unsigned)luaL_checkinteger(L, 2)); + return 1; } #define UD_MODULE "lua_gobject.core.module" static int -module_gc (lua_State *L) +module_gc(lua_State *L) { - GModule **module = luaL_checkudata (L, 1, UD_MODULE); - g_module_close (*module); + GModule **module = luaL_checkudata(L, 1, UD_MODULE); + g_module_close(*module); - /* Unset the metatable / make the module unusable */ - lua_pushnil (L); - lua_setmetatable (L, 1); - return 0; + /* Unset the metatable / make the module unusable */ + lua_pushnil(L); + lua_setmetatable(L, 1); + return 0; } static int -module_index (lua_State *L) +module_index(lua_State *L) { - GModule **module = luaL_checkudata (L, 1, UD_MODULE); - gpointer address; - if (g_module_symbol (*module, luaL_checkstring (L, 2), &address)) - { - lua_pushlightuserdata (L, address); - return 1; - } - - lua_pushnil (L); - lua_pushstring (L, g_module_error ()); - return 2; + GModule **module = luaL_checkudata(L, 1, UD_MODULE); + gpointer address; + if (g_module_symbol(*module, luaL_checkstring(L, 2), &address)) { + lua_pushlightuserdata(L, address); + return 1; + } + + lua_pushnil(L); + lua_pushstring(L, g_module_error()); + return 2; } static const struct luaL_Reg module_reg[] = { - { "__gc", module_gc }, - { "__index", module_index }, - { NULL, NULL } + { "__gc", module_gc }, + { "__index", module_index }, + { NULL, NULL } }; #if defined(G_WITH_CYGWIN) @@ -532,262 +500,231 @@ static const struct luaL_Reg module_reg[] = { #define MODULE_NAME_FORMAT_PLAIN "lib%s.so" #endif -/* Creates 'module' object which resolves symbol names to - lightuserdata addresses. - module, path = core.module(basename[, version]) */ +/* Creates 'module' object which resolves symbol names to lightuserdata addresses. +module, path = core.module(basename[, version]) */ static int -core_module (lua_State *L) +core_module(lua_State *L) { - char *name; + char *name; - /* If the version is present, combine it with basename. - Except on OpenBSD, where libraries are versioned like libfoo.so.0.0 - and we will always load the shared object with the highest version - number. - */ + /* If the version is present, combine it with basename. Except on OpenBSD, where libraries are versioned like libfoo.so.0.0 and we will always load the shared object with the highest version number. */ #ifndef __OpenBSD__ - if (!lua_isnoneornil (L, 2)) - name = g_strdup_printf (MODULE_NAME_FORMAT_VERSION, - luaL_checkstring (L, 1), - (int) luaL_checkinteger (L, 2)); - else + if (!lua_isnoneornil(L, 2)) + name = g_strdup_printf(MODULE_NAME_FORMAT_VERSION, + luaL_checkstring(L, 1), + (int) luaL_checkinteger(L, 2)); + else #endif - name = g_strdup_printf (MODULE_NAME_FORMAT_PLAIN, - luaL_checkstring (L, 1)); + name = g_strdup_printf(MODULE_NAME_FORMAT_PLAIN, + luaL_checkstring(L, 1)); #if defined(__APPLE__) -/* GLib 2.76 improved g_module_open() on MacOS, see - https://gitlab.gnome.org/GNOME/glib/-/merge_requests/2950. For - older GLib versions, use the previous workaround. */ +/* GLib 2.76 improved g_module_open() on MacOS, see https://gitlab.gnome.org/GNOME/glib/-/merge_requests/2950. For older GLib versions, use the previous workaround. */ #if !GLIB_CHECK_VERSION(2, 76, 0) - char *path = g_module_build_path (GOBJECT_INTROSPECTION_LIBDIR, - name); - g_free(name); - name = path; + char *path = g_module_build_path(GOBJECT_INTROSPECTION_LIBDIR, + name); + g_free(name); + name = path; #endif #endif - /* Try to load the module. */ - GModule *module = g_module_open (name, 0); - if (module == NULL) - { - lua_pushnil (L); - goto end; - } + /* Try to load the module. */ + GModule *module = g_module_open(name, 0); + if (module == NULL) { + lua_pushnil(L); + goto end; + } - /* Embed the module in the userdata for the module. */ - *(GModule **) lua_newuserdata (L, sizeof (module)) = module; - luaL_getmetatable (L, UD_MODULE); - lua_setmetatable (L, -2); + /* Embed the module in the userdata for the module. */ + *(GModule **) lua_newuserdata(L, sizeof(module)) = module; + luaL_getmetatable(L, UD_MODULE); + lua_setmetatable(L, -2); end: - lua_pushstring (L, name); - g_free (name); - return 2; + lua_pushstring(L, name); + g_free(name); + return 2; } -static int core_upcase (lua_State *L) +static int core_upcase(lua_State *L) { - gchar *str = g_ascii_strup (luaL_checkstring (L, 1), -1); - lua_pushstring (L, str); - g_free (str); - return 1; + gchar *str = g_ascii_strup(luaL_checkstring(L, 1), -1); + lua_pushstring(L, str); + g_free(str); + return 1; } -static int core_downcase (lua_State *L) +static int core_downcase(lua_State *L) { - gchar *str = g_ascii_strdown (luaL_checkstring (L, 1), -1); - lua_pushstring (L, str); - g_free (str); - return 1; + gchar *str = g_ascii_strdown(luaL_checkstring(L, 1), -1); + lua_pushstring(L, str); + g_free(str); + return 1; } static const struct luaL_Reg lua_gobject_reg[] = { - { "log", core_log }, - { "gtype", core_gtype }, - { "repotype", core_repotype }, - { "constant", core_constant }, - { "yield", core_yield }, - { "registerlock", core_registerlock }, - { "band", core_band }, - { "bor", core_bor }, - { "module", core_module }, - { "upcase", core_upcase }, - { "downcase", core_downcase }, - { NULL, NULL } + { "log", core_log }, + { "gtype", core_gtype }, + { "repotype", core_repotype }, + { "constant", core_constant }, + { "yield", core_yield }, + { "registerlock", core_registerlock }, + { "band", core_band }, + { "bor", core_bor }, + { "module", core_module }, + { "upcase", core_upcase }, + { "downcase", core_downcase }, + { NULL, NULL } }; static void -create_repo_table (lua_State *L, const char *name, void *key) +create_repo_table(lua_State *L, const char *name, void *key) { - lua_newtable (L); - lua_pushlightuserdata (L, key); - lua_pushvalue (L, -2); - lua_rawset (L, LUA_REGISTRYINDEX); - lua_setfield (L, -2, name); + lua_newtable(L); + lua_pushlightuserdata(L, key); + lua_pushvalue(L, -2); + lua_rawset(L, LUA_REGISTRYINDEX); + lua_setfield(L, -2, name); } static void -set_resident (lua_State *L) -{ - /* Get '_CLIBS' table from the registry (Lua5.2). */ - lua_getfield (L, LUA_REGISTRYINDEX, "_CLIBS"); - if (!lua_isnil (L, -1)) - { - /* Remove the very last item in they array part, which is handle - to our loaded module used by _CLIBS.gctm to clean modules - upon state cleanup. But before removing it, check, that it is - really the handle of our module. Our module filename is - passed as arg 2. */ - lua_pushvalue (L, 2); - lua_gettable (L, -2); - lua_rawgeti (L, -2, lua_objlen (L, -2)); - if (lua_equal (L, -1, -2)) - { - lua_pushnil (L); - lua_rawseti (L, -4, lua_objlen (L, -4)); - } - lua_pop (L, 3); - return; - } - else - { - if (lua_gettop(L) == 3) - { - /* Some Lua versions give us the path to the .so on the stack. - Just load & leak it. */ - GModule* module = g_module_open(lua_tostring(L, 2), - G_MODULE_BIND_LAZY | - G_MODULE_BIND_LOCAL); - if (module != NULL) - return; - } - - /* This hack tries to enumerate the whole registry table and - find 'LOADLIB: path' library. When it detects itself, it - just removes pointer to the loaded library, disallowing Lua - to close it, thus leaving it resident even when the state is - closed. */ - - /* Note: 'nil' is on the stack from lua_getfield() call above. */ - while (lua_next (L, LUA_REGISTRYINDEX)) - { - if (lua_type (L, -2) == LUA_TSTRING) - { - const char *str = lua_tostring (L, -2); - if (g_str_has_prefix (str, "LOADLIB: ") && - strstr (str, "lua_gobject_core")) - { - /* NULL the pointer to the loaded library. */ - if (lua_type (L, -1) == LUA_TUSERDATA) - { - gpointer *lib = lua_touserdata (L, -1); - *lib = NULL; - } - - /* Clean the stack and return. */ - lua_pop (L, 2); - return; +set_resident(lua_State *L) +{ + /* Get '_CLIBS' table from the registry(Lua5.2). */ + lua_getfield(L, LUA_REGISTRYINDEX, "_CLIBS"); + if (!lua_isnil(L, -1)) { + /* Remove the very last item in they array part, which is handle + to our loaded module used by _CLIBS.gctm to clean modules + upon state cleanup. But before removing it, check, that it is + really the handle of our module. Our module filename is + passed as arg 2. */ + lua_pushvalue(L, 2); + lua_gettable(L, -2); + lua_rawgeti(L, -2, lua_objlen(L, -2)); + if (lua_equal(L, -1, -2)) { + lua_pushnil(L); + lua_rawseti(L, -4, lua_objlen(L, -4)); + } + lua_pop(L, 3); + return; + } else { + if (lua_gettop(L) == 3) { + /* Some Lua versions give us the path to the .so on the stack. Just load & leak it. */ + GModule* module = g_module_open(lua_tostring(L, 2), + G_MODULE_BIND_LAZY | + G_MODULE_BIND_LOCAL); + if (module != NULL) + return; } - } - lua_pop (L, 1); + /* This hack tries to enumerate the whole registry table and find 'LOADLIB: path' library. When it detects itself, it just removes pointer to the loaded library, disallowing Lua to close it, thus leaving it resident even when the state is closed. */ + + /* Note: 'nil' is on the stack from lua_getfield() call above. */ + while (lua_next(L, LUA_REGISTRYINDEX)) { + if (lua_type(L, -2) == LUA_TSTRING) { + const char *str = lua_tostring(L, -2); + if (g_str_has_prefix(str, "LOADLIB: ") && + strstr(str, "lua_gobject_core")) { + /* NULL the pointer to the loaded library. */ + if (lua_type(L, -1) == LUA_TUSERDATA) { + gpointer *lib = lua_touserdata(L, -1); + *lib = NULL; + } + + /* Clean the stack and return. */ + lua_pop(L, 2); + return; + } + } + + lua_pop(L, 1); + } } - } } G_MODULE_EXPORT int -luaopen_LuaGObject_lua_gobject_core (lua_State* L) +luaopen_LuaGObject_lua_gobject_core(lua_State* L) { - LgiStateMutex *mutex; - gint state_id; - - /* Try to make itself resident. This is needed because this dynamic - module is 'statically' linked with glib/gobject, and these - libraries are not designed to be unloaded. Once they are - unloaded, they cannot be safely loaded again into the same - process. To avoid problems when repeatedly opening and closing - lua_States and loading lua_gobject into them, we try to make the whole - 'core' module resident. */ - set_resident (L); + LgiStateMutex *mutex; + gint state_id; + + /* Try to make itself resident. This is needed because this dynamic module is 'statically' linked with glib/gobject, and these libraries are not designed to be unloaded. Once they are unloaded, they cannot be safely loaded again into the same process. To avoid problems when repeatedly opening and closing lua_States and loading lua_gobject into them, we try to make the whole 'core' module resident. */ + set_resident(L); #if !GLIB_CHECK_VERSION(2, 36, 0) - g_type_init (); + g_type_init(); #endif - /* Early GLib initializations. Make sure that following fundamental - G_TYPEs are already initialized. */ - volatile GType unused; - unused = G_TYPE_DATE; - unused = G_TYPE_REGEX; - unused = G_TYPE_DATE_TIME; - unused = G_TYPE_VARIANT_TYPE; - unused = G_TYPE_STRV; - unused = unused; - - /* Register 'guard' metatable. */ - luaL_newmetatable (L, UD_GUARD); - lua_pushcfunction (L, guard_gc); - lua_setfield (L, -2, "__gc"); - lua_pop (L, 1); - - /* Register 'module' metatable. */ - luaL_newmetatable (L, UD_MODULE); - luaL_register (L, NULL, module_reg); - lua_pop (L, 1); - - /* Register 'call-mutex' metatable. */ - lua_pushlightuserdata (L, &call_mutex_mt); - lua_newtable (L); - lua_pushcfunction (L, call_mutex_gc); - lua_setfield (L, -2, "__gc"); - lua_rawset (L, LUA_REGISTRYINDEX); - - /* Create call mutex guard, keep it locked initially (it is unlocked - only when we are calling out to GObject-C code) and store it into - the registry. */ - lua_pushlightuserdata (L, &call_mutex); - mutex = lua_newuserdata (L, sizeof (*mutex)); - mutex->mutex = &mutex->state_mutex; - g_rec_mutex_init (&mutex->state_mutex); - g_rec_mutex_lock (&mutex->state_mutex); - lua_pushlightuserdata (L, &call_mutex_mt); - lua_rawget (L, LUA_REGISTRYINDEX); - lua_setmetatable (L, -2); - lua_rawset (L, LUA_REGISTRYINDEX); - - /* Register 'lua_gobject.core' interface. */ - lua_newtable (L); - luaL_register (L, NULL, lua_gobject_reg); - - /* Add the state ID */ - state_id = g_atomic_int_add (&global_state_id, 1); - if (state_id == 0) - lua_pushliteral (L, ""); - else - lua_pushfstring (L, "+L%d", state_id); - lua_setfield (L, -2, "id"); - - /* Add lock and enter/leave locking functions. */ - lua_pushlightuserdata (L, lua_gobject_state_get_lock (L)); - lua_setfield (L, -2, "lock"); - lua_pushlightuserdata (L, lua_gobject_state_enter); - lua_setfield (L, -2, "enter"); - lua_pushlightuserdata (L, lua_gobject_state_leave); - lua_setfield (L, -2, "leave"); - - /* Create repo and index table. */ - create_repo_table (L, "index", &repo_index); - create_repo_table (L, "repo", &repo); - - /* Initialize modules. */ - lua_gobject_buffer_init (L); - lua_gobject_gi_init (L); - lua_gobject_marshal_init (L); - lua_gobject_record_init (L); - lua_gobject_object_init (L); - lua_gobject_callable_init (L); - - /* Return registration table. */ - return 1; + /* Early GLib initializations. Make sure that following fundamental G_TYPEs are already initialized. */ + volatile GType unused; + unused = G_TYPE_DATE; + unused = G_TYPE_REGEX; + unused = G_TYPE_DATE_TIME; + unused = G_TYPE_VARIANT_TYPE; + unused = G_TYPE_STRV; + unused = unused; + + /* Register 'guard' metatable. */ + luaL_newmetatable(L, UD_GUARD); + lua_pushcfunction(L, guard_gc); + lua_setfield(L, -2, "__gc"); + lua_pop(L, 1); + + /* Register 'module' metatable. */ + luaL_newmetatable(L, UD_MODULE); + luaL_register(L, NULL, module_reg); + lua_pop(L, 1); + + /* Register 'call-mutex' metatable. */ + lua_pushlightuserdata(L, &call_mutex_mt); + lua_newtable(L); + lua_pushcfunction(L, call_mutex_gc); + lua_setfield(L, -2, "__gc"); + lua_rawset(L, LUA_REGISTRYINDEX); + + /* Create call mutex guard, keep it locked initially(it is unlocked only when we are calling out to GObject-C code) and store it into the registry. */ + lua_pushlightuserdata(L, &call_mutex); + mutex = lua_newuserdata(L, sizeof(*mutex)); + mutex->mutex = &mutex->state_mutex; + g_rec_mutex_init(&mutex->state_mutex); + g_rec_mutex_lock(&mutex->state_mutex); + lua_pushlightuserdata(L, &call_mutex_mt); + lua_rawget(L, LUA_REGISTRYINDEX); + lua_setmetatable(L, -2); + lua_rawset(L, LUA_REGISTRYINDEX); + + /* Register 'lua_gobject.core' interface. */ + lua_newtable(L); + luaL_register(L, NULL, lua_gobject_reg); + + /* Add the state ID */ + state_id = g_atomic_int_add(&global_state_id, 1); + if (state_id == 0) + lua_pushliteral(L, ""); + else + lua_pushfstring(L, "+L%d", state_id); + lua_setfield(L, -2, "id"); + + /* Add lock and enter/leave locking functions. */ + lua_pushlightuserdata(L, lua_gobject_state_get_lock(L)); + lua_setfield(L, -2, "lock"); + lua_pushlightuserdata(L, lua_gobject_state_enter); + lua_setfield(L, -2, "enter"); + lua_pushlightuserdata(L, lua_gobject_state_leave); + lua_setfield(L, -2, "leave"); + + /* Create repo and index table. */ + create_repo_table(L, "index", &repo_index); + create_repo_table(L, "repo", &repo); + + /* Initialize modules. */ + lua_gobject_buffer_init(L); + lua_gobject_gi_init(L); + lua_gobject_marshal_init(L); + lua_gobject_record_init(L); + lua_gobject_object_init(L); + lua_gobject_callable_init(L); + + /* Return registration table. */ + return 1; } diff --git a/LuaGObject/gi.c b/LuaGObject/gi.c index 4ef6b71b..f9e13505 100644 --- a/LuaGObject/gi.c +++ b/LuaGObject/gi.c @@ -93,7 +93,7 @@ infos_index(lua_State *L) for (n = 0; n < infos->count; n++) { GIBaseInfo *info = infos->item_get(infos->info, n); if (strcmp(gi_base_info_get_name(info), name) == 0) - return lua_gobject_gi_info_new(L, info); + return lua_gobject_gi_info_new(L, info); gi_base_info_unref(info); } diff --git a/LuaGObject/override/GObject-Object.lua b/LuaGObject/override/GObject-Object.lua index 22c88abe..86f63899 100644 --- a/LuaGObject/override/GObject-Object.lua +++ b/LuaGObject/override/GObject-Object.lua @@ -1,15 +1,9 @@ ------------------------------------------------------------------------------- --- --- LuaGObject GObject.Object handling. --- --- Copyright (c) 2010-2014 Pavel Holejsovsky --- Licensed under the MIT license: --- http://www.opensource.org/licenses/mit-license.php --- ------------------------------------------------------------------------------- +-- LuaGObject GObject.Object handling. +-- Copyright (c) 2010-2014 Pavel Holejsovsky +-- Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php local pairs, select, setmetatable, error, type - = pairs, select, setmetatable, error, type + = pairs, select, setmetatable, error, type local core = require 'LuaGObject.core' local gi = core.gi @@ -27,163 +21,153 @@ local Object = repo.GObject.Object -- Add overrides for GObject.TypeClass TypeClass._free = gi.GObject.resolve.g_type_class_unref local type_class_ref = core.callable.new { - addr = gi.GObject.resolve.g_type_class_ref, - ret = ti.ptr, ti.GType + addr = gi.GObject.resolve.g_type_class_ref, + ret = ti.ptr, ti.GType } function TypeClass:_new() - local ptr = type_class_ref(self._gtype) - return core.record.new(self, ptr, true) + local ptr = type_class_ref(self._gtype) + return core.record.new(self, ptr, true) end --- Object constructor, 'param' contains table _with properties/signals --- to initialize. +-- Object constructor, 'param' contains table _with properties/signals to initialize. local parameter_repo = repo.GObject.Parameter --- Before GLib 2.54, g_object_newv() was annotated with [rename-to g_object_new]. --- Starting from GLib 2.54, g_object_new_with_properties() has this annotation. --- We always want g_object_newv(). +-- Before GLib 2.54, g_object_newv() was annotated with [rename-to g_object_new]. Starting from GLib 2.54, g_object_new_with_properties() has this annotation. We always want g_object_newv(). local object_new = gi.GObject.Object.methods.newv or gi.GObject.Object.methods.new if object_new then - object_new = core.callable.new(object_new) + object_new = core.callable.new(object_new) else - -- Unfortunately, older GI (<1.30) does not export g_object_newv() - -- in the typelib, so we have to workaround here with manually - -- implemented C version. - object_new = core.object.new + -- Unfortunately, older GI (<1.30) does not export g_object_newv() in the typelib, so we have to workaround here with manually implemented C version. + object_new = core.object.new end -- Generic construction method. function Object:_construct(gtype, param, owns) - local object - if type(param) == 'userdata' then - -- Wrap existing GObject instance in the LuaGObject proxy. - object = core.object.new(param, owns) - gtype = object._gtype - end - - -- Check that gtype fits. - if not Type.is_a(gtype, self._gtype) then - error(("`%s' is not subtype of `%s'"):format( - Type.name(gtype), self._name), 3) - end - - -- In 'wrap' mode, just return the created object. - if object then return object end - - -- Process 'args' table, separate properties from other fields. - local parameters, others, safe = {}, {}, {} - for name, arg in pairs(param or {}) do - if type(name) == 'string' then - local argtype = self[name] - if gi.isinfo(argtype) and argtype.is_property then - local parameter = core.record.new(parameter_repo) - local atname = argtype.name - local value = parameter.value - - -- Store the name string in some safe Lua place ('safe' - -- table), because param is GParameter, which contains - -- only non-owning pointer to the string, and it could be - -- Lua-GC'ed while still referenced by GParameter - -- instance. - safe[#safe + 1] = atname - safe[#safe + 1] = value - - parameter.name = atname - local gtype = Type.from_typeinfo(argtype.typeinfo) - Value.init(value, gtype) - local marshaller = Value.find_marshaller(gtype, argtype.typeinfo) - marshaller(value, nil, arg) - parameters[#parameters + 1] = parameter - else - others[name] = arg - end - end - end - - -- Create the object. - object = object_new(gtype, parameters) - - -- Perform initialization on interfaces. - if next(self._implements) then - local inited - for _, initname in ipairs { '_init1', '_init2' } do - for _, interface in pairs(self._implements) do - local init = interface[initname] - if init then - local ok, err = init(object) - if not ok then return nil, err end - if ok == '_initskip' then - -- This initializer does not apply, continue looking - -- for others. - else - inited = true - break; - end - end - end - if inited then break end - end - end - - -- Attach arguments previously filtered out from creation. - for name, value in pairs(others) do - if type(name) == 'string' then object[name] = value end - end - - -- In case that type has _container_add() method, use it to process - -- array part of the args. - local add = self._container_add - if add and param then - for i = 1, #param do add(object, param[i]) end - end - return object + local object + if type(param) == 'userdata' then + -- Wrap existing GObject instance in the LuaGObject proxy. + object = core.object.new(param, owns) + gtype = object._gtype + end + + -- Check that gtype fits. + if not Type.is_a(gtype, self._gtype) then + error(("`%s' is not subtype of `%s'"):format( + Type.name(gtype), self._name), 3) + end + + -- In 'wrap' mode, just return the created object. + if object then return object end + + -- Process 'args' table, separate properties from other fields. + local parameters, others, safe = {}, {}, {} + for name, arg in pairs(param or {}) do + if type(name) == 'string' then + local argtype = self[name] + if gi.isinfo(argtype) and argtype.is_property then + local parameter = core.record.new(parameter_repo) + local atname = argtype.name + local value = parameter.value + + -- Store the name string in some safe Lua place ('safe' table), because param is GParameter, which contains only non-owning pointer to the string, and it could be Lua-GC'ed while still referenced by GParameter instance. + safe[#safe + 1] = atname + safe[#safe + 1] = value + + parameter.name = atname + local gtype = Type.from_typeinfo(argtype.typeinfo) + Value.init(value, gtype) + local marshaller = Value.find_marshaller(gtype, argtype.typeinfo) + marshaller(value, nil, arg) + parameters[#parameters + 1] = parameter + else + others[name] = arg + end + end + end + + -- Create the object. + object = object_new(gtype, parameters) + + -- Perform initialization on interfaces. + if next(self._implements) then + local inited + for _, initname in ipairs { '_init1', '_init2' } do + for _, interface in pairs(self._implements) do + local init = interface[initname] + if init then + local ok, err = init(object) + if not ok then return nil, err end + if ok == '_initskip' then + -- This initializer does not apply, continue looking + -- for others. + else + inited = true + break; + end + end + end + if inited then break end + end + end + + -- Attach arguments previously filtered out from creation. + for name, value in pairs(others) do + if type(name) == 'string' then object[name] = value end + end + + -- In case that type has _container_add() method, use it to process + -- array part of the args. + local add = self._container_add + if add and param then + for i = 1, #param do add(object, param[i]) end + end + return object end function Object:_new(...) - -- Invoke object's construct method which does the work. - return self:_construct(self._gtype, ...) + -- Invoke object's construct method which does the work. + return self:_construct(self._gtype, ...) end --- Override normal 'new' constructor, to allow creating objects with --- specified GType. +-- Override normal 'new' constructor, to allow creating objects with specified GType. function Object.new(gtype, params, owns) - -- Find proper repo instance for gtype. - local gtype_walker = gtype - while true do - local self = core.repotype(gtype_walker) - if self then - -- We have repo instance, use it to construct the object. - return self:_construct(gtype, params, owns) - end - gtype_walker = Type.parent(gtype_walker) - if not gtype_walker then - error(("`%s': cannot create object, type not found"):format(gtype), 2) - end - end + -- Find proper repo instance for gtype. + local gtype_walker = gtype + while true do + local self = core.repotype(gtype_walker) + if self then + -- We have repo instance, use it to construct the object. + return self:_construct(gtype, params, owns) + end + gtype_walker = Type.parent(gtype_walker) + if not gtype_walker then + error(("`%s': cannot create object, type not found"):format(gtype), 2) + end + end end -- Prepare callbacks for get_property and set_property local get_property_guard, get_property_addr = core.marshal.callback( - gi.GObject.ObjectClass.fields.get_property.typeinfo.interface, - function(self, prop_id, value, pspec) - local name = pspec.name:gsub('%-', '_') - local prop_get = core.object.query(self, 'repo')._property_get[name] - if prop_get then - value.value = prop_get(self) - else - value.value = self.priv[name] - end + gi.GObject.ObjectClass.fields.get_property.typeinfo.interface, + function(self, prop_id, value, pspec) + local name = pspec.name:gsub('%-', '_') + local prop_get = core.object.query(self, 'repo')._property_get[name] + if prop_get then + value.value = prop_get(self) + else + value.value = self.priv[name] + end end) local set_property_guard, set_property_addr = core.marshal.callback( - gi.GObject.ObjectClass.fields.get_property.typeinfo.interface, - function(self, prop_id, value, pspec) - local name = pspec.name:gsub('%-', '_') - local prop_set = core.object.query(self, 'repo')._property_set[name] - if prop_set then - prop_set(self, value.value) - else - self.priv[name] = value.value - end + gi.GObject.ObjectClass.fields.get_property.typeinfo.interface, + function(self, prop_id, value, pspec) + local name = pspec.name:gsub('%-', '_') + local prop_set = core.object.query(self, 'repo')._property_set[name] + if prop_set then + prop_set(self, value.value) + else + self.priv[name] = value.value + end end) if not core.guards then core.guards = {} end @@ -191,102 +175,101 @@ core.guards.get_property = get_property_guard core.guards.set_property = set_property_guard -- _class_init method on the Object will install all properties --- accumulated in _property table. It will be called automatically on +-- accumulated in _property table. It will be called automatically on -- derived classes during class initialization routine. function Object:_class_init(class) - if next(self._property) then - -- First install get/set_property overrides, unless already present. - if not self._override.get_property then - class.get_property = get_property_addr - end - if not self._override.set_property then - class.set_property = set_property_addr - end - - -- Install properties. - local prop_id = 0 - for name, pspec in pairs(self._property) do - prop_id = prop_id + 1 - class:install_property(prop_id, pspec) - end - end + if next(self._property) then + -- First install get/set_property overrides, unless already present. + if not self._override.get_property then + class.get_property = get_property_addr + end + if not self._override.set_property then + class.set_property = set_property_addr + end + + -- Install properties. + local prop_id = 0 + for name, pspec in pairs(self._property) do + prop_id = prop_id + 1 + class:install_property(prop_id, pspec) + end + end end --- Initially unowned creation is similar to normal GObject creation, --- but we have to ref_sink newly created object. +-- Initially unowned creation is similar to normal GObject creation, but we have to ref_sink newly created object. local InitiallyUnowned = repo.GObject.InitiallyUnowned function InitiallyUnowned:_construct(...) - local object = Object._construct(self, ...) - return Object.ref_sink(object) + local object = Object._construct(self, ...) + return Object.ref_sink(object) end -- Reading 'class' yields real instance of the object class. Object._attribute = { _type = {} } function Object._attribute._type:get() - return core.object.query(self, 'repo') + return core.object.query(self, 'repo') end -- Custom _element implementation, checks dynamically inherited -- interfaces and dynamic properties. local inherited_element = Object._element function Object:_element(object, name) - local element, category = inherited_element(self, object, name) - if element then return element, category end - - -- Everything else works only if we have object instance. - if not object then return nil end - - -- List all interfaces implemented by this object and try whether - -- they can handle specified _element request. - local interfaces = Type.interfaces(object._gtype) - for i = 1, #interfaces do - local info = gi[core.gtype(interfaces[i])] - local iface = info and repo[info.namespace][info.name] - if iface then element, category = iface:_element(object, name, self) end - if element then return element, category end - end - - -- Element not found in the repo (typelib), try whether dynamic - -- property of the specified name exists. - local property = Object._class.find_property( - object._class, name:gsub('_', '-')) - if property then return property, '_property' end + local element, category = inherited_element(self, object, name) + if element then return element, category end + + -- Everything else works only if we have object instance. + if not object then return nil end + + -- List all interfaces implemented by this object and try whether + -- they can handle specified _element request. + local interfaces = Type.interfaces(object._gtype) + for i = 1, #interfaces do + local info = gi[core.gtype(interfaces[i])] + local iface = info and repo[info.namespace][info.name] + if iface then element, category = iface:_element(object, name, self) end + if element then return element, category end + end + + -- Element not found in the repo (typelib), try whether dynamic + -- property of the specified name exists. + local property = Object._class.find_property( + object._class, name:gsub('_', '-')) + if property then return property, '_property' end end -- Sets/gets property using specified marshaller attributes. local function marshal_property(obj, name, flags, gtype, marshaller, ...) - -- Check access rights of the property. - local mode = select('#', ...) > 0 and 'WRITABLE' or 'READABLE' - if not flags[mode] then - error(("%s: `%s' not %s"):format(core.object.query(obj, 'repo')._name, - name, core.downcase(mode))) - end - local value = Value(gtype) - if mode == 'WRITABLE' then - marshaller(value, nil, ...) - Object.set_property(obj, name, value) - else - Object.get_property(obj, name, value) - return marshaller(value) - end + -- Check access rights of the property. + local mode = select('#', ...) > 0 and 'WRITABLE' or 'READABLE' + if not flags[mode] then + error(("%s: `%s' not %s"):format(core.object.query(obj, 'repo')._name, + name, core.downcase(mode))) + end + local value = Value(gtype) + if mode == 'WRITABLE' then + marshaller(value, nil, ...) + Object.set_property(obj, name, value) + else + Object.get_property(obj, name, value) + return marshaller(value) + end end -- Property accessor. function Object:_access_property(object, prop, ...) - if gi.isinfo(prop) then - -- GI-based property - local typeinfo = prop.typeinfo - local gtype = Type.from_typeinfo(typeinfo) - local marshaller = Value.find_marshaller(gtype, typeinfo, - prop.transfer) - return marshal_property(object, prop.name, - repo.GObject.ParamFlags[prop.flags], - gtype, marshaller, ...) - else - -- pspec-based property - return marshal_property(object, prop.name, prop.flags, prop.value_type, - Value.find_marshaller(prop.value_type), ...) - end + if gi.isinfo(prop) then + -- GI-based property + local typeinfo = prop.typeinfo + local gtype = Type.from_typeinfo(typeinfo) + local marshaller = Value.find_marshaller(gtype, typeinfo, + prop.transfer) + return marshal_property(object, prop.name, + repo.GObject.ParamFlags[prop.flags], + gtype, marshaller, ...) + else + -- pspec-based property + return marshal_property(object, prop.name, prop.flags, prop.value_type, + Value.find_marshaller(prop.value_type), ...) + end end local quark_from_string = repo.GLib.quark_from_string @@ -295,82 +278,85 @@ local signal_connect_closure_by_id = repo.GObject.signal_connect_closure_by_id local signal_emitv = repo.GObject.signal_emitv -- Connects signal to specified object instance. local function connect_signal(obj, gtype, name, closure, detail, after) - return signal_connect_closure_by_id( - obj, signal_lookup(name, gtype), - detail and quark_from_string(detail) or 0, - closure, after or false) + return signal_connect_closure_by_id( + obj, signal_lookup(name, gtype), + detail and quark_from_string(detail) or 0, + closure, after or false) end -- Emits signal on specified object instance. local function emit_signal(obj, gtype, info, detail, ...) - -- Compile callable info. - local call_info = Closure.CallInfo.new(info) + -- Compile callable info. + local call_info = Closure.CallInfo.new(info) - -- Marshal input arguments. - local retval, params, marshalling_params = call_info:pre_call(obj, ...) + -- Marshal input arguments. + local retval, params, marshalling_params = call_info:pre_call(obj, ...) - -- Invoke the signal. - signal_emitv(params, signal_lookup(info.name, gtype), + -- Invoke the signal. + signal_emitv(params, signal_lookup(info.name, gtype), detail and quark_from_string(detail) or 0, retval) - -- Unmarshal results. - return call_info:post_call(params, retval, marshalling_params) + -- Unmarshal results. + return call_info:post_call(params, retval, marshalling_params) end -- Signal accessor. function Object:_access_signal(object, info, ...) - local gtype = self._gtype - if select('#', ...) > 0 then - -- Assignment means 'connect signal without detail'. - connect_signal(object, gtype, info.name, Closure((...), info)) - else - -- Reading yields table with signal operations. - local mt = {} - local pad = setmetatable({}, mt) - function pad:connect(target, detail, after) - return connect_signal(object, gtype, info.name, - Closure(target, info), detail, after) - end - function pad:emit(...) - return emit_signal(object, gtype, info, nil, ...) - end - function mt:__call(_, ...) - return emit_signal(object, gtype, info, nil, ...) - end - - -- If signal supports details, add metatable implementing - -- __newindex for connecting in the 'on_signal['detail'] = - -- handler' form. - if not info.is_signal or info.flags.detailed then - function pad:emit(detail, ...) - return emit_signal(object, gtype, info, detail, ...) - end - function mt:__newindex(detail, target) - connect_signal(object, gtype, info.name, Closure(target, info), - detail) - end - end - - -- Return created signal pad. - return pad - end + local gtype = self._gtype + if select('#', ...) > 0 then + -- Assignment means 'connect signal without detail'. + connect_signal(object, gtype, info.name, Closure((...), info)) + else + -- Reading yields table with signal operations. + local mt = {} + local pad = setmetatable({}, mt) + function pad:connect(target, detail, after) + return connect_signal(object, gtype, info.name, + Closure(target, info), detail, after) + end + function pad:emit(...) + return emit_signal(object, gtype, info, nil, ...) + end + function mt:__call(_, ...) + return emit_signal(object, gtype, info, nil, ...) + end + + -- If signal supports details, add metatable implementing __newindex for connecting in the 'on_signal['detail'] = handler' form. + if not info.is_signal or info.flags.detailed then + function pad:emit(detail, ...) + return emit_signal(object, gtype, info, detail, ...) + end + function mt:__newindex(detail, target) + connect_signal(object, gtype, info.name, Closure(target, info), + detail) + end + end + + -- Return created signal pad. + return pad + end end --- GOI<1.30 does not export 'Object.on_notify' signal from the --- typelib. Work-around this problem by implementing custom on_notify --- attribute. +-- For certain libraries, type info is incomplete and signals are not found by introspection. In these cases, it's useful to provide a good helper method +function Object:connect_signal(name, callback, detail, after) + local info = gi[core.gtype(self._gtype)] + local closure = Closure((callback), info) + return signal_connect_closure_by_id( + self, signal_lookup(name, self._gtype), + detail and quark_from_string(detail) or 0, closure, after or false) +end + +-- GOI<1.30 does not export 'Object.on_notify' signal from the typelib. Work-around this problem by implementing custom on_notify attribute. if not gi.GObject.Object.signals.notify then - local notify_info = gi.GObject.ObjectClass.fields.notify.typeinfo.interface - function Object._attribute.on_notify(object, ...) - local repotable = core.object.query(object, 'repo') - return Object._access_signal(repotable, object, notify_info, ...) - end + local notify_info = gi.GObject.ObjectClass.fields.notify.typeinfo.interface + function Object._attribute.on_notify(object, ...) + local repotable = core.object.query(object, 'repo') + return Object._access_signal(repotable, object, notify_info, ...) + end end --- Bind property implementation. For some strange reason, GoI<1.30 --- exports it only on GInitiallyUnowned and not on GObject. Oh --- well... +-- Bind property implementation. For some strange reason, GoI<1.30 exports it only on GInitiallyUnowned and not on GObject. Oh well... for _, name in pairs { 'bind_property', 'bind_property_full' } do - if not Object[name] then - Object._method[name] = InitiallyUnowned[name] - end + if not Object[name] then + Object._method[name] = InitiallyUnowned[name] + end end diff --git a/docs/Manual.md b/docs/Manual.md index 6b49f196..0d7fc64b 100644 --- a/docs/Manual.md +++ b/docs/Manual.md @@ -173,6 +173,17 @@ Both forms of signal connection will connect the handler to be called before the end window.on_notify:connect(notify_handler, "is-active", false) +Certain libraries do not export complete typelibs. As a consequence, some signals become unintrospectable and these conveniences become unavailable. In cases where LuaGObject fails to discover a signal, a method named `:connect_signal()` is provided to connect manually using only GObject machinery. For instance, to connect to the `::about-to-finish` signal on GStreamer's unintrospectable PlayBin3, + + local player = Gst.ElementFactory.make("playbin3", "player) + player:connect_signal("about-to-finish", function() + -- Play next_track seamlessly. + player:set_property("uri", next_track) + player:set_state "PLAYING" + end) + +The `:connect_signal()` method also optionally takes a `detail` as a third argument for signals which support them (similarly to `::on_notify`), and a fourth argument `after` which if true will execute the given handler function after other signal handlers. + #### 3.4.2 Emitting Signals Emitting an existing signal is usually only necessary in the implementation of a subclass. The simplest method to do so is to "call" the signal on the object instance as if it where a function. For instance, for an object `window` which subclasses `Gtk.Window`, to emit the parent class' `destroy` signal one can write: