ruby-dbus 0.16.0 → 0.18.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/NEWS.md +160 -0
- data/README.md +3 -5
- data/Rakefile +18 -8
- data/VERSION +1 -1
- data/doc/Reference.md +106 -7
- data/examples/doc/_extract_examples +7 -0
- data/examples/gdbus/gdbus +31 -24
- data/examples/no-introspect/nm-test.rb +2 -0
- data/examples/no-introspect/tracker-test.rb +3 -1
- data/examples/rhythmbox/playpause.rb +2 -1
- data/examples/service/call_service.rb +2 -1
- data/examples/service/complex-property.rb +21 -0
- data/examples/service/service_newapi.rb +1 -1
- data/examples/simple/call_introspect.rb +1 -0
- data/examples/simple/get_id.rb +2 -1
- data/examples/simple/properties.rb +2 -0
- data/examples/utils/listnames.rb +1 -0
- data/examples/utils/notify.rb +1 -0
- data/lib/dbus/api_options.rb +9 -0
- data/lib/dbus/auth.rb +20 -15
- data/lib/dbus/bus.rb +123 -75
- data/lib/dbus/bus_name.rb +12 -8
- data/lib/dbus/core_ext/class/attribute.rb +1 -1
- data/lib/dbus/data.rb +821 -0
- data/lib/dbus/emits_changed_signal.rb +83 -0
- data/lib/dbus/error.rb +4 -2
- data/lib/dbus/introspect.rb +132 -31
- data/lib/dbus/logger.rb +3 -1
- data/lib/dbus/marshall.rb +247 -296
- data/lib/dbus/matchrule.rb +16 -16
- data/lib/dbus/message.rb +44 -37
- data/lib/dbus/message_queue.rb +16 -10
- data/lib/dbus/object.rb +358 -24
- data/lib/dbus/object_path.rb +11 -6
- data/lib/dbus/proxy_object.rb +22 -1
- data/lib/dbus/proxy_object_factory.rb +13 -7
- data/lib/dbus/proxy_object_interface.rb +63 -30
- data/lib/dbus/raw_message.rb +91 -0
- data/lib/dbus/type.rb +318 -86
- data/lib/dbus/xml.rb +32 -17
- data/lib/dbus.rb +14 -7
- data/ruby-dbus.gemspec +7 -3
- data/spec/async_spec.rb +2 -0
- data/spec/binding_spec.rb +2 -0
- data/spec/bus_and_xml_backend_spec.rb +2 -0
- data/spec/bus_driver_spec.rb +2 -0
- data/spec/bus_name_spec.rb +3 -1
- data/spec/bus_spec.rb +2 -0
- data/spec/byte_array_spec.rb +2 -0
- data/spec/client_robustness_spec.rb +4 -2
- data/spec/data/marshall.yaml +1667 -0
- data/spec/data_spec.rb +673 -0
- data/spec/emits_changed_signal_spec.rb +58 -0
- data/spec/err_msg_spec.rb +2 -0
- data/spec/introspect_xml_parser_spec.rb +2 -0
- data/spec/introspection_spec.rb +2 -0
- data/spec/main_loop_spec.rb +3 -1
- data/spec/node_spec.rb +23 -0
- data/spec/object_path_spec.rb +3 -0
- data/spec/object_spec.rb +138 -0
- data/spec/packet_marshaller_spec.rb +41 -0
- data/spec/packet_unmarshaller_spec.rb +248 -0
- data/spec/property_spec.rb +192 -5
- data/spec/proxy_object_spec.rb +2 -0
- data/spec/server_robustness_spec.rb +2 -0
- data/spec/server_spec.rb +2 -0
- data/spec/service_newapi.rb +70 -70
- data/spec/session_bus_spec.rb +3 -1
- data/spec/session_bus_spec_manual.rb +2 -0
- data/spec/signal_spec.rb +5 -3
- data/spec/spec_helper.rb +37 -9
- data/spec/thread_safety_spec.rb +2 -0
- data/spec/tools/dbus-limited-session.conf +4 -0
- data/spec/type_spec.rb +214 -6
- data/spec/value_spec.rb +16 -1
- data/spec/variant_spec.rb +4 -2
- data/spec/zzz_quit_spec.rb +16 -0
- metadata +34 -8
@@ -0,0 +1,83 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This file is part of the ruby-dbus project
|
4
|
+
# Copyright (C) 2022 Martin Vidner
|
5
|
+
#
|
6
|
+
# This library is free software; you can redistribute it and/or
|
7
|
+
# modify it under the terms of the GNU Lesser General Public
|
8
|
+
# License, version 2.1 as published by the Free Software Foundation.
|
9
|
+
# See the file "COPYING" for the exact licensing terms.
|
10
|
+
|
11
|
+
module DBus
|
12
|
+
# Describes the behavior of PropertiesChanged signal, for a single property
|
13
|
+
# or for an entire interface.
|
14
|
+
#
|
15
|
+
# The possible values are:
|
16
|
+
#
|
17
|
+
# - *true*: the signal is emitted with the value included.
|
18
|
+
# - *:invalidates*: the signal is emitted but the value is not included
|
19
|
+
# in the signal.
|
20
|
+
# - *:const*: the property never changes value during the lifetime
|
21
|
+
# of the object it belongs to, and hence the signal
|
22
|
+
# is never emitted for it (but clients can cache the value)
|
23
|
+
# - *false*: the signal won't be emitted (clients should re-Get the property value)
|
24
|
+
#
|
25
|
+
# The default is:
|
26
|
+
# - for an interface: *true*
|
27
|
+
# - for a property: what the parent interface specifies
|
28
|
+
#
|
29
|
+
# @see DBus::Object.emits_changed_signal
|
30
|
+
# @see DBus::Object.dbus_attr_accessor
|
31
|
+
# @see https://dbus.freedesktop.org/doc/dbus-specification.html#introspection-format
|
32
|
+
#
|
33
|
+
# Immutable once constructed.
|
34
|
+
class EmitsChangedSignal
|
35
|
+
# @return [true,false,:const,:invalidates]
|
36
|
+
attr_reader :value
|
37
|
+
|
38
|
+
# @param value [true,false,:const,:invalidates,nil]
|
39
|
+
# See class-level description above, {EmitsChangedSignal}.
|
40
|
+
# @param interface [Interface,nil]
|
41
|
+
# If the (property-level) *value* is unspecified (nil), this is the
|
42
|
+
# containing {Interface} to get the value from.
|
43
|
+
def initialize(value, interface: nil)
|
44
|
+
if value.nil?
|
45
|
+
raise ArgumentError, "Both arguments are nil" if interface.nil?
|
46
|
+
|
47
|
+
@value = interface.emits_changed_signal.value
|
48
|
+
else
|
49
|
+
expecting = [true, false, :const, :invalidates]
|
50
|
+
unless expecting.include?(value)
|
51
|
+
raise ArgumentError, "Expecting one of #{expecting.inspect}. Seen #{value.inspect}"
|
52
|
+
end
|
53
|
+
|
54
|
+
@value = value
|
55
|
+
end
|
56
|
+
|
57
|
+
freeze
|
58
|
+
end
|
59
|
+
|
60
|
+
# Return introspection XML string representation
|
61
|
+
# @return [String]
|
62
|
+
def to_xml
|
63
|
+
return "" if @value == true
|
64
|
+
|
65
|
+
" <annotation name=\"org.freedesktop.DBus.Property.EmitsChangedSignal\" value=\"#{@value}\"/>\n"
|
66
|
+
end
|
67
|
+
|
68
|
+
def to_s
|
69
|
+
@value.to_s
|
70
|
+
end
|
71
|
+
|
72
|
+
def ==(other)
|
73
|
+
if other.is_a?(self.class)
|
74
|
+
other.value == @value
|
75
|
+
else
|
76
|
+
other == value
|
77
|
+
end
|
78
|
+
end
|
79
|
+
alias eql? ==
|
80
|
+
|
81
|
+
DEFAULT_ECS = EmitsChangedSignal.new(true)
|
82
|
+
end
|
83
|
+
end
|
data/lib/dbus/error.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# error.rb
|
2
4
|
#
|
3
5
|
# This file is part of the ruby-dbus project
|
@@ -32,7 +34,7 @@ module DBus
|
|
32
34
|
end
|
33
35
|
# TODO: validate error name
|
34
36
|
end
|
35
|
-
end
|
37
|
+
end
|
36
38
|
|
37
39
|
# @example raise a generic error
|
38
40
|
# raise DBus.error, "message"
|
@@ -43,4 +45,4 @@ module DBus
|
|
43
45
|
DBus::Error.new(nil, name)
|
44
46
|
end
|
45
47
|
module_function :error
|
46
|
-
end
|
48
|
+
end
|
data/lib/dbus/introspect.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# dbus/introspection.rb - module containing a low-level D-Bus introspection implementation
|
2
4
|
#
|
3
5
|
# This file is part of the ruby-dbus project
|
@@ -10,9 +12,9 @@
|
|
10
12
|
|
11
13
|
module DBus
|
12
14
|
# Regular expressions that should match all method names.
|
13
|
-
METHOD_SIGNAL_RE = /^[A-Za-z][A-Za-z0-9_]
|
15
|
+
METHOD_SIGNAL_RE = /^[A-Za-z][A-Za-z0-9_]*$/.freeze
|
14
16
|
# Regular expressions that should match all interface names.
|
15
|
-
INTERFACE_ELEMENT_RE = /^[A-Za-z][A-Za-z0-9_]
|
17
|
+
INTERFACE_ELEMENT_RE = /^[A-Za-z][A-Za-z0-9_]*$/.freeze
|
16
18
|
|
17
19
|
# Exception raised when an invalid class definition is encountered.
|
18
20
|
class InvalidClassDefinition < Exception
|
@@ -24,21 +26,42 @@ module DBus
|
|
24
26
|
# method call instantiates and configures this class for us.
|
25
27
|
#
|
26
28
|
# It also is the local definition of interface exported by the program.
|
27
|
-
# At the client side, see ProxyObjectInterface
|
29
|
+
# At the client side, see {ProxyObjectInterface}.
|
28
30
|
class Interface
|
29
|
-
# The name of the interface.
|
31
|
+
# @return [String] The name of the interface.
|
30
32
|
attr_reader :name
|
31
|
-
# The methods that are part of the interface.
|
33
|
+
# @return [Hash{Symbol => DBus::Method}] The methods that are part of the interface.
|
32
34
|
attr_reader :methods
|
33
|
-
# The signals that are part of the interface.
|
35
|
+
# @return [Hash{Symbol => Signal}] The signals that are part of the interface.
|
34
36
|
attr_reader :signals
|
35
37
|
|
38
|
+
# @return [Hash{Symbol => Property}]
|
39
|
+
attr_reader :properties
|
40
|
+
|
41
|
+
# @return [EmitsChangedSignal]
|
42
|
+
attr_reader :emits_changed_signal
|
43
|
+
|
36
44
|
# Creates a new interface with a given _name_.
|
37
45
|
def initialize(name)
|
38
46
|
validate_name(name)
|
39
47
|
@name = name
|
40
48
|
@methods = {}
|
41
49
|
@signals = {}
|
50
|
+
@properties = {}
|
51
|
+
@emits_changed_signal = EmitsChangedSignal::DEFAULT_ECS
|
52
|
+
end
|
53
|
+
|
54
|
+
# Helper for {Object.emits_changed_signal=}.
|
55
|
+
# @api private
|
56
|
+
def emits_changed_signal=(ecs)
|
57
|
+
raise TypeError unless ecs.is_a? EmitsChangedSignal
|
58
|
+
# equal?: object identity
|
59
|
+
unless @emits_changed_signal.equal?(EmitsChangedSignal::DEFAULT_ECS) ||
|
60
|
+
@emits_changed_signal.value == ecs.value
|
61
|
+
raise "emits_change_signal was assigned more than once"
|
62
|
+
end
|
63
|
+
|
64
|
+
@emits_changed_signal = ecs
|
42
65
|
end
|
43
66
|
|
44
67
|
# Validates a service _name_.
|
@@ -47,33 +70,57 @@ module DBus
|
|
47
70
|
raise InvalidIntrospectionData if name =~ /^\./ || name =~ /\.$/
|
48
71
|
raise InvalidIntrospectionData if name =~ /\.\./
|
49
72
|
raise InvalidIntrospectionData if name !~ /\./
|
73
|
+
|
50
74
|
name.split(".").each do |element|
|
51
75
|
raise InvalidIntrospectionData if element !~ INTERFACE_ELEMENT_RE
|
52
76
|
end
|
53
77
|
end
|
54
78
|
|
55
|
-
#
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
79
|
+
# Add _ifc_el_ as a known {Method}, {Signal} or {Property}
|
80
|
+
# @param ifc_el [InterfaceElement]
|
81
|
+
def define(ifc_el)
|
82
|
+
name = ifc_el.name.to_sym
|
83
|
+
category = case ifc_el
|
84
|
+
when Method
|
85
|
+
@methods
|
86
|
+
when Signal
|
87
|
+
@signals
|
88
|
+
when Property
|
89
|
+
@properties
|
90
|
+
end
|
91
|
+
category[name] = ifc_el
|
62
92
|
end
|
93
|
+
alias declare define
|
63
94
|
alias << define
|
64
95
|
|
65
96
|
# Defines a method with name _id_ and a given _prototype_ in the
|
66
97
|
# interface.
|
98
|
+
# Better name: declare_method
|
67
99
|
def define_method(id, prototype)
|
68
100
|
m = Method.new(id)
|
69
101
|
m.from_prototype(prototype)
|
70
102
|
define(m)
|
71
103
|
end
|
72
|
-
|
104
|
+
alias declare_method define_method
|
105
|
+
|
106
|
+
# Return introspection XML string representation of the property.
|
107
|
+
# @return [String]
|
108
|
+
def to_xml
|
109
|
+
xml = " <interface name=\"#{name}\">\n"
|
110
|
+
xml += emits_changed_signal.to_xml
|
111
|
+
methods.each_value { |m| xml += m.to_xml }
|
112
|
+
signals.each_value { |m| xml += m.to_xml }
|
113
|
+
properties.each_value { |m| xml += m.to_xml }
|
114
|
+
xml += " </interface>\n"
|
115
|
+
xml
|
116
|
+
end
|
117
|
+
end
|
73
118
|
|
74
119
|
# = A formal parameter has a name and a type
|
75
120
|
class FormalParameter
|
121
|
+
# @return [#to_s]
|
76
122
|
attr_reader :name
|
123
|
+
# @return [SingleCompleteType]
|
77
124
|
attr_reader :type
|
78
125
|
|
79
126
|
def initialize(name, type)
|
@@ -95,14 +142,16 @@ module DBus
|
|
95
142
|
# This is a generic class for entities that are part of the interface
|
96
143
|
# such as methods and signals.
|
97
144
|
class InterfaceElement
|
98
|
-
# The name of the interface element
|
145
|
+
# @return [Symbol] The name of the interface element
|
99
146
|
attr_reader :name
|
100
|
-
|
147
|
+
|
148
|
+
# @return [Array<FormalParameter>] The parameters of the interface element
|
101
149
|
attr_reader :params
|
102
150
|
|
103
151
|
# Validates element _name_.
|
104
152
|
def validate_name(name)
|
105
153
|
return if (name =~ METHOD_SIGNAL_RE) && (name.bytesize <= 255)
|
154
|
+
|
106
155
|
raise InvalidMethodName, name
|
107
156
|
end
|
108
157
|
|
@@ -123,13 +172,13 @@ module DBus
|
|
123
172
|
def add_param(name_signature_pair)
|
124
173
|
add_fparam(*name_signature_pair)
|
125
174
|
end
|
126
|
-
end
|
175
|
+
end
|
127
176
|
|
128
177
|
# = D-Bus interface method class
|
129
178
|
#
|
130
179
|
# This is a class representing methods that are part of an interface.
|
131
180
|
class Method < InterfaceElement
|
132
|
-
# The list of return values for the method
|
181
|
+
# @return [Array<FormalParameter>] The list of return values for the method
|
133
182
|
attr_reader :rets
|
134
183
|
|
135
184
|
# Creates a new method interface element with the given _name_.
|
@@ -139,15 +188,19 @@ module DBus
|
|
139
188
|
end
|
140
189
|
|
141
190
|
# Add a return value _name_ and _signature_.
|
191
|
+
# @param name [#to_s]
|
192
|
+
# @param signature [SingleCompleteType]
|
142
193
|
def add_return(name, signature)
|
143
194
|
@rets << FormalParameter.new(name, signature)
|
144
195
|
end
|
145
196
|
|
146
197
|
# Add parameter types by parsing the given _prototype_.
|
198
|
+
# @param prototype [Prototype]
|
147
199
|
def from_prototype(prototype)
|
148
200
|
prototype.split(/, */).each do |arg|
|
149
201
|
arg = arg.split(" ")
|
150
202
|
raise InvalidClassDefinition if arg.size != 2
|
203
|
+
|
151
204
|
dir, arg = arg
|
152
205
|
if arg =~ /:/
|
153
206
|
arg = arg.split(":")
|
@@ -166,20 +219,21 @@ module DBus
|
|
166
219
|
end
|
167
220
|
|
168
221
|
# Return an XML string representation of the method interface elment.
|
222
|
+
# @return [String]
|
169
223
|
def to_xml
|
170
|
-
xml =
|
224
|
+
xml = " <method name=\"#{@name}\">\n"
|
171
225
|
@params.each do |param|
|
172
|
-
name = param.name ?
|
173
|
-
xml +=
|
226
|
+
name = param.name ? "name=\"#{param.name}\" " : ""
|
227
|
+
xml += " <arg #{name}direction=\"in\" type=\"#{param.type}\"/>\n"
|
174
228
|
end
|
175
229
|
@rets.each do |param|
|
176
|
-
name = param.name ?
|
177
|
-
xml +=
|
230
|
+
name = param.name ? "name=\"#{param.name}\" " : ""
|
231
|
+
xml += " <arg #{name}direction=\"out\" type=\"#{param.type}\"/>\n"
|
178
232
|
end
|
179
|
-
xml +=
|
233
|
+
xml += " </method>\n"
|
180
234
|
xml
|
181
235
|
end
|
182
|
-
end
|
236
|
+
end
|
183
237
|
|
184
238
|
# = D-Bus interface signal class
|
185
239
|
#
|
@@ -201,13 +255,60 @@ module DBus
|
|
201
255
|
|
202
256
|
# Return an XML string representation of the signal interface elment.
|
203
257
|
def to_xml
|
204
|
-
xml =
|
258
|
+
xml = " <signal name=\"#{@name}\">\n"
|
205
259
|
@params.each do |param|
|
206
|
-
name = param.name ?
|
207
|
-
xml +=
|
260
|
+
name = param.name ? "name=\"#{param.name}\" " : ""
|
261
|
+
xml += " <arg #{name}type=\"#{param.type}\"/>\n"
|
208
262
|
end
|
209
|
-
xml +=
|
263
|
+
xml += " </signal>\n"
|
210
264
|
xml
|
211
265
|
end
|
212
|
-
end
|
213
|
-
|
266
|
+
end
|
267
|
+
|
268
|
+
# An (exported) property
|
269
|
+
# https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-properties
|
270
|
+
class Property
|
271
|
+
# @return [Symbol] The name of the property, for example FooBar.
|
272
|
+
attr_reader :name
|
273
|
+
# @return [SingleCompleteType]
|
274
|
+
attr_reader :type
|
275
|
+
# @return [Symbol] :read :write or :readwrite
|
276
|
+
attr_reader :access
|
277
|
+
|
278
|
+
# @return [Symbol,nil] What to call at Ruby side.
|
279
|
+
# (Always without the trailing `=`)
|
280
|
+
# It is `nil` IFF representing a client-side proxy.
|
281
|
+
attr_reader :ruby_name
|
282
|
+
|
283
|
+
def initialize(name, type, access, ruby_name:)
|
284
|
+
@name = name.to_sym
|
285
|
+
@type = type
|
286
|
+
@access = access
|
287
|
+
@ruby_name = ruby_name
|
288
|
+
end
|
289
|
+
|
290
|
+
# @return [Boolean]
|
291
|
+
def readable?
|
292
|
+
access == :read || access == :readwrite
|
293
|
+
end
|
294
|
+
|
295
|
+
# @return [Boolean]
|
296
|
+
def writable?
|
297
|
+
access == :write || access == :readwrite
|
298
|
+
end
|
299
|
+
|
300
|
+
# Return introspection XML string representation of the property.
|
301
|
+
def to_xml
|
302
|
+
" <property type=\"#{@type}\" name=\"#{@name}\" access=\"#{@access}\"/>\n"
|
303
|
+
end
|
304
|
+
|
305
|
+
# @param xml_node [AbstractXML::Node]
|
306
|
+
# @return [Property]
|
307
|
+
def self.from_xml(xml_node)
|
308
|
+
name = xml_node["name"].to_sym
|
309
|
+
type = xml_node["type"]
|
310
|
+
access = xml_node["access"].to_sym
|
311
|
+
new(name, type, access, ruby_name: nil)
|
312
|
+
end
|
313
|
+
end
|
314
|
+
end
|
data/lib/dbus/logger.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# dbus/logger.rb - debug logging
|
2
4
|
#
|
3
5
|
# This file is part of the ruby-dbus project
|
@@ -16,7 +18,7 @@ module DBus
|
|
16
18
|
# with DEBUG if $DEBUG is set, otherwise INFO.
|
17
19
|
def logger
|
18
20
|
unless defined? @logger
|
19
|
-
@logger = Logger.new(
|
21
|
+
@logger = Logger.new($stderr)
|
20
22
|
@logger.level = $DEBUG ? Logger::DEBUG : Logger::INFO
|
21
23
|
end
|
22
24
|
@logger
|