1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
|
------------------------------------------------------------------------------
--
-- LGI GObject.Value support.
--
-- Copyright (c) 2010, 2011 Pavel Holejsovsky
-- Licensed under the MIT license:
-- http://www.opensource.org/licenses/mit-license.php
--
------------------------------------------------------------------------------
local assert, pairs, select, type, tostring, error =
assert, pairs, select, type, tostring, error
local lgi = require 'lgi'
local core = require 'lgi.core'
local repo = core.repo
local ffi = require 'lgi.ffi'
local gi = core.gi
local Type = repo.GObject.Type
-- Value is constructible from any kind of source Lua value, and the
-- type of the value can be hinted by type name.
local Value = repo.GObject.Value
local log = lgi.log.domain('Lgi')
-- Workaround for incorrect annotations - g_value_set_xxx are missing
-- (allow-none) annotations in glib < 2.30.
for _, name in pairs { 'set_object', 'set_variant', 'set_string' } do
if not gi.GObject.Value.methods[name].args[1].optional then
log.message("g_value_%s() is missing (allow-none)", name)
local setter = Value[name]
Value._method[name] =
function(value, val)
if not val then Value.reset(value) else setter(value, val) end
end
end
end
-- Workaround for incorrect annotations - g_value_get_variant is missing
-- (transfer-none).
local _ = Value.get_variant
Value.get_variant = core.callable.new {
addr = gi.GObject.resolve.g_value_get_variant,
name = 'GObject.Value.get_variant',
ret = repo.GLib.Variant, Value,
}
-- Do not allow direct access to fields.
local value_field_gtype = Value._field.g_type
Value._field = nil
-- Register _uninit function, to avoid memory leaks from values which
-- are inline-allocated by core.record.new.
Value._uninit = core.record.value_unset
Value._copy = core.record.value_copy
-- 'type' property controls gtype of the property.
Value._attribute = { gtype = {} }
function Value._attribute.gtype.get(value)
return core.record.field(value, value_field_gtype)
end
function Value._attribute.gtype.set(value, newtype)
local gtype = core.record.field(value, value_field_gtype)
if gtype then
if newtype then
-- Try converting old value to new one.
local dest = core.record.new(Value)
Value.init(dest, newtype)
if not Value.transform(value, dest) then
error(("GObject.Value: cannot convert `%s' to `%s'"):format(
gtype, core.record.field(dest, value_field_gtype)))
end
Value.unset(value)
Value.init(value, newtype)
Value.copy(dest, value)
else
Value.unset(value)
end
elseif newtype then
-- No value was set and some is requested, so set it.
Value.init(value, newtype)
end
end
local value_marshallers = {}
for name, gtype in pairs(Type) do
name = core.downcase(name)
local get = Value['get_' .. name]
local set = Value['set_' .. name]
if get and set then
value_marshallers[gtype] =
function(value, params, ...)
return (select('#', ...) > 0 and set or get)(value, ...)
end
end
end
-- Interface marshaller is the same as object marshaller.
value_marshallers[Type.INTERFACE] = value_marshallers[Type.OBJECT]
-- Override 'boxed' marshaller, default one marshalls to gpointer
-- instead of target boxed type.
value_marshallers[Type.BOXED] =
function(value, params, ...)
local repotype = core.repotype(core.record.field(value, value_field_gtype))
if select('#', ...) > 0 then
Value.set_boxed(value, core.record.query((...), 'addr', repotype))
else
return core.record.new(repotype, Value.get_boxed(value))
end
end
-- Override marshallers for enums and bitmaps, marshal them as strings
-- or sets of string flags.
for name, gtype in pairs { ENUM = Type.ENUM, FLAGS = Type.FLAGS } do
name = core.downcase(name)
local get = Value._method['get_' .. name]
local set = Value._method['set_' .. name]
value_marshallers[gtype] = function(value, params, ...)
local rtype
if select('#', ...) > 0 then
local param = ...
if type(param) ~= 'number' then
rtype = core.repotype(core.record.field(value, value_field_gtype))
param = rtype(param)
end
set(value, param)
else
rtype = core.repotype(core.record.field(value, value_field_gtype))
return rtype[get(value)]
end
end
end
-- Create GStrv marshaller, implement it using typeinfo marshaller
-- with proper null-terminated-array-of-utf8 typeinfo 'stolen' from
-- g_shell_parse_argv().
value_marshallers[Type.STRV] = core.marshal.container(
gi.GLib.shell_parse_argv.args[3].typeinfo)
-- Finds marshaller closure which can marshal type described either by
-- gtype or typeinfo/transfer combo.
function Value._method.find_marshaller(gtype, typeinfo, transfer)
-- Check whether we can have marshaller for typeinfo, if the
-- typeinfo is container.
local marshaller
if typeinfo then
marshaller = core.marshal.container(typeinfo, transfer)
if marshaller then return marshaller end
end
-- Special case for non-gtype records.
if not gtype and typeinfo and typeinfo.tag == 'interface' then
-- Workaround for GoI < 1.30; it does not know that GLib structs are
-- boxed, so it does not assign them GType; moreover it incorrectly
-- considers GParamSpec as GType-less struct instead of the class.
local function marshal_record_no_gtype(value, params, ...)
-- Check actual gtype of the real value.
local gtype = core.record.field(value, value_field_gtype)
if Type.is_a(gtype, Type.PARAM) then
return value_marshallers[Type.PARAM](value, ...)
end
-- Find out proper getter/setter method for the value.
local get, set
if Type.is_a(gtype, Type.BOXED) then
get, set = Value.get_boxed, Value.set_boxed
else
get, set = Value.get_pointer, Value.set_pointer
end
-- Do GValue<->record transfer.
local record_repo = core.repotype(typeinfo.interface)
if select('#', ...) > 0 then
set(value, core.record.query((...), 'addr', record_repo))
else
return core.record.new(record_repo, get(value))
end
end
return marshal_record_no_gtype
end
local gt = gtype
if type(gt) == 'userdata' then gt = Type.name(gt) end
-- Special marshaller, allowing only 'nil'.
if not gt then return function() end end
-- Find marshaller according to gtype of the value.
while gt do
-- Check simple and/or fundamental marshallers.
marshaller = value_marshallers[gt] or core.marshal.fundamental(gt)
if marshaller then return marshaller end
gt = Type.parent(gt)
end
error(("GValue marshaller for `%s' not found"):format(tostring(gtype)))
end
-- Value 'value' property provides access to GValue's embedded data.
function Value._attribute:value(...)
local marshaller = Value._method.find_marshaller(
core.record.field(self, value_field_gtype))
return marshaller(self, nil, ...)
end
-- Implement custom 'constructor', taking optionally two values (type
-- and value). The reason why it is overriden is that the order of
-- initialization is important, and standard record intializer cannot
-- enforce the order.
function Value:_new(gtype, value)
local v = core.record.new(Value)
if gtype then v.gtype = gtype end
if value then v.value = value end
return v
end
|