1
------------------------------------------------------------------------------
3
-- LGI GObject.Value support.
5
-- Copyright (c) 2010, 2011 Pavel Holejsovsky
6
-- Licensed under the MIT license:
7
-- http://www.opensource.org/licenses/mit-license.php
9
------------------------------------------------------------------------------
11
local assert, pairs, select, type, tostring, error =
12
assert, pairs, select, type, tostring, error
13
local lgi = require 'lgi'
14
local core = require 'lgi.core'
15
local repo = core.repo
17
local Type = repo.GObject.Type
19
-- Value is constructible from any kind of source Lua value, and the
20
-- type of the value can be hinted by type name.
21
local Value = repo.GObject.Value
22
local value_info = gi.GObject.Value
24
local log = lgi.log.domain('Lgi')
26
-- Workaround for incorrect annotations - g_value_set_xxx are missing
27
-- (allow-none) annotations in glib < 2.30.
28
for _, name in pairs { 'set_object', 'set_variant', 'set_string' } do
29
if not value_info.methods[name].args[1].optional then
30
log.message("g_value_%s() is missing (allow-none)", name)
31
local setter = Value[name]
34
if not val then Value.reset(value) else setter(value, val) end
39
-- Do not allow direct access to fields.
40
local value_field_gtype = Value._field.g_type
43
-- 'type' property controls gtype of the property.
44
Value._attribute = { gtype = {} }
45
function Value._attribute.gtype.get(value)
46
return core.record.field(value, value_field_gtype)
48
function Value._attribute.gtype.set(value, newtype)
49
local gtype = core.record.field(value, value_field_gtype)
52
-- Try converting old value to new one.
53
local dest = core.record.new(value_info)
54
Value.init(dest, newtype)
55
if not Value.transform(value, dest) then
56
error(("GObject.Value: cannot convert `%s' to `%s'"):format(
57
gtype, core.record.field(dest, value_field_gtype)))
60
Value.init(value, newtype)
61
Value.copy(dest, value)
66
-- No value was set and some is requested, so set it.
67
Value.init(value, newtype)
71
local value_marshallers = {}
72
for name, gtype in pairs(Type) do
73
local get = Value._method['get_' .. name:lower()]
74
local set = Value._method['set_' .. name:lower()]
76
value_marshallers[gtype] =
77
function(value, params, ...)
78
return (select('#', ...) > 0 and set or get)(value, ...)
83
-- Interface marshaller is the same as object marshaller.
84
value_marshallers[Type.INTERFACE] = value_marshallers[Type.OBJECT]
86
-- Override 'boxed' marshaller, default one marshalls to gpointer
87
-- instead of target boxed type.
88
value_marshallers[Type.BOXED] =
89
function(value, params, ...)
90
local gtype = core.record.field(value, value_field_gtype)
91
if select('#', ...) > 0 then
92
Value.set_boxed(value, core.record.query((...), 'addr', gtype))
94
return core.record.new(gi[core.gtype(gtype)], Value.get_boxed(value))
98
-- Override marshallers for enums and bitmaps, marshal them as strings
99
-- or sets of string flags.
100
for name, gtype in pairs { ENUM = Type.ENUM, FLAGS = Type.FLAGS } do
101
local get = Value._method['get_' .. name:lower()]
102
local set = Value._method['set_' .. name:lower()]
103
value_marshallers[gtype] = function(value, params, ...)
105
if select('#', ...) > 0 then
107
if type(param) ~= 'number' then
108
rtype = core.repotype(core.record.field(value, value_field_gtype))
113
rtype = core.repotype(core.record.field(value, value_field_gtype))
114
return rtype[get(value)]
119
-- Create GStrv marshaller, implement it using typeinfo marshaller
120
-- with proper null-terminated-array-of-utf8 typeinfo 'stolen' from
121
-- g_shell_parse_argv().
122
value_marshallers[Type.STRV] = core.marshal.container(
123
gi.GLib.shell_parse_argv.args[3].typeinfo)
125
-- Finds marshaller closure which can marshal type described either by
126
-- gtype or typeinfo/transfer combo.
127
function Value._method.find_marshaller(gtype, typeinfo, transfer)
128
-- Check whether we can have marshaller for typeinfo, if the
129
-- typeinfo is container.
132
marshaller = core.marshal.container(typeinfo, transfer)
133
if marshaller then return marshaller end
136
-- Special case for non-gtype records.
137
if not gtype and typeinfo and typeinfo.tag == 'interface' then
138
-- Workaround for GoI < 1.30; it does not know that GLib structs are
139
-- boxed, so it does not assign them GType; moreover it incorrectly
140
-- considers GParamSpec as GType-less struct instead of the class.
141
local function marshal_record_no_gtype(value, params, ...)
142
-- Check actual gtype of the real value.
143
local gtype = core.record.field(value, value_field_gtype)
145
if Type.is_a(gtype, Type.PARAM) then
146
return value_marshallers[Type.PARAM](value, ...)
149
-- Find out proper getter/setter method for the value.
151
if Type.is_a(gtype, Type.BOXED) then
152
get, set = Value.get_boxed, Value.set_boxed
154
get, set = Value.get_pointer, Value.set_pointer
157
-- Do GValue<->record transfer.
158
local record_info = typeinfo.interface
159
if select('#', ...) > 0 then
160
set(value, core.record.query((...), 'addr', record_info))
162
return core.record.new(record_info, get(value))
165
return marshal_record_no_gtype
169
if type(gt) == 'number' then gt = Type.name(gt) end
171
-- Special marshaller, allowing only 'nil'.
172
if not gt then return function() end end
174
-- Find marshaller according to gtype of the value.
176
-- Check simple and/or fundamental marshallers.
177
marshaller = value_marshallers[gt] or core.marshal.fundamental(gt)
178
if marshaller then return marshaller end
181
error(("GValue marshaller for `%s' not found"):format(tostring(gtype)))
184
-- Value 'value' property provides access to GValue's embedded data.
185
function Value._attribute:value(...)
186
local marshaller = Value._method.find_marshaller(
187
core.record.field(self, value_field_gtype))
188
return marshaller(self, nil, ...)
191
-- Implement custom 'constructor', taking optionally two values (type
192
-- and value). The reason why it is overriden is that the order of
193
-- initialization is important, and standard record intializer cannot
194
-- enforce the order.
195
function Value:_new(gtype, value)
196
local v = core.record.new(value_info)
197
if gtype then v.gtype = gtype end
198
if value then v.value = value end