Permalink
Browse files

Rework enums and bitflags handling

They are now primarily trwated as string or sets of strings, instead of numbers.
  • Loading branch information...
1 parent 7d40c1c commit d19244b4e403f9386d7f9576e89086ea4c1cce3b @pavouk committed Dec 27, 2011
Showing with 205 additions and 58 deletions.
  1. +7 −0 README.md
  2. +56 −5 docs/guide.md
  3. +75 −14 lgi/enum.lua
  4. +0 −6 lgi/init.lua
  5. +20 −1 lgi/marshal.c
  6. +2 −3 lgi/override/Clutter.lua
  7. +3 −2 lgi/override/GObject-Object.lua
  8. +4 −5 samples/console.lua
  9. +38 −22 tests/gireg.lua
View
@@ -43,7 +43,14 @@ markdown processor if you want to read it in HTML.
### 0.4 (unreleased)
+- Changed handling of enums and bitflags, switched from marshaling
+ them as numbers to prefering strings for enums and tables (sets or
+ lists) for bitflags. Numeric values still work for Lua->C
+ marshalling, but backward compatibility is broken in C->Lua enum and
+ bitflags marshalling.
- Compatible with Lua 5.2 and LuaJIT
+- Added standardized way for overrides to handle constructor argument
+ table array part.
- Existing Gtk overrides reworked and improved, there is now a way to
describe and create widget hierarchies in Lua-friendly way. See
`docs/gtk.lua`, chapter about `Gtk.Container` for overview and
View
@@ -61,6 +61,11 @@ mapping between GLib types and Lua types is established.
* `gboolean` is mapped to Lua's `boolean` type, with `true` and
`false` values
* All numeric types are mapped to Lua's `number` type
+* Enumerations are primarily handled as strings with uppercased GType
+ nicks, optionally the direct numeric values are also accepted.
+* Bitflags are primarily handled as lists or sets of strings with
+ uppercased GType nicks, optionally the direct numeric values are
+ also accepted.
* `gchar*` string is mapped to Lua as `string` type, UTF-8 encoded
* C array types and `GArray` is mapped to Lua tables, using array part
of the table. Note that although in C the arrays are 0-based, when
@@ -440,11 +445,22 @@ Fields are accessed using `.` operator on structure instance, for example
## 5. Enums and bitflags, constants
-Enum instances are represented as plain numbers in LGI. So in any
-place where enum or bitflags instance is needed, a number can be used
-directly instead.
+LGI primarily maps enumerations to strings containing uppercased nicks
+of enumeration constant names. Optionally, a direct enumeration value
+is also accepted. Similarly, bitflags are primarily handled as sets
+containing uppercased flag nicks, but also lists of these nicks or
+direct numeric value is accepted. When a numeric value cannot be
+mapped cleanly to the known set of bitflags, the remaining number is
+stored in the first array slot of the returned set.
-### 5.1. Accessing values
+Note that this behavior changed in lgi 0.4; up to that alpha release,
+lgi handled enums and bitmaps exclusively as numbers only. The change
+is compatible in Lua->C direction, where numbers still can be used,
+but incompatible in C->Lua direction, where lgi used to return
+numbers, while now it returns either string with enum value or table
+with flags.
+
+### 5.1. Accessing numeric values
In order to retrieve real enum values from symbolic names, enum and
bitflags are loaded into repository as tables mapping symbolic names
@@ -459,7 +475,8 @@ yields following output:
};
so constants can be referenced using `Gtk.WindowType.TOPLEVEL`
-construct.
+construct, or directly using string `'TOPLEVEL'` when a
+`Gtk.WindowType` is expected.
### 5.2. Backward mapping, getting names from numeric values
@@ -493,6 +510,40 @@ all symbolic names which make up the requested value:
SORTED = 32;
ODD = 2;
};
+
+This way, it is possible to check for presence of specified flag very
+easily:
+
+ if Gtk.RegionFlags[flags].ODD then
+ -- Code handling region-odd case
+ endif
+
+If the value cannot be cleanly decomposed to known flags, remaining
+bits are accumulated into number stored at index 1:
+
+> dump(Gtk.RegionFlags[51])
+
+ ["table: 0x242fb20"] = { -- table: 0x242fb20
+ EVEN = 1;
+ SORTED = 32;
+ [1] = 16;
+ ODD = 2;
+ };
+
+To construct numeric value which can be passed to a function expecting
+an enum, it is possible to simply add requested flags. However, there
+is a danger if some definition contains multiple flags , in which case
+numeric adding produces incorrect results. Therefore, it is possible
+to use bitflags pseudoconstructor', which accepts table containing
+requested flags:
+
+> =Gtk.RegionFlags { 'FIRST', 'SORTED' }
+
+ 36
+
+> =Gtk.RegionFlags { Gtk.RegionFlags.ODD, 16, 'EVEN' }
+
+ 19
## 6. Threading and synchronization
View
@@ -13,6 +13,37 @@ local core = require 'lgi.core'
local gi = core.gi
local component = require 'lgi.component'
+-- Prepare needed bit operations. Prefer bit32 C module if available,
+-- but if it is not, use poor-man Lua-only variants.
+local bor, has_bit
+local ok, bitlib = pcall(require, 'bit32')
+if ok then
+ -- Lua 5.2 style bit operations.
+ bor, has_bit = bitlib.bor, bitlib.btest
+else
+ ok, bitlib = pcall(require, 'bit')
+ if ok then
+ -- LuaBitOp package.
+ bor, has_bit = bitlib.bor, bitlib.band
+ else
+ -- Poor-man's Lua-only implementation, slow but out-of-the-box
+ -- for any kind of Lua.
+ function has_bit(value, bitmask)
+ return value % (2 * bitmask) >= bitmask
+ end
+ local function bor(o1, o2)
+ local res, bit = 0, 1
+ while bit <= o1 or bit <= o2 do
+ if has_bit(o1, bit) or has_bit(o2, bit) then
+ res = res + bit
+ end
+ bit = bit * 2
+ end
+ return res
+ end
+ end
+end
+
local enum = {
enum_mt = component.mt:clone { '_method' },
bitflags_mt = component.mt:clone { '_method' }
@@ -29,8 +60,9 @@ function enum.load(info, meta)
local prefix = info.name:gsub('%u+[^%u]+', '%1_'):lower()
local namespace = core.repo[info.namespace]
enum_type._method = setmetatable(
- {},
- { __index = function(_, name) return namespace[prefix .. name] end })
+ {}, { __index = function(_, name)
+ return namespace[prefix .. name]
+ end })
end
-- Load all enum values.
@@ -47,26 +79,55 @@ end
-- Enum reverse mapping, value->name.
function enum.enum_mt:_element(instance, value)
- local element, category = component.mt._element(self, instance, value)
- if element then return element, category end
- for name, val in pairs(self) do
- if val == value then return name end
+ if type(value) == 'number' then
+ for name, val in pairs(self) do
+ if val == value then return name end
+ end
+ return value
+ else
+ return component.mt._element(self, instance, value)
end
end
+-- Constructs enum number from specified string.
+function enum.enum_mt:_new(param)
+ if type(param) == 'string' then param = self[param] end
+ return param
+end
+
-- Resolving arbitrary number to the table containing symbolic names
-- of contained bits.
function enum.bitflags_mt:_element(instance, value)
- local element, category = component.mt._element(self, instance, value)
- if element then return element, category end
- if type(value) ~= 'number' then return end
- local result = {}
- for name, flag in pairs(self) do
- if type(flag) == 'number' and core.has_bit(value, flag) then
- result[name] = flag
+ if type(value) == 'number' then
+ local result, remainder = {}, value
+ for name, flag in pairs(self) do
+ if type(flag) == 'number' and has_bit(value, flag) then
+ result[name] = true
+ remainder = remainder - flag
+ end
+ end
+ if remainder > 0 then result[1] = remainder end
+ return result
+ else
+ return component.mt._element(self, instance, value)
+ end
+end
+
+-- 'Constructs' number from specified flags (or accepts just number).
+function enum.bitflags_mt:_new(param)
+ if type(param) == 'string' then
+ return self[param]
+ elseif type(param) == 'number' then
+ return param
+ else
+ local num = 0
+ for key, value in pairs(param) do
+ if type(key) == 'string' then value = key end
+ if type(value) == 'string' then value = self[value] end
+ num = bor(num, value)
end
+ return num
end
- return result
end
return enum
View
@@ -19,12 +19,6 @@ local core = require 'lgi.core'
-- Create lgi table, containing the module.
local lgi = { _NAME = 'lgi', _VERSION = require 'lgi.version' }
--- Add simple flag-checking function, avoid compatibility hassle with
--- importing bitlib just because of this simple operation.
-function core.has_bit(value, flag)
- return value % (2 * flag) >= flag
-end
-
-- Forward 'yield' functionality into external interface.
lgi.yield = core.yield
View
@@ -826,6 +826,16 @@ lgi_marshal_2c (lua_State *L, GITypeInfo *ti, GIArgInfo *ai,
{
case GI_INFO_TYPE_ENUM:
case GI_INFO_TYPE_FLAGS:
+ /* If the argument is not numeric, convert to number
+ first. Use enum/flags 'constructor' to do this. */
+ if (lua_type (L, narg) != LUA_TNUMBER)
+ {
+ lgi_type_get_repotype (L, G_TYPE_INVALID, info);
+ lua_pushvalue (L, narg);
+ lua_call (L, 1, 1);
+ narg = -1;
+ }
+
/* Directly store underlying value. */
marshal_2c_int (L, g_enum_info_get_storage_type (info), arg, narg,
optional, FALSE);
@@ -1094,9 +1104,18 @@ lgi_marshal_2lua (lua_State *L, GITypeInfo *ti, GITransfer transfer,
{
case GI_INFO_TYPE_ENUM:
case GI_INFO_TYPE_FLAGS:
- /* Directly store underlying value. */
+ /* Prepare repotable of enum/flags on the stack. */
+ lgi_type_get_repotype (L, G_TYPE_INVALID, info);
+
+ /* Unmarshal the numeric value. */
marshal_2lua_int (L, g_enum_info_get_storage_type (info),
arg, FALSE);
+
+ /* Get symbolic value from the table. */
+ lua_gettable (L, -2);
+
+ /* Remove the table from the stack. */
+ lua_remove (L, -2);
break;
case GI_INFO_TYPE_STRUCT:
View
@@ -40,7 +40,6 @@ Clutter.threads_init()
-- Automatically initialize clutter, avoid continuing if
-- initialization fails.
local status = Clutter.init()
-if status ~= Clutter.InitError.SUCCESS then
- error(("Clutter initialization failed: %s"):format(
- Clutter.InitError(status)))
+if status ~= 'SUCCESS' then
+ error(("Clutter initialization failed: %s"):format(status))
end
@@ -128,7 +128,7 @@ end
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 core.has_bit(flags, repo.GObject.ParamFlags[mode]) then
+ if not flags[mode] then
error(("%s: `%s' not %s"):format(core.object.query(obj, 'repo')._name,
name, mode:lower()))
end
@@ -147,7 +147,8 @@ function Object:_access_property(object, property, ...)
local typeinfo = property.typeinfo
local gtype = Type.from_typeinfo(typeinfo)
local marshaller = Value.find_marshaller(gtype, typeinfo, property.transfer)
- return marshal_property(object, property.name, property.flags,
+ return marshal_property(object, property.name,
+ repo.GObject.ParamFlags[property.flags],
gtype, marshaller, ...)
end
View
@@ -283,11 +283,10 @@ local function Console()
function entry:on_key_press_event(event)
-- Lookup action to be activated for specified key combination.
local action = keytable[event.keyval]
- local mask = Gdk.ModifierType[event.state]
- local wants_control = actions.multiline.active
- and Gdk.ModifierType.CONTROL_MASK or nil
- if not action or mask.SHIFT_MASK
- or mask.CONTROL_MASK ~= wants_control then
+ local state = event.state
+ local without_control = not state.CONTROL_MASK
+ if not action or state.SHIFT_MASK
+ or actions.multiline.active == without_control then
return false
end
Oops, something went wrong.

0 comments on commit d19244b

Please sign in to comment.