em-ruby-dbus 0.11.0
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 +7 -0
- data/COPYING +504 -0
- data/NEWS +253 -0
- data/README.md +93 -0
- data/Rakefile +58 -0
- data/VERSION +1 -0
- data/doc/Reference.md +207 -0
- data/doc/Tutorial.md +480 -0
- data/doc/ex-calling-methods.body.rb +8 -0
- data/doc/ex-calling-methods.rb +3 -0
- data/doc/ex-properties.body.rb +9 -0
- data/doc/ex-properties.rb +3 -0
- data/doc/ex-setup.rb +7 -0
- data/doc/ex-signal.body.rb +20 -0
- data/doc/ex-signal.rb +3 -0
- data/doc/example-helper.rb +6 -0
- data/em-ruby-dbus.gemspec +20 -0
- data/examples/gdbus/gdbus +255 -0
- data/examples/gdbus/gdbus.glade +184 -0
- data/examples/gdbus/launch.sh +4 -0
- data/examples/no-introspect/nm-test.rb +21 -0
- data/examples/no-introspect/tracker-test.rb +16 -0
- data/examples/rhythmbox/playpause.rb +25 -0
- data/examples/service/call_service.rb +25 -0
- data/examples/service/service_newapi.rb +51 -0
- data/examples/simple/call_introspect.rb +34 -0
- data/examples/simple/properties.rb +19 -0
- data/examples/utils/listnames.rb +11 -0
- data/examples/utils/notify.rb +19 -0
- data/lib/dbus.rb +82 -0
- data/lib/dbus/auth.rb +269 -0
- data/lib/dbus/bus.rb +739 -0
- data/lib/dbus/core_ext/array/extract_options.rb +31 -0
- data/lib/dbus/core_ext/class/attribute.rb +129 -0
- data/lib/dbus/core_ext/kernel/singleton_class.rb +8 -0
- data/lib/dbus/core_ext/module/remove_method.rb +14 -0
- data/lib/dbus/error.rb +46 -0
- data/lib/dbus/export.rb +128 -0
- data/lib/dbus/introspect.rb +219 -0
- data/lib/dbus/logger.rb +31 -0
- data/lib/dbus/loop-em.rb +19 -0
- data/lib/dbus/marshall.rb +434 -0
- data/lib/dbus/matchrule.rb +101 -0
- data/lib/dbus/message.rb +276 -0
- data/lib/dbus/message_queue.rb +166 -0
- data/lib/dbus/proxy_object.rb +149 -0
- data/lib/dbus/proxy_object_factory.rb +41 -0
- data/lib/dbus/proxy_object_interface.rb +128 -0
- data/lib/dbus/type.rb +193 -0
- data/lib/dbus/xml.rb +161 -0
- data/test/async_spec.rb +47 -0
- data/test/binding_spec.rb +74 -0
- data/test/bus_and_xml_backend_spec.rb +39 -0
- data/test/bus_driver_spec.rb +20 -0
- data/test/bus_spec.rb +20 -0
- data/test/byte_array_spec.rb +38 -0
- data/test/err_msg_spec.rb +42 -0
- data/test/introspect_xml_parser_spec.rb +26 -0
- data/test/introspection_spec.rb +32 -0
- data/test/main_loop_spec.rb +82 -0
- data/test/property_spec.rb +53 -0
- data/test/server_robustness_spec.rb +66 -0
- data/test/server_spec.rb +53 -0
- data/test/service_newapi.rb +217 -0
- data/test/session_bus_spec_manual.rb +15 -0
- data/test/signal_spec.rb +90 -0
- data/test/spec_helper.rb +33 -0
- data/test/thread_safety_spec.rb +31 -0
- data/test/tools/dbus-launch-simple +35 -0
- data/test/tools/dbus-limited-session.conf +28 -0
- data/test/tools/test_env +13 -0
- data/test/tools/test_server +39 -0
- data/test/type_spec.rb +19 -0
- data/test/value_spec.rb +81 -0
- data/test/variant_spec.rb +66 -0
- metadata +145 -0
@@ -0,0 +1,31 @@
|
|
1
|
+
# copied from activesupport/core_ext from Rails, MIT license
|
2
|
+
# https://github.com/rails/rails/tree/5aa869861c192daceafe3a3ee50eb93f5a2b7bd2/activesupport/lib/active_support/core_ext
|
3
|
+
class Hash
|
4
|
+
# By default, only instances of Hash itself are extractable.
|
5
|
+
# Subclasses of Hash may implement this method and return
|
6
|
+
# true to declare themselves as extractable. If a Hash
|
7
|
+
# is extractable, Array#extract_options! pops it from
|
8
|
+
# the Array when it is the last element of the Array.
|
9
|
+
def extractable_options?
|
10
|
+
instance_of?(Hash)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class Array
|
15
|
+
# Extracts options from a set of arguments. Removes and returns the last
|
16
|
+
# element in the array if it's a hash, otherwise returns a blank hash.
|
17
|
+
#
|
18
|
+
# def options(*args)
|
19
|
+
# args.extract_options!
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# options(1, 2) # => {}
|
23
|
+
# options(1, 2, a: :b) # => {:a=>:b}
|
24
|
+
def extract_options!
|
25
|
+
if last.is_a?(Hash) && last.extractable_options?
|
26
|
+
pop
|
27
|
+
else
|
28
|
+
{}
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
# copied from activesupport/core_ext from Rails, MIT license
|
2
|
+
# https://github.com/rails/rails/tree/5aa869861c192daceafe3a3ee50eb93f5a2b7bd2/activesupport/lib/active_support/core_ext
|
3
|
+
require 'dbus/core_ext/kernel/singleton_class'
|
4
|
+
require 'dbus/core_ext/module/remove_method'
|
5
|
+
require 'dbus/core_ext/array/extract_options'
|
6
|
+
|
7
|
+
class Class
|
8
|
+
# Declare a class-level attribute whose value is inheritable by subclasses.
|
9
|
+
# Subclasses can change their own value and it will not impact parent class.
|
10
|
+
#
|
11
|
+
# class Base
|
12
|
+
# class_attribute :setting
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# class Subclass < Base
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# Base.setting = true
|
19
|
+
# Subclass.setting # => true
|
20
|
+
# Subclass.setting = false
|
21
|
+
# Subclass.setting # => false
|
22
|
+
# Base.setting # => true
|
23
|
+
#
|
24
|
+
# In the above case as long as Subclass does not assign a value to setting
|
25
|
+
# by performing <tt>Subclass.setting = _something_ </tt>, <tt>Subclass.setting</tt>
|
26
|
+
# would read value assigned to parent class. Once Subclass assigns a value then
|
27
|
+
# the value assigned by Subclass would be returned.
|
28
|
+
#
|
29
|
+
# This matches normal Ruby method inheritance: think of writing an attribute
|
30
|
+
# on a subclass as overriding the reader method. However, you need to be aware
|
31
|
+
# when using +class_attribute+ with mutable structures as +Array+ or +Hash+.
|
32
|
+
# In such cases, you don't want to do changes in places but use setters:
|
33
|
+
#
|
34
|
+
# Base.setting = []
|
35
|
+
# Base.setting # => []
|
36
|
+
# Subclass.setting # => []
|
37
|
+
#
|
38
|
+
# # Appending in child changes both parent and child because it is the same object:
|
39
|
+
# Subclass.setting << :foo
|
40
|
+
# Base.setting # => [:foo]
|
41
|
+
# Subclass.setting # => [:foo]
|
42
|
+
#
|
43
|
+
# # Use setters to not propagate changes:
|
44
|
+
# Base.setting = []
|
45
|
+
# Subclass.setting += [:foo]
|
46
|
+
# Base.setting # => []
|
47
|
+
# Subclass.setting # => [:foo]
|
48
|
+
#
|
49
|
+
# For convenience, an instance predicate method is defined as well.
|
50
|
+
# To skip it, pass <tt>instance_predicate: false</tt>.
|
51
|
+
#
|
52
|
+
# Subclass.setting? # => false
|
53
|
+
#
|
54
|
+
# Instances may overwrite the class value in the same way:
|
55
|
+
#
|
56
|
+
# Base.setting = true
|
57
|
+
# object = Base.new
|
58
|
+
# object.setting # => true
|
59
|
+
# object.setting = false
|
60
|
+
# object.setting # => false
|
61
|
+
# Base.setting # => true
|
62
|
+
#
|
63
|
+
# To opt out of the instance reader method, pass <tt>instance_reader: false</tt>.
|
64
|
+
#
|
65
|
+
# object.setting # => NoMethodError
|
66
|
+
# object.setting? # => NoMethodError
|
67
|
+
#
|
68
|
+
# To opt out of the instance writer method, pass <tt>instance_writer: false</tt>.
|
69
|
+
#
|
70
|
+
# object.setting = false # => NoMethodError
|
71
|
+
#
|
72
|
+
# To opt out of both instance methods, pass <tt>instance_accessor: false</tt>.
|
73
|
+
def class_attribute(*attrs)
|
74
|
+
options = attrs.extract_options!
|
75
|
+
instance_reader = options.fetch(:instance_accessor, true) && options.fetch(:instance_reader, true)
|
76
|
+
instance_writer = options.fetch(:instance_accessor, true) && options.fetch(:instance_writer, true)
|
77
|
+
instance_predicate = options.fetch(:instance_predicate, true)
|
78
|
+
|
79
|
+
attrs.each do |name|
|
80
|
+
define_singleton_method(name) { nil }
|
81
|
+
define_singleton_method("#{name}?") { !!public_send(name) } if instance_predicate
|
82
|
+
|
83
|
+
ivar = "@#{name}"
|
84
|
+
|
85
|
+
define_singleton_method("#{name}=") do |val|
|
86
|
+
singleton_class.class_eval do
|
87
|
+
remove_possible_method(name)
|
88
|
+
define_method(name) { val }
|
89
|
+
end
|
90
|
+
|
91
|
+
if singleton_class?
|
92
|
+
class_eval do
|
93
|
+
remove_possible_method(name)
|
94
|
+
define_method(name) do
|
95
|
+
if instance_variable_defined? ivar
|
96
|
+
instance_variable_get ivar
|
97
|
+
else
|
98
|
+
singleton_class.send name
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
val
|
104
|
+
end
|
105
|
+
|
106
|
+
if instance_reader
|
107
|
+
remove_possible_method name
|
108
|
+
define_method(name) do
|
109
|
+
if instance_variable_defined?(ivar)
|
110
|
+
instance_variable_get ivar
|
111
|
+
else
|
112
|
+
self.class.public_send name
|
113
|
+
end
|
114
|
+
end
|
115
|
+
define_method("#{name}?") { !!public_send(name) } if instance_predicate
|
116
|
+
end
|
117
|
+
|
118
|
+
attr_writer name if instance_writer
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
private
|
123
|
+
|
124
|
+
unless respond_to?(:singleton_class?)
|
125
|
+
def singleton_class?
|
126
|
+
ancestors.first != self
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
# copied from activesupport/core_ext from Rails, MIT license
|
2
|
+
# https://github.com/rails/rails/tree/5aa869861c192daceafe3a3ee50eb93f5a2b7bd2/activesupport/lib/active_support/core_ext
|
3
|
+
module Kernel
|
4
|
+
# class_eval on an object acts like singleton_class.class_eval.
|
5
|
+
def class_eval(*args, &block)
|
6
|
+
singleton_class.class_eval(*args, &block)
|
7
|
+
end
|
8
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# copied from activesupport/core_ext from Rails, MIT license
|
2
|
+
# https://github.com/rails/rails/tree/5aa869861c192daceafe3a3ee50eb93f5a2b7bd2/activesupport/lib/active_support/core_ext
|
3
|
+
class Module
|
4
|
+
def remove_possible_method(method)
|
5
|
+
if method_defined?(method) || private_method_defined?(method)
|
6
|
+
undef_method(method)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def redefine_method(method, &block)
|
11
|
+
remove_possible_method(method)
|
12
|
+
define_method(method, &block)
|
13
|
+
end
|
14
|
+
end
|
data/lib/dbus/error.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
# error.rb
|
2
|
+
#
|
3
|
+
# This file is part of the ruby-dbus project
|
4
|
+
# Copyright (C) 2007 Arnaud Cornet and Paul van Tilburg
|
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
|
+
# Represents a D-Bus Error, both on the client and server side.
|
13
|
+
class Error < StandardError
|
14
|
+
# error_name. +message+ is inherited from +Exception+
|
15
|
+
attr_reader :name
|
16
|
+
# for received errors, the raw D-Bus message
|
17
|
+
attr_reader :dbus_message
|
18
|
+
|
19
|
+
# If +msg+ is a +DBus::Message+, its contents is used for initialization.
|
20
|
+
# Otherwise, +msg+ is taken as a string and +name+ is used.
|
21
|
+
def initialize(msg, name = "org.freedesktop.DBus.Error.Failed")
|
22
|
+
if msg.is_a? DBus::Message
|
23
|
+
@dbus_message = msg
|
24
|
+
@name = msg.error_name
|
25
|
+
super(msg.params[0]) # or nil
|
26
|
+
if msg.params[1].is_a? Array
|
27
|
+
set_backtrace msg.params[1]
|
28
|
+
end
|
29
|
+
else
|
30
|
+
@name = name
|
31
|
+
super(msg)
|
32
|
+
end
|
33
|
+
# TODO validate error name
|
34
|
+
end
|
35
|
+
end # class Error
|
36
|
+
|
37
|
+
# @example raise a generic error
|
38
|
+
# raise DBus.error, "message"
|
39
|
+
# @example raise a specific error
|
40
|
+
# raise DBus.error("org.example.Error.SeatOccupied"), "Seat #{n} is occupied"
|
41
|
+
def error(name = "org.freedesktop.DBus.Error.Failed")
|
42
|
+
# message will be set by Kernel.raise
|
43
|
+
DBus::Error.new(nil, name)
|
44
|
+
end
|
45
|
+
module_function :error
|
46
|
+
end # module DBus
|
data/lib/dbus/export.rb
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
# dbus/introspection.rb - module containing a low-level D-Bus introspection implementation
|
2
|
+
#
|
3
|
+
# This file is part of the ruby-dbus project
|
4
|
+
# Copyright (C) 2007 Arnaud Cornet and Paul van Tilburg
|
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
|
+
require 'thread'
|
12
|
+
|
13
|
+
module DBus
|
14
|
+
# Exported object type
|
15
|
+
# = Exportable D-Bus object class
|
16
|
+
#
|
17
|
+
# Objects that are going to be exported by a D-Bus service
|
18
|
+
# should inherit from this class. At the client side, use ProxyObject.
|
19
|
+
class Object
|
20
|
+
# The path of the object.
|
21
|
+
attr_reader :path
|
22
|
+
# The interfaces that the object supports. Hash: String => Interface
|
23
|
+
class_attribute :intfs
|
24
|
+
# The service that the object is exported by.
|
25
|
+
attr_writer :service
|
26
|
+
|
27
|
+
@@cur_intf = nil # Interface
|
28
|
+
@@intfs_mutex = Mutex.new
|
29
|
+
|
30
|
+
# Create a new object with a given _path_.
|
31
|
+
# Use Service#export to export it.
|
32
|
+
def initialize(path)
|
33
|
+
@path = path
|
34
|
+
@service = nil
|
35
|
+
end
|
36
|
+
|
37
|
+
# State that the object implements the given _intf_.
|
38
|
+
def implements(intf)
|
39
|
+
# use a setter
|
40
|
+
self.intfs = (self.intfs || {}).merge({intf.name => intf})
|
41
|
+
end
|
42
|
+
|
43
|
+
# Dispatch a message _msg_ to call exported methods
|
44
|
+
def dispatch(msg)
|
45
|
+
case msg.message_type
|
46
|
+
when Message::METHOD_CALL
|
47
|
+
reply = nil
|
48
|
+
begin
|
49
|
+
if not self.intfs[msg.interface]
|
50
|
+
raise DBus.error("org.freedesktop.DBus.Error.UnknownMethod"),
|
51
|
+
"Interface \"#{msg.interface}\" of object \"#{msg.path}\" doesn't exist"
|
52
|
+
end
|
53
|
+
meth = self.intfs[msg.interface].methods[msg.member.to_sym]
|
54
|
+
if not meth
|
55
|
+
raise DBus.error("org.freedesktop.DBus.Error.UnknownMethod"),
|
56
|
+
"Method \"#{msg.member}\" on interface \"#{msg.interface}\" of object \"#{msg.path}\" doesn't exist"
|
57
|
+
end
|
58
|
+
methname = Object.make_method_name(msg.interface, msg.member)
|
59
|
+
retdata = method(methname).call(*msg.params)
|
60
|
+
retdata = [*retdata]
|
61
|
+
|
62
|
+
reply = Message.method_return(msg)
|
63
|
+
meth.rets.zip(retdata).each do |rsig, rdata|
|
64
|
+
reply.add_param(rsig.type, rdata)
|
65
|
+
end
|
66
|
+
rescue => ex
|
67
|
+
dbus_msg_exc = msg.annotate_exception(ex)
|
68
|
+
reply = ErrorMessage.from_exception(dbus_msg_exc).reply_to(msg)
|
69
|
+
end
|
70
|
+
@service.bus.message_queue.push(reply)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# Select (and create) the interface that the following defined methods
|
75
|
+
# belong to.
|
76
|
+
def self.dbus_interface(s)
|
77
|
+
@@intfs_mutex.synchronize do
|
78
|
+
unless @@cur_intf = (self.intfs && self.intfs[s])
|
79
|
+
@@cur_intf = Interface.new(s)
|
80
|
+
self.intfs = (self.intfs || {}).merge({s => @@cur_intf})
|
81
|
+
end
|
82
|
+
yield
|
83
|
+
@@cur_intf = nil
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# Dummy undefined interface class.
|
88
|
+
class UndefinedInterface < ScriptError
|
89
|
+
def initialize(sym)
|
90
|
+
super "No interface specified for #{sym}"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# Defines an exportable method on the object with the given name _sym_,
|
95
|
+
# _prototype_ and the code in a block.
|
96
|
+
def self.dbus_method(sym, protoype = "", &block)
|
97
|
+
raise UndefinedInterface, sym if @@cur_intf.nil?
|
98
|
+
@@cur_intf.define(Method.new(sym.to_s).from_prototype(protoype))
|
99
|
+
define_method(Object.make_method_name(@@cur_intf.name, sym.to_s), &block)
|
100
|
+
end
|
101
|
+
|
102
|
+
# Emits a signal from the object with the given _interface_, signal
|
103
|
+
# _sig_ and arguments _args_.
|
104
|
+
def emit(intf, sig, *args)
|
105
|
+
@service.bus.emit(@service, self, intf, sig, *args)
|
106
|
+
end
|
107
|
+
|
108
|
+
# Defines a signal for the object with a given name _sym_ and _prototype_.
|
109
|
+
def self.dbus_signal(sym, protoype = "")
|
110
|
+
raise UndefinedInterface, sym if @@cur_intf.nil?
|
111
|
+
cur_intf = @@cur_intf
|
112
|
+
signal = Signal.new(sym.to_s).from_prototype(protoype)
|
113
|
+
cur_intf.define(Signal.new(sym.to_s).from_prototype(protoype))
|
114
|
+
define_method(sym.to_s) do |*args|
|
115
|
+
emit(cur_intf, signal, *args)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
####################################################################
|
120
|
+
private
|
121
|
+
|
122
|
+
# Helper method that returns a method name generated from the interface
|
123
|
+
# name _intfname_ and method name _methname_.
|
124
|
+
def self.make_method_name(intfname, methname)
|
125
|
+
"#{intfname}%%#{methname}"
|
126
|
+
end
|
127
|
+
end # class Object
|
128
|
+
end # module DBus
|
@@ -0,0 +1,219 @@
|
|
1
|
+
# dbus/introspection.rb - module containing a low-level D-Bus introspection implementation
|
2
|
+
#
|
3
|
+
# This file is part of the ruby-dbus project
|
4
|
+
# Copyright (C) 2007 Arnaud Cornet and Paul van Tilburg
|
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
|
+
# Regular expressions that should match all method names.
|
13
|
+
MethodSignalRE = /^[A-Za-z][A-Za-z0-9_]*$/
|
14
|
+
# Regular expressions that should match all interface names.
|
15
|
+
InterfaceElementRE = /^[A-Za-z][A-Za-z0-9_]*$/
|
16
|
+
|
17
|
+
# Exception raised when an unknown signal is used.
|
18
|
+
class UnknownSignal < Exception
|
19
|
+
end
|
20
|
+
|
21
|
+
# Exception raised when an invalid class definition is encountered.
|
22
|
+
class InvalidClassDefinition < Exception
|
23
|
+
end
|
24
|
+
|
25
|
+
# = D-Bus interface class
|
26
|
+
#
|
27
|
+
# This class is the interface descriptor. In most cases, the Introspect()
|
28
|
+
# method call instantiates and configures this class for us.
|
29
|
+
#
|
30
|
+
# It also is the local definition of interface exported by the program.
|
31
|
+
# At the client side, see ProxyObjectInterface
|
32
|
+
class Interface
|
33
|
+
# The name of the interface. String
|
34
|
+
attr_reader :name
|
35
|
+
# The methods that are part of the interface. Hash: Symbol => DBus::Method
|
36
|
+
attr_reader :methods
|
37
|
+
# The signals that are part of the interface. Hash: Symbol => Signal
|
38
|
+
attr_reader :signals
|
39
|
+
|
40
|
+
# Creates a new interface with a given _name_.
|
41
|
+
def initialize(name)
|
42
|
+
validate_name(name)
|
43
|
+
@name = name
|
44
|
+
@methods, @signals = Hash.new, Hash.new
|
45
|
+
end
|
46
|
+
|
47
|
+
# Validates a service _name_.
|
48
|
+
def validate_name(name)
|
49
|
+
raise InvalidIntrospectionData if name.bytesize > 255
|
50
|
+
raise InvalidIntrospectionData if name =~ /^\./ or name =~ /\.$/
|
51
|
+
raise InvalidIntrospectionData if name =~ /\.\./
|
52
|
+
raise InvalidIntrospectionData if not name =~ /\./
|
53
|
+
name.split(".").each do |element|
|
54
|
+
raise InvalidIntrospectionData if not element =~ InterfaceElementRE
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Helper method for defining a method _m_.
|
59
|
+
def define(m)
|
60
|
+
if m.kind_of?(Method)
|
61
|
+
@methods[m.name.to_sym] = m
|
62
|
+
elsif m.kind_of?(Signal)
|
63
|
+
@signals[m.name.to_sym] = m
|
64
|
+
end
|
65
|
+
end
|
66
|
+
alias :<< :define
|
67
|
+
|
68
|
+
# Defines a method with name _id_ and a given _prototype_ in the
|
69
|
+
# interface.
|
70
|
+
def define_method(id, prototype)
|
71
|
+
m = Method.new(id)
|
72
|
+
m.from_prototype(prototype)
|
73
|
+
define(m)
|
74
|
+
end
|
75
|
+
end # class Interface
|
76
|
+
|
77
|
+
# = A formal parameter has a name and a type
|
78
|
+
class FormalParameter
|
79
|
+
attr_reader :name
|
80
|
+
attr_reader :type
|
81
|
+
|
82
|
+
def initialize(name, type)
|
83
|
+
@name = name
|
84
|
+
@type = type
|
85
|
+
end
|
86
|
+
|
87
|
+
# backward compatibility, deprecated
|
88
|
+
def [](index)
|
89
|
+
case index
|
90
|
+
when 0 then name
|
91
|
+
when 1 then type
|
92
|
+
else nil
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# = D-Bus interface element class
|
98
|
+
#
|
99
|
+
# This is a generic class for entities that are part of the interface
|
100
|
+
# such as methods and signals.
|
101
|
+
class InterfaceElement
|
102
|
+
# The name of the interface element. Symbol
|
103
|
+
attr_reader :name
|
104
|
+
# The parameters of the interface element. Array: FormalParameter
|
105
|
+
attr_reader :params
|
106
|
+
|
107
|
+
# Validates element _name_.
|
108
|
+
def validate_name(name)
|
109
|
+
if (not name =~ MethodSignalRE) or (name.bytesize > 255)
|
110
|
+
raise InvalidMethodName, name
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# Creates a new element with the given _name_.
|
115
|
+
def initialize(name)
|
116
|
+
validate_name(name.to_s)
|
117
|
+
@name = name
|
118
|
+
@params = Array.new
|
119
|
+
end
|
120
|
+
|
121
|
+
# Adds a formal parameter with _name_ and _signature_
|
122
|
+
# (See also Message#add_param which takes signature+value)
|
123
|
+
def add_fparam(name, signature)
|
124
|
+
@params << FormalParameter.new(name, signature)
|
125
|
+
end
|
126
|
+
|
127
|
+
# Deprecated, for backward compatibility
|
128
|
+
def add_param(name_signature_pair)
|
129
|
+
add_fparam(*name_signature_pair)
|
130
|
+
end
|
131
|
+
end # class InterfaceElement
|
132
|
+
|
133
|
+
# = D-Bus interface method class
|
134
|
+
#
|
135
|
+
# This is a class representing methods that are part of an interface.
|
136
|
+
class Method < InterfaceElement
|
137
|
+
# The list of return values for the method. Array: FormalParameter
|
138
|
+
attr_reader :rets
|
139
|
+
|
140
|
+
# Creates a new method interface element with the given _name_.
|
141
|
+
def initialize(name)
|
142
|
+
super(name)
|
143
|
+
@rets = Array.new
|
144
|
+
end
|
145
|
+
|
146
|
+
# Add a return value _name_ and _signature_.
|
147
|
+
def add_return(name, signature)
|
148
|
+
@rets << FormalParameter.new(name, signature)
|
149
|
+
end
|
150
|
+
|
151
|
+
# Add parameter types by parsing the given _prototype_.
|
152
|
+
def from_prototype(prototype)
|
153
|
+
prototype.split(/, */).each do |arg|
|
154
|
+
arg = arg.split(" ")
|
155
|
+
raise InvalidClassDefinition if arg.size != 2
|
156
|
+
dir, arg = arg
|
157
|
+
if arg =~ /:/
|
158
|
+
arg = arg.split(":")
|
159
|
+
name, sig = arg
|
160
|
+
else
|
161
|
+
sig = arg
|
162
|
+
end
|
163
|
+
case dir
|
164
|
+
when "in"
|
165
|
+
add_fparam(name, sig)
|
166
|
+
when "out"
|
167
|
+
add_return(name, sig)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
self
|
171
|
+
end
|
172
|
+
|
173
|
+
# Return an XML string representation of the method interface elment.
|
174
|
+
def to_xml
|
175
|
+
xml = %{<method name="#{@name}">\n}
|
176
|
+
@params.each do |param|
|
177
|
+
name = param.name ? %{name="#{param.name}" } : ""
|
178
|
+
xml += %{<arg #{name}direction="in" type="#{param.type}"/>\n}
|
179
|
+
end
|
180
|
+
@rets.each do |param|
|
181
|
+
name = param.name ? %{name="#{param.name}" } : ""
|
182
|
+
xml += %{<arg #{name}direction="out" type="#{param.type}"/>\n}
|
183
|
+
end
|
184
|
+
xml += %{</method>\n}
|
185
|
+
xml
|
186
|
+
end
|
187
|
+
end # class Method
|
188
|
+
|
189
|
+
# = D-Bus interface signal class
|
190
|
+
#
|
191
|
+
# This is a class representing signals that are part of an interface.
|
192
|
+
class Signal < InterfaceElement
|
193
|
+
# Add parameter types based on the given _prototype_.
|
194
|
+
def from_prototype(prototype)
|
195
|
+
prototype.split(/, */).each do |arg|
|
196
|
+
if arg =~ /:/
|
197
|
+
arg = arg.split(":")
|
198
|
+
name, sig = arg
|
199
|
+
else
|
200
|
+
sig = arg
|
201
|
+
end
|
202
|
+
add_fparam(name, sig)
|
203
|
+
end
|
204
|
+
self
|
205
|
+
end
|
206
|
+
|
207
|
+
# Return an XML string representation of the signal interface elment.
|
208
|
+
def to_xml
|
209
|
+
xml = %{<signal name="#{@name}">\n}
|
210
|
+
@params.each do |param|
|
211
|
+
name = param.name ? %{name="#{param.name}" } : ""
|
212
|
+
xml += %{<arg #{name}type="#{param.type}"/>\n}
|
213
|
+
end
|
214
|
+
xml += %{</signal>\n}
|
215
|
+
xml
|
216
|
+
end
|
217
|
+
end # class Signal
|
218
|
+
end # module DBus
|
219
|
+
|