em-ruby-dbus 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. checksums.yaml +7 -0
  2. data/COPYING +504 -0
  3. data/NEWS +253 -0
  4. data/README.md +93 -0
  5. data/Rakefile +58 -0
  6. data/VERSION +1 -0
  7. data/doc/Reference.md +207 -0
  8. data/doc/Tutorial.md +480 -0
  9. data/doc/ex-calling-methods.body.rb +8 -0
  10. data/doc/ex-calling-methods.rb +3 -0
  11. data/doc/ex-properties.body.rb +9 -0
  12. data/doc/ex-properties.rb +3 -0
  13. data/doc/ex-setup.rb +7 -0
  14. data/doc/ex-signal.body.rb +20 -0
  15. data/doc/ex-signal.rb +3 -0
  16. data/doc/example-helper.rb +6 -0
  17. data/em-ruby-dbus.gemspec +20 -0
  18. data/examples/gdbus/gdbus +255 -0
  19. data/examples/gdbus/gdbus.glade +184 -0
  20. data/examples/gdbus/launch.sh +4 -0
  21. data/examples/no-introspect/nm-test.rb +21 -0
  22. data/examples/no-introspect/tracker-test.rb +16 -0
  23. data/examples/rhythmbox/playpause.rb +25 -0
  24. data/examples/service/call_service.rb +25 -0
  25. data/examples/service/service_newapi.rb +51 -0
  26. data/examples/simple/call_introspect.rb +34 -0
  27. data/examples/simple/properties.rb +19 -0
  28. data/examples/utils/listnames.rb +11 -0
  29. data/examples/utils/notify.rb +19 -0
  30. data/lib/dbus.rb +82 -0
  31. data/lib/dbus/auth.rb +269 -0
  32. data/lib/dbus/bus.rb +739 -0
  33. data/lib/dbus/core_ext/array/extract_options.rb +31 -0
  34. data/lib/dbus/core_ext/class/attribute.rb +129 -0
  35. data/lib/dbus/core_ext/kernel/singleton_class.rb +8 -0
  36. data/lib/dbus/core_ext/module/remove_method.rb +14 -0
  37. data/lib/dbus/error.rb +46 -0
  38. data/lib/dbus/export.rb +128 -0
  39. data/lib/dbus/introspect.rb +219 -0
  40. data/lib/dbus/logger.rb +31 -0
  41. data/lib/dbus/loop-em.rb +19 -0
  42. data/lib/dbus/marshall.rb +434 -0
  43. data/lib/dbus/matchrule.rb +101 -0
  44. data/lib/dbus/message.rb +276 -0
  45. data/lib/dbus/message_queue.rb +166 -0
  46. data/lib/dbus/proxy_object.rb +149 -0
  47. data/lib/dbus/proxy_object_factory.rb +41 -0
  48. data/lib/dbus/proxy_object_interface.rb +128 -0
  49. data/lib/dbus/type.rb +193 -0
  50. data/lib/dbus/xml.rb +161 -0
  51. data/test/async_spec.rb +47 -0
  52. data/test/binding_spec.rb +74 -0
  53. data/test/bus_and_xml_backend_spec.rb +39 -0
  54. data/test/bus_driver_spec.rb +20 -0
  55. data/test/bus_spec.rb +20 -0
  56. data/test/byte_array_spec.rb +38 -0
  57. data/test/err_msg_spec.rb +42 -0
  58. data/test/introspect_xml_parser_spec.rb +26 -0
  59. data/test/introspection_spec.rb +32 -0
  60. data/test/main_loop_spec.rb +82 -0
  61. data/test/property_spec.rb +53 -0
  62. data/test/server_robustness_spec.rb +66 -0
  63. data/test/server_spec.rb +53 -0
  64. data/test/service_newapi.rb +217 -0
  65. data/test/session_bus_spec_manual.rb +15 -0
  66. data/test/signal_spec.rb +90 -0
  67. data/test/spec_helper.rb +33 -0
  68. data/test/thread_safety_spec.rb +31 -0
  69. data/test/tools/dbus-launch-simple +35 -0
  70. data/test/tools/dbus-limited-session.conf +28 -0
  71. data/test/tools/test_env +13 -0
  72. data/test/tools/test_server +39 -0
  73. data/test/type_spec.rb +19 -0
  74. data/test/value_spec.rb +81 -0
  75. data/test/variant_spec.rb +66 -0
  76. metadata +145 -0
@@ -0,0 +1,149 @@
1
+ # This file is part of the ruby-dbus project
2
+ # Copyright (C) 2007 Arnaud Cornet and Paul van Tilburg
3
+ # Copyright (C) 2009-2014 Martin Vidner
4
+ #
5
+ # This library is free software; you can redistribute it and/or
6
+ # modify it under the terms of the GNU Lesser General Public
7
+ # License, version 2.1 as published by the Free Software Foundation.
8
+ # See the file "COPYING" for the exact licensing terms.
9
+
10
+ module DBus
11
+ # D-Bus proxy object class
12
+ #
13
+ # Class representing a remote object in an external application.
14
+ # Typically, calling a method on an instance of a ProxyObject sends a message
15
+ # over the bus so that the method is executed remotely on the correctponding
16
+ # object.
17
+ class ProxyObject
18
+ # The names of direct subnodes of the object in the tree.
19
+ attr_accessor :subnodes
20
+ # Flag determining whether the object has been introspected.
21
+ attr_accessor :introspected
22
+ # The (remote) destination of the object.
23
+ attr_reader :destination
24
+ # The path to the object.
25
+ attr_reader :path
26
+ # The bus the object is reachable via.
27
+ attr_reader :bus
28
+ # @return [String] The name of the default interface of the object.
29
+ attr_accessor :default_iface
30
+
31
+ # Creates a new proxy object living on the given _bus_ at destination _dest_
32
+ # on the given _path_.
33
+ def initialize(bus, dest, path)
34
+ @bus, @destination, @path = bus, dest, path
35
+ @interfaces = Hash.new
36
+ @subnodes = Array.new
37
+ end
38
+
39
+ # Returns the interfaces of the object.
40
+ def interfaces
41
+ @interfaces.keys
42
+ end
43
+
44
+ # Retrieves an interface of the proxy object
45
+ # @param [String] intfname
46
+ # @return [ProxyObjectInterface]
47
+ def [](intfname)
48
+ @interfaces[intfname]
49
+ end
50
+
51
+ # Maps the given interface name _intfname_ to the given interface _intf.
52
+ # @param [String] intfname
53
+ # @param [ProxyObjectInterface] intf
54
+ # @return [ProxyObjectInterface]
55
+ def []=(intfname, intf)
56
+ @interfaces[intfname] = intf
57
+ end
58
+
59
+ # Introspects the remote object. Allows you to find and select
60
+ # interfaces on the object.
61
+ def introspect
62
+ # Synchronous call here.
63
+ xml = @bus.introspect_data(@destination, @path)
64
+ ProxyObjectFactory.introspect_into(self, xml)
65
+ define_shortcut_methods()
66
+ xml
67
+ end
68
+
69
+ # For each non duplicated method name in any interface present on the
70
+ # caller, defines a shortcut method dynamically.
71
+ # This function is automatically called when a {ProxyObject} is
72
+ # introspected.
73
+ def define_shortcut_methods
74
+ # builds a list of duplicated methods
75
+ dup_meths, univocal_meths = [],{}
76
+ @interfaces.each_value do |intf|
77
+ intf.methods.each_value do |meth|
78
+ name = meth.name.to_sym
79
+ # don't overwrite instance methods!
80
+ if dup_meths.include? name or self.class.instance_methods.include? name
81
+ next
82
+ elsif univocal_meths.include? name
83
+ univocal_meths.delete name
84
+ dup_meths << name
85
+ else
86
+ univocal_meths[name] = intf
87
+ end
88
+ end
89
+ end
90
+ univocal_meths.each do |name, intf|
91
+ # creates a shortcut function that forwards each call to the method on
92
+ # the appropriate intf
93
+ singleton_class.class_eval do
94
+ define_method name do |*args, &reply_handler|
95
+ intf.method(name).call(*args, &reply_handler)
96
+ end
97
+ end
98
+ end
99
+ end
100
+
101
+ # Returns whether the object has an interface with the given _name_.
102
+ def has_iface?(name)
103
+ raise "Cannot call has_iface? if not introspected" if not @introspected
104
+ @interfaces.member?(name)
105
+ end
106
+
107
+ # Registers a handler, the code block, for a signal with the given _name_.
108
+ # It uses _default_iface_ which must have been set.
109
+ # @return [void]
110
+ def on_signal(name, &block)
111
+ if @default_iface and has_iface?(@default_iface)
112
+ @interfaces[@default_iface].on_signal(name, &block)
113
+ else
114
+ # TODO improve
115
+ raise NoMethodError
116
+ end
117
+ end
118
+
119
+ ####################################################
120
+ private
121
+
122
+ # Handles all unkown methods, mostly to route method calls to the
123
+ # default interface.
124
+ def method_missing(name, *args, &reply_handler)
125
+ if @default_iface and has_iface?(@default_iface)
126
+ begin
127
+ @interfaces[@default_iface].method(name).call(*args, &reply_handler)
128
+ rescue NameError => e
129
+ # interesting, foo.method("unknown")
130
+ # raises NameError, not NoMethodError
131
+ raise unless e.to_s =~ /undefined method `#{name}'/
132
+ # BTW e.exception("...") would preserve the class.
133
+ raise NoMethodError,"undefined method `#{name}' for DBus interface `#{@default_iface}' on object `#{@path}'"
134
+ end
135
+ else
136
+ # TODO distinguish:
137
+ # - di not specified
138
+ #TODO
139
+ # - di is specified but not found in introspection data
140
+ raise NoMethodError, "undefined method `#{name}' for DBus interface `#{@default_iface}' on object `#{@path}'"
141
+ end
142
+ end
143
+
144
+ # Returns the singleton class of the object.
145
+ def singleton_class
146
+ (class << self ; self ; end)
147
+ end
148
+ end # class ProxyObject
149
+ end
@@ -0,0 +1,41 @@
1
+ # This file is part of the ruby-dbus project
2
+ # Copyright (C) 2007 Arnaud Cornet and Paul van Tilburg
3
+ # Copyright (C) 2009-2014 Martin Vidner
4
+ #
5
+ # This library is free software; you can redistribute it and/or
6
+ # modify it under the terms of the GNU Lesser General Public
7
+ # License, version 2.1 as published by the Free Software Foundation.
8
+ # See the file "COPYING" for the exact licensing terms.
9
+
10
+ module DBus
11
+ # = D-Bus proxy object factory class
12
+ #
13
+ # Class that generates and sets up a proxy object based on introspection data.
14
+ class ProxyObjectFactory
15
+ # Creates a new proxy object factory for the given introspection XML _xml_,
16
+ # _bus_, destination _dest_, and _path_.
17
+ def initialize(xml, bus, dest, path)
18
+ @xml, @bus, @path, @dest = xml, bus, path, dest
19
+ end
20
+
21
+ # Investigates the sub-nodes of the proxy object _po_ based on the
22
+ # introspection XML data _xml_ and sets them up recursively.
23
+ def ProxyObjectFactory.introspect_into(po, xml)
24
+ intfs, po.subnodes = IntrospectXMLParser.new(xml).parse
25
+ intfs.each do |i|
26
+ poi = ProxyObjectInterface.new(po, i.name)
27
+ i.methods.each_value { |m| poi.define(m) }
28
+ i.signals.each_value { |s| poi.define(s) }
29
+ po[i.name] = poi
30
+ end
31
+ po.introspected = true
32
+ end
33
+
34
+ # Generates, sets up and returns the proxy object.
35
+ def build
36
+ po = ProxyObject.new(@bus, @dest, @path)
37
+ ProxyObjectFactory.introspect_into(po, @xml)
38
+ po
39
+ end
40
+ end # class ProxyObjectFactory
41
+ end
@@ -0,0 +1,128 @@
1
+ # This file is part of the ruby-dbus project
2
+ # Copyright (C) 2007 Arnaud Cornet and Paul van Tilburg
3
+ # Copyright (C) 2009-2014 Martin Vidner
4
+ #
5
+ # This library is free software; you can redistribute it and/or
6
+ # modify it under the terms of the GNU Lesser General Public
7
+ # License, version 2.1 as published by the Free Software Foundation.
8
+ # See the file "COPYING" for the exact licensing terms.
9
+
10
+ module DBus
11
+ # = D-Bus proxy object interface class
12
+ #
13
+ # A class similar to the normal Interface used as a proxy for remote
14
+ # object interfaces.
15
+ class ProxyObjectInterface
16
+ # The proxied methods contained in the interface.
17
+ attr_accessor :methods
18
+ # The proxied signals contained in the interface.
19
+ attr_accessor :signals
20
+ # The proxy object to which this interface belongs.
21
+ attr_reader :object
22
+ # The name of the interface.
23
+ attr_reader :name
24
+
25
+ # Creates a new proxy interface for the given proxy _object_
26
+ # and the given _name_.
27
+ def initialize(object, name)
28
+ @object, @name = object, name
29
+ @methods, @signals = Hash.new, Hash.new
30
+ end
31
+
32
+ # Returns the string representation of the interface (the name).
33
+ def to_str
34
+ @name
35
+ end
36
+
37
+ # Returns the singleton class of the interface.
38
+ def singleton_class
39
+ (class << self ; self ; end)
40
+ end
41
+
42
+ # Defines a method on the interface from the Method descriptor _m_.
43
+ def define_method_from_descriptor(m)
44
+ m.params.each do |fpar|
45
+ par = fpar.type
46
+ # This is the signature validity check
47
+ Type::Parser.new(par).parse
48
+ end
49
+
50
+ singleton_class.class_eval do
51
+ define_method m.name do |*args, &reply_handler|
52
+ if m.params.size != args.size
53
+ raise ArgumentError, "wrong number of arguments (#{args.size} for #{m.params.size})"
54
+ end
55
+
56
+ msg = Message.new(Message::METHOD_CALL)
57
+ msg.path = @object.path
58
+ msg.interface = @name
59
+ msg.destination = @object.destination
60
+ msg.member = m.name
61
+ msg.sender = @object.bus.unique_name
62
+ m.params.each do |fpar|
63
+ par = fpar.type
64
+ msg.add_param(par, args.shift)
65
+ end
66
+ @object.bus.send_sync_or_async(msg, &reply_handler)
67
+ end
68
+ end
69
+
70
+ @methods[m.name] = m
71
+ end
72
+
73
+ # Defines a signal from the descriptor _s_.
74
+ def define_signal_from_descriptor(s)
75
+ @signals[s.name] = s
76
+ end
77
+
78
+ # Defines a signal or method based on the descriptor _m_.
79
+ def define(m)
80
+ if m.kind_of?(Method)
81
+ define_method_from_descriptor(m)
82
+ elsif m.kind_of?(Signal)
83
+ define_signal_from_descriptor(m)
84
+ end
85
+ end
86
+
87
+ # Defines a proxied method on the interface.
88
+ def define_method(methodname, prototype)
89
+ m = Method.new(methodname)
90
+ m.from_prototype(prototype)
91
+ define(m)
92
+ end
93
+
94
+ # @overload on_signal(name, &block)
95
+ # @overload on_signal(bus, name, &block)
96
+ # Registers a handler (code block) for a signal with _name_ arriving
97
+ # over the given _bus_. If no block is given, the signal is unregistered.
98
+ # Note that specifying _bus_ is discouraged and the option is kept only for
99
+ # backward compatibility.
100
+ # @return [void]
101
+ def on_signal(bus = @object.bus, name, &block)
102
+ mr = DBus::MatchRule.new.from_signal(self, name)
103
+ if block.nil?
104
+ bus.remove_match(mr)
105
+ else
106
+ bus.add_match(mr) { |msg| block.call(*msg.params) }
107
+ end
108
+ end
109
+
110
+ PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties"
111
+
112
+ # Read a property.
113
+ def [](propname)
114
+ self.object[PROPERTY_INTERFACE].Get(self.name, propname)[0]
115
+ end
116
+
117
+ # Write a property.
118
+ def []=(propname, value)
119
+ self.object[PROPERTY_INTERFACE].Set(self.name, propname, value)
120
+ end
121
+
122
+ # Read all properties at once, as a hash.
123
+ # @return [Hash{String}]
124
+ def all_properties
125
+ self.object[PROPERTY_INTERFACE].GetAll(self.name)[0]
126
+ end
127
+ end # class ProxyObjectInterface
128
+ end
data/lib/dbus/type.rb ADDED
@@ -0,0 +1,193 @@
1
+ # dbus/type.rb - module containing low-level D-Bus data type information
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
+
13
+ # = D-Bus type module
14
+ #
15
+ # This module containts the constants of the types specified in the D-Bus
16
+ # protocol.
17
+ module Type
18
+ # Mapping from type number to name and alignment.
19
+ TypeMapping = {
20
+ 0 => ["INVALID", nil],
21
+ ?y => ["BYTE", 1],
22
+ ?b => ["BOOLEAN", 4],
23
+ ?n => ["INT16", 2],
24
+ ?q => ["UINT16", 2],
25
+ ?i => ["INT32", 4],
26
+ ?u => ["UINT32", 4],
27
+ ?x => ["INT64", 8],
28
+ ?t => ["UINT64", 8],
29
+ ?d => ["DOUBLE", 8],
30
+ ?r => ["STRUCT", 8],
31
+ ?a => ["ARRAY", 4],
32
+ ?v => ["VARIANT", 1],
33
+ ?o => ["OBJECT_PATH", 4],
34
+ ?s => ["STRING", 4],
35
+ ?g => ["SIGNATURE", 1],
36
+ ?e => ["DICT_ENTRY", 8],
37
+ ?h => ["UNIX_FD", 4],
38
+ }
39
+ # Defines the set of constants
40
+ TypeMapping.each_pair do |key, value|
41
+ Type.const_set(value.first, key)
42
+ end
43
+
44
+ # Exception raised when an unknown/incorrect type is encountered.
45
+ class SignatureException < Exception
46
+ end
47
+
48
+ # = D-Bus type conversion class
49
+ #
50
+ # Helper class for representing a D-Bus type.
51
+ class Type
52
+ # Returns the signature type number.
53
+ attr_reader :sigtype
54
+ # Return contained member types.
55
+ attr_reader :members
56
+
57
+ # Create a new type instance for type number _sigtype_.
58
+ def initialize(sigtype)
59
+ if not TypeMapping.keys.member?(sigtype)
60
+ raise SignatureException, "Unknown key in signature: #{sigtype.chr}"
61
+ end
62
+ @sigtype = sigtype
63
+ @members = Array.new
64
+ end
65
+
66
+ # Return the required alignment for the type.
67
+ def alignment
68
+ TypeMapping[@sigtype].last
69
+ end
70
+
71
+ # Return a string representation of the type according to the
72
+ # D-Bus specification.
73
+ def to_s
74
+ case @sigtype
75
+ when STRUCT
76
+ "(" + @members.collect { |t| t.to_s }.join + ")"
77
+ when ARRAY
78
+ "a" + child.to_s
79
+ when DICT_ENTRY
80
+ "{" + @members.collect { |t| t.to_s }.join + "}"
81
+ else
82
+ if not TypeMapping.keys.member?(@sigtype)
83
+ raise NotImplementedError
84
+ end
85
+ @sigtype.chr
86
+ end
87
+ end
88
+
89
+ # Add a new member type _a_.
90
+ def <<(a)
91
+ if not [STRUCT, ARRAY, DICT_ENTRY].member?(@sigtype)
92
+ raise SignatureException
93
+ end
94
+ raise SignatureException if @sigtype == ARRAY and @members.size > 0
95
+ if @sigtype == DICT_ENTRY
96
+ if @members.size == 2
97
+ raise SignatureException, "Dict entries have exactly two members"
98
+ end
99
+ if @members.size == 0
100
+ if [STRUCT, ARRAY, DICT_ENTRY].member?(a.sigtype)
101
+ raise SignatureException, "Dict entry keys must be basic types"
102
+ end
103
+ end
104
+ end
105
+ @members << a
106
+ end
107
+
108
+ # Return the first contained member type.
109
+ def child
110
+ @members[0]
111
+ end
112
+
113
+ def inspect
114
+ s = TypeMapping[@sigtype].first
115
+ if [STRUCT, ARRAY].member?(@sigtype)
116
+ s += ": " + @members.inspect
117
+ end
118
+ s
119
+ end
120
+ end # class Type
121
+
122
+ # = D-Bus type parser class
123
+ #
124
+ # Helper class to parse a type signature in the protocol.
125
+ class Parser
126
+ # Create a new parser for the given _signature_.
127
+ def initialize(signature)
128
+ @signature = signature
129
+ @idx = 0
130
+ end
131
+
132
+ # Returns the next character from the signature.
133
+ def nextchar
134
+ c = @signature[@idx]
135
+ @idx += 1
136
+ c
137
+ end
138
+
139
+ # Parse one character _c_ of the signature.
140
+ def parse_one(c)
141
+ res = nil
142
+ case c
143
+ when ?a
144
+ res = Type.new(ARRAY)
145
+ c = nextchar
146
+ raise SignatureException, "Parse error in #{@signature}" if c == nil
147
+ child = parse_one(c)
148
+ res << child
149
+ when ?(
150
+ res = Type.new(STRUCT)
151
+ while (c = nextchar) != nil and c != ?)
152
+ res << parse_one(c)
153
+ end
154
+ raise SignatureException, "Parse error in #{@signature}" if c == nil
155
+ when ?{
156
+ res = Type.new(DICT_ENTRY)
157
+ while (c = nextchar) != nil and c != ?}
158
+ res << parse_one(c)
159
+ end
160
+ raise SignatureException, "Parse error in #{@signature}" if c == nil
161
+ else
162
+ res = Type.new(c)
163
+ end
164
+ res
165
+ end
166
+
167
+ # Parse the entire signature, return a DBus::Type object.
168
+ def parse
169
+ @idx = 0
170
+ ret = Array.new
171
+ while (c = nextchar)
172
+ ret << parse_one(c)
173
+ end
174
+ ret
175
+ end
176
+ end # class Parser
177
+ end # module Type
178
+
179
+ # shortcuts
180
+
181
+ # Parse a String to a DBus::Type::Type
182
+ def type(string_type)
183
+ Type::Parser.new(string_type).parse[0]
184
+ end
185
+ module_function :type
186
+
187
+ # Make an explicit [Type, value] pair
188
+ def variant(string_type, value)
189
+ [type(string_type), value]
190
+ end
191
+ module_function :variant
192
+
193
+ end # module DBus