dbus 0.1.5

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.
@@ -0,0 +1,98 @@
1
+ /*--------------------------------------------------
2
+ * D-BUS bindings for Ruby
3
+ * (C) Copyright 2004 Leon Breedt
4
+ * (C) Copyright 2004 Provenco Group Ltd
5
+ *
6
+ * Licensed under the same terms as the D-BUS library
7
+ *--------------------------------------------------*/
8
+
9
+ #include "ruby-dbus.h"
10
+
11
+ VALUE cDBusPendingCall;
12
+
13
+ RDBUS_GENERATE_NEW(cDBusPendingCall, DBusPendingCall *, dbus_pending_call_unref)
14
+ RDBUS_GENERATE_GET(DBusPendingCall)
15
+
16
+ /*
17
+ * call-seq:
18
+ * cancel => nil
19
+ *
20
+ * Cancels the pending call, any reply or error received will be
21
+ * ignored.
22
+ */
23
+ static VALUE
24
+ cDBusPendingCall_cancel(VALUE self)
25
+ {
26
+ dbus_pending_call_cancel(CALL_GET(self));
27
+ return Qnil;
28
+ }
29
+
30
+ /*
31
+ * call-seq:
32
+ * get_completed => true|false
33
+ *
34
+ * Returns +true+ if the pending call has received a reply.
35
+ */
36
+ static VALUE
37
+ cDBusPendingCall_get_completed(VALUE self)
38
+ {
39
+ if (dbus_pending_call_get_completed(CALL_GET(self)))
40
+ return Qtrue;
41
+ return Qfalse;
42
+ }
43
+
44
+ /*
45
+ * call-seq:
46
+ * get_reply => message
47
+ *
48
+ * Returns the reply DBusMessage, or +nil+ if no reply
49
+ * has been received yet.
50
+ */
51
+ static VALUE
52
+ cDBusPendingCall_get_reply(VALUE self)
53
+ {
54
+ DBusMessage *msg = NULL;
55
+
56
+ msg = dbus_pending_call_get_reply(CALL_GET(self));
57
+ if (msg != NULL)
58
+ return RDBUS_NEW(cDBusMessage, msg);
59
+ return Qnil;
60
+ }
61
+
62
+ /*
63
+ * call-seq:
64
+ * block => nil
65
+ *
66
+ * Blocks until the pending call is completed and a reply is
67
+ * available.
68
+ *
69
+ * The blocking behaviour is the same as that for
70
+ * DBusConnection#send_with_reply_and_block.
71
+ */
72
+ static VALUE
73
+ cDBusPendingCall_block(VALUE self)
74
+ {
75
+ dbus_pending_call_block(CALL_GET(self));
76
+ return Qnil;
77
+ }
78
+
79
+ /*
80
+ * Document-class: DBus::Binding::DBusPendingCall
81
+ *
82
+ * A DBusPendingCall is used to retrieve asynchronous replies to
83
+ * method call messages.
84
+ *
85
+ * See DBusConnection#send_with_reply.
86
+ */
87
+ void
88
+ Init_dbus_pending_call()
89
+ {
90
+ cDBusPendingCall = rb_define_class_under(mDBusBinding, "DBusPendingCall", rb_cObject);
91
+
92
+ rb_define_singleton_method(cDBusPendingCall, "new", rdbus_private_method, 0);
93
+
94
+ rb_define_method(cDBusPendingCall, "cancel", cDBusPendingCall_cancel, 0);
95
+ rb_define_method(cDBusPendingCall, "get_completed", cDBusPendingCall_get_completed, 0);
96
+ rb_define_method(cDBusPendingCall, "get_reply", cDBusPendingCall_get_reply, 0);
97
+ rb_define_method(cDBusPendingCall, "block", cDBusPendingCall_block, 0);
98
+ }
@@ -0,0 +1,126 @@
1
+ /*--------------------------------------------------
2
+ * D-BUS bindings for Ruby
3
+ * (C) Copyright 2004 Leon Breedt
4
+ * (C) Copyright 2004 Provenco Group Ltd
5
+ *
6
+ * Licensed under the same terms as the D-BUS library
7
+ *--------------------------------------------------*/
8
+
9
+ #include "ruby-dbus.h"
10
+
11
+ static void _server_free(DBusServer *server);
12
+
13
+ VALUE cDBusServer;
14
+
15
+ RDBUS_GENERATE_NEW(cDBusServer, DBusServer *, _server_free)
16
+ RDBUS_GENERATE_GET(DBusServer)
17
+
18
+ static void
19
+ _server_free(DBusServer *server)
20
+ {
21
+ dbus_server_disconnect(server);
22
+ dbus_server_unref(server);
23
+ }
24
+
25
+ /*
26
+ * call-seq:
27
+ * listen(address) => server
28
+ *
29
+ * Creates a new server instance listening for connections on the given
30
+ * address.
31
+ *
32
+ * See DBusConnection#new for details on the format of the address.
33
+ */
34
+ static VALUE
35
+ cDBusServer_listen(VALUE klass, VALUE address)
36
+ {
37
+ DBusServer *server = NULL;
38
+
39
+ RDBUS_TRY(server = dbus_server_listen(StringValuePtr(address), &error));
40
+ RDBUS_RAISE_IF(server == NULL, "failed to create DBusServer");
41
+ return RDBUS_NEW(cDBusServer, server);
42
+ }
43
+
44
+ /*
45
+ * call-seq:
46
+ * disconnect => nil
47
+ *
48
+ * Releases the server's address and stops listening for new clients.
49
+ */
50
+ static VALUE
51
+ cDBusServer_disconnect(VALUE self)
52
+ {
53
+ dbus_server_disconnect(SERVER_GET(self));
54
+ return Qnil;
55
+ }
56
+
57
+ /*
58
+ * call-seq:
59
+ * get_is_connected => true|false
60
+ *
61
+ * Returns +true+ if the server is still listening for new clients.
62
+ */
63
+ static VALUE
64
+ cDBusServer_get_is_connected(VALUE self)
65
+ {
66
+ if (dbus_server_get_is_connected(SERVER_GET(self)))
67
+ return Qtrue;
68
+ return Qfalse;
69
+ }
70
+
71
+ /*
72
+ * call-seq:
73
+ * get_address => address
74
+ *
75
+ * Returns the address the server is listening on.
76
+ */
77
+ static VALUE
78
+ cDBusServer_get_address(VALUE self)
79
+ {
80
+ char *address = NULL;
81
+ VALUE address_r;
82
+
83
+ address = dbus_server_get_address(SERVER_GET(self));
84
+ if (address == NULL)
85
+ return Qnil;
86
+ address_r = rb_str_new2(address);
87
+ dbus_free(address);
88
+ return address_r;
89
+ }
90
+
91
+ /*
92
+ * call-seq:
93
+ * setup_with_g_main => nil
94
+ *
95
+ * Sets up the dispatching of the server to integrate with the GLIB main
96
+ * loop.
97
+ */
98
+ static VALUE
99
+ cDBusServer_setup_with_g_main(VALUE self)
100
+ {
101
+ dbus_server_setup_with_g_main(SERVER_GET(self), NULL);
102
+ return Qnil;
103
+ }
104
+
105
+ /*
106
+ * Document-class: DBus::Binding::DBusServer
107
+ *
108
+ * A DBusServer listens for and handles connections from clients.
109
+ *
110
+ * This class isn't particularly useful right now, as it does not provide new
111
+ * connection, watch, and timeout callback functionality yet.
112
+ */
113
+ void
114
+ Init_dbus_server()
115
+ {
116
+ cDBusServer = rb_define_class_under(mDBusBinding, "DBusServer", rb_cObject);
117
+
118
+ rb_define_singleton_method(cDBusServer, "new", cDBusServer_listen, 1);
119
+ rb_define_singleton_method(cDBusServer, "listen", cDBusServer_listen, 1);
120
+
121
+ rb_define_method(cDBusServer, "disconnect", cDBusServer_disconnect, 0);
122
+ rb_define_method(cDBusServer, "get_is_connected", cDBusServer_get_is_connected, 0);
123
+ rb_define_method(cDBusServer, "get_address", cDBusServer_get_address, 0);
124
+
125
+ rb_define_method(cDBusServer, "setup_with_g_main", cDBusServer_setup_with_g_main, 0);
126
+ }
@@ -0,0 +1,127 @@
1
+ /*---------------------------------------------------
2
+ *
3
+ * D-BUS bindings for Ruby
4
+ * (C) Copyright 2004 Provenco Group Ltd
5
+ *
6
+ * Licensed under the same terms as the D-BUS library
7
+ *
8
+ *--------------------------------------------------*/
9
+
10
+ #ifndef RUBY_DBUS_H
11
+ #define RUBY_DBUS_H
12
+
13
+
14
+ #define DBUS_API_SUBJECT_TO_CHANGE 1
15
+ #include <dbus/dbus.h>
16
+ #include <dbus/dbus-glib.h>
17
+ #include "ruby.h"
18
+
19
+ #define RDBUS_GENERATE_PROTO_NEW(klass, ctype) \
20
+ VALUE _rdbus_ ##klass## _new_from(ctype obj)
21
+
22
+ #define RDBUS_GENERATE_PROTO_GET(ctype) \
23
+ ctype * _rdbus_ ##ctype## _get_wrapped(VALUE value)
24
+
25
+ #define RDBUS_GENERATE_NEW(klass, ctype, free) \
26
+ RDBUS_GENERATE_NEW_WITH_MARK(klass, ctype, NULL, free)
27
+
28
+ #define RDBUS_GENERATE_NEW_WITH_MARK(klass, ctype, mark, free) \
29
+ RDBUS_GENERATE_PROTO_NEW(klass, ctype) \
30
+ { \
31
+ return Data_Wrap_Struct(klass, mark, free, obj); \
32
+ }
33
+
34
+ #define RDBUS_GENERATE_GET(ctype) \
35
+ RDBUS_GENERATE_PROTO_GET(ctype) \
36
+ { \
37
+ ctype *obj = NULL; \
38
+ Data_Get_Struct(value, ctype, obj); \
39
+ if (obj == NULL) \
40
+ rb_raise(eDBusError, "failed to retrieve " #ctype); \
41
+ return obj; \
42
+ }
43
+
44
+ #define RDBUS_TRY_ERR(expr, klass) \
45
+ do { \
46
+ DBusError error; \
47
+ dbus_error_init(&error); \
48
+ (expr); \
49
+ if (dbus_error_is_set(&error)) \
50
+ rb_raise(klass, error.message); \
51
+ } while (0);
52
+
53
+ #define RDBUS_TRY(expr) \
54
+ RDBUS_TRY_ERR((expr), eDBusError)
55
+
56
+ #define RDBUS_NEW(klass, obj) \
57
+ _rdbus_ ##klass## _new_from(obj)
58
+
59
+ #define RDBUS_GET(ctype, value) \
60
+ _rdbus_ ##ctype## _get_wrapped(value)
61
+
62
+ #define RDBUS_RAISE_IF(cond, message) \
63
+ if (cond) rb_raise(eDBusError, message)
64
+
65
+
66
+
67
+ #define CONN_GET(obj) \
68
+ RDBUS_GET(DBusConnection, obj)
69
+
70
+ #define MSG_GET(obj) \
71
+ (RDBUS_GET(DBusMessage, obj))
72
+
73
+ #define ITER_GET(obj) \
74
+ (RDBUS_GET(RubyDBusMessageIter, obj)->real_iter)
75
+
76
+ #define CALL_GET(obj) \
77
+ (RDBUS_GET(DBusPendingCall, obj))
78
+
79
+ #define SERVER_GET(obj) \
80
+ (RDBUS_GET(DBusServer, obj))
81
+
82
+
83
+
84
+ extern VALUE mDBus;
85
+ extern VALUE mDBusBinding;
86
+ extern VALUE cDBusConnection;
87
+ extern VALUE cDBusServer;
88
+ extern VALUE cDBusMessage;
89
+ extern VALUE cDBusMessageIter;
90
+ extern VALUE cDBusPendingCall;
91
+ extern VALUE eDBusError;
92
+
93
+ typedef struct _RubyDBusMessageIter RubyDBusMessageIter;
94
+
95
+
96
+
97
+ void Init_dbus_bus();
98
+ void Init_dbus_connection();
99
+ void Init_dbus_message();
100
+ void Init_dbus_message_iter();
101
+ void Init_dbus_pending_call();
102
+ void Init_dbus_server();
103
+
104
+ RDBUS_GENERATE_PROTO_NEW(cDBusConnection, DBusConnection *);
105
+ RDBUS_GENERATE_PROTO_NEW(cDBusMessage, DBusMessage *);
106
+ RDBUS_GENERATE_PROTO_NEW(cDBusMessageIter, RubyDBusMessageIter *);
107
+ RDBUS_GENERATE_PROTO_NEW(cDBusPendingCall, DBusPendingCall *);
108
+ RDBUS_GENERATE_PROTO_NEW(cDBusServer, DBusServer *);
109
+
110
+ RDBUS_GENERATE_PROTO_GET(DBusConnection);
111
+ RDBUS_GENERATE_PROTO_GET(DBusMessage);
112
+ RDBUS_GENERATE_PROTO_GET(DBusMessageIter);
113
+ RDBUS_GENERATE_PROTO_GET(DBusPendingCall);
114
+ RDBUS_GENERATE_PROTO_GET(DBusServer);
115
+
116
+ RubyDBusMessageIter *rdbus_message_iter_new (DBusMessage *msg, short is_append_iter);
117
+ VALUE rdbus_private_method (VALUE klass);
118
+
119
+
120
+ /* --- Externals (copied from Python bindings) --- */
121
+ void dbus_connection_setup_with_g_main (DBusConnection *connection,
122
+ GMainContext *context);
123
+ void dbus_server_setup_with_g_main (DBusServer *server,
124
+ GMainContext *context);
125
+ void dbus_g_thread_init ();
126
+
127
+ #endif
@@ -0,0 +1,359 @@
1
+ #-----------------------------------------------------
2
+ # D-BUS bindings for Ruby
3
+ # (C) Copyright 2004 Leon Breedt
4
+ # (C) Copyright 2004 Provenco Group Ltd
5
+ #
6
+ # Licensed under the same terms as the D-BUS library
7
+ #-----------------------------------------------------
8
+
9
+ require 'dbus.so'
10
+ require 'dbus/version'
11
+ require 'dbus/binding'
12
+
13
+ module DBus
14
+
15
+ # Represents a connection to a DBus daemon
16
+ class Bus
17
+ TYPE_SESSION = BUS_SESSION
18
+ TYPE_SYSTEM = BUS_SYSTEM
19
+ TYPE_ACTIVATION = BUS_ACTIVATION
20
+
21
+ def initialize(bus_type=TYPE_SESSION, glib_mainloop=true)
22
+ @connection = DBus::Binding::bus_get(bus_type)
23
+ @connection.add_filter(method(:signal_dispatcher))
24
+ @rules = {}
25
+ @connection.setup_with_g_main if glib_mainloop
26
+ end
27
+
28
+ def get_connection
29
+ @connection
30
+ end
31
+
32
+ def get_service(name="org.freedesktop.Broadcast")
33
+ RemoteService.new(self, name)
34
+ end
35
+
36
+ # Add a handler +handler_proc+ for the match rule resulting from
37
+ # passing the parameters to Bus#build_signal_rule. Multiple handlers
38
+ # can be installed for the same signal.
39
+ def add_signal_receiver(handler_proc, signal_name=nil, interface=nil, service=nil, path=nil)
40
+ rule = build_signal_rule(signal_name, interface, service, path)
41
+ @rules[rule] ||= []
42
+ @rules[rule] << handler_proc unless @rules[rule].include?(handler_proc)
43
+ DBus::Binding::bus_add_match(@connection, rule)
44
+ end
45
+
46
+ # Remove the handler +handler_proc+ for a match rule previously added using
47
+ # Bus#add_signal_receiver.
48
+ def remove_signal_receiver(handler_proc, signal_name=nil, interface=nil, service=nil, path=nil)
49
+ rule = build_signal_rule(signal_name, interface, service, path)
50
+ return nil unless @rules.has_key?(rule)
51
+ @rules[rule].remove(handler_proc)
52
+ DBus::Binding::bus_remove_match(@conn, rule)
53
+ end
54
+
55
+ # Generate a match rule for a signal with the specified arguments. If any of
56
+ # the method arguments is +nil+, its processing (below) is skipped.
57
+ #
58
+ # - The rule "type" is set to "signal"
59
+ # - The rule "interface" is set to the value of the +interface+ argument
60
+ # - The rule "sender" is set as follows: If the given +service+ argument does not
61
+ # start with ":", and is not "org.freedesktop.DBus", the "sender" value
62
+ # is set to the owner of the supplied +service+ argument (determined with
63
+ # a +GetServiceOwner+ call), otherwise, the +service+ value is used
64
+ # - The "path" parameter is set to the value of the +path+ argument
65
+ # - The "member" parameter is set to the value of the +signal_name+ argument
66
+ #
67
+ def build_signal_rule(signal_name, interface, service, path)
68
+ rule = "type='signal'"
69
+ if interface
70
+ rule += ",interface='%s'" % interface
71
+ end
72
+ if service
73
+ if service[0,1] != ":" && service != "org.freedesktop.DBus"
74
+ bus_service = get_service("org.freedesktop.DBus")
75
+ bus_object = bus_service.get_object("/org/freedesktop/DBus",
76
+ "org.freedesktop.DBus")
77
+ service = bus_object.GetServiceOwner(service)
78
+ end
79
+ rule += ",sender='%s'" % service
80
+ end
81
+ if path
82
+ rule += ",path='%s'" % path
83
+ end
84
+ if signal_name
85
+ rule += ",member='%s'" % signal_name
86
+ end
87
+ rule
88
+ end
89
+
90
+ protected
91
+ def signal_dispatcher(connection, message)
92
+ return HANDLER_RESULT_NOT_YET_HANDLED unless message.get_type == MESSAGE_TYPE_SIGNAL
93
+ interface = message.get_interface
94
+ service = message.get_sender
95
+ path = message.get_path
96
+ member = message.get_member
97
+ rule = build_signal_rule(member, interface, service, path)
98
+ if @rules.has_key?(rule)
99
+ args = [interface, member, service, path, message]
100
+ @rules[rule].each {|f| f.call(*args)}
101
+ end
102
+ HANDLER_RESULT_HANDLED
103
+ end
104
+ end
105
+
106
+ # The system-wide message bus
107
+ class SystemBus < Bus
108
+ def initialize(glib_mainloop=true)
109
+ super(Bus::TYPE_SYSTEM, glib_mainloop)
110
+ end
111
+ end
112
+
113
+ # The session (current login) message bus
114
+ class SessionBus < Bus
115
+ def initialize(glib_mainloop=true)
116
+ super(Bus::TYPE_SESSION, glib_mainloop)
117
+ end
118
+ end
119
+
120
+ # The bus that activated this process (if
121
+ # this process was launched by DBus activation)
122
+ class ActivationBus < Bus
123
+ def initialize(glib_mainloop=true)
124
+ super(Bus::TYPE_ACTIVATION, glib_mainloop)
125
+ end
126
+ end
127
+
128
+ # Represents a remote object.
129
+ #
130
+ # A RemoteObject is provided by a RemoteService on a particular Bus. RemoteObjects
131
+ # have member functions, and can be called like normal Ruby objects.
132
+ class RemoteObject
133
+ def initialize(service, object_path, interface)
134
+ @service = service
135
+ @object_path = object_path
136
+ @interface = interface
137
+ end
138
+
139
+ # Connect the signal +signal_name+ on this remote object to the
140
+ # supplied handler proc +handler_proc+.
141
+ def connect_to_signal(signal_name, handler_proc)
142
+ @service.get_bus.add_signal_receiver(handler_proc,
143
+ signal_name,
144
+ @interface,
145
+ @service.get_service_name,
146
+ @object_path)
147
+ end
148
+
149
+ # Implements magic remote method calls
150
+ def method_missing(sym, *args)
151
+ name = sym.id2name
152
+ message = DBus::Binding::DBusMessage.new_method_call(@service.get_service_name,
153
+ @object_path,
154
+ @interface,
155
+ name)
156
+ iter = message.get_iter
157
+ args.each{|a| iter.append(a)}
158
+ reply = @service.get_bus.get_connection.send_with_reply_and_block(message, 5000)
159
+ reply_args = reply.to_a
160
+ return nil if reply_args.empty?
161
+ return reply_args[0] if reply_args.length == 1
162
+ return reply_args
163
+ end
164
+ end
165
+
166
+ # Base class for exporting your own Services across the Bus
167
+ #
168
+ # Just inherit from Service, providing the name of your service
169
+ # (e.g. org.designfu.SampleService)
170
+ class Service
171
+ def initialize(name, bus=nil)
172
+ @name = name
173
+ @bus = bus
174
+ @bus = Bus.new if @bus.nil?
175
+ DBus::Binding::bus_acquire_service(@bus.get_connection, @name)
176
+ end
177
+
178
+ # The name of this service
179
+ def get_name
180
+ @name
181
+ end
182
+
183
+ # The bus this service is defined on
184
+ def get_bus
185
+ @bus
186
+ end
187
+ end
188
+
189
+ # Base class for objects that support D-BUS invocation messages
190
+ class DBusCallable
191
+ # Create a new DBusCallable instance on the specified +connection+.
192
+ # +dbus_methods+ is an Array containing a list of symbols for the
193
+ # methods which may be invoked remotely.
194
+ def initialize(connection, dbus_methods=[])
195
+ @connection = connection
196
+ @dbus_methods = dbus_methods
197
+ end
198
+
199
+ # Process the method invocation message given in +message+. Returns
200
+ # the reply message.
201
+ def dispatch_message(message)
202
+ dispatch(message.get_member, message.to_a, message)
203
+ end
204
+
205
+ # Invoke the method +name+, with arguments +args+, and source invocation
206
+ # request message +source_message+, returning the reply message.
207
+ def dispatch(name, args, source_message)
208
+ unless @dbus_methods.include?(name.to_sym)
209
+ return new_error_reply(source_message, "Method '#{name}' not in allowed list")
210
+ end
211
+ unless self.respond_to?(name)
212
+ return new_error_reply(source_message, "No such method '#{name}'")
213
+ end
214
+ ret = nil
215
+ begin
216
+ args = [source_message, *args]
217
+ ret = self.send(name, *args)
218
+ rescue
219
+ return new_error_reply(source_message, $!.to_s)
220
+ end
221
+ reply = DBus::Binding::DBusMessage.new_method_return(source_message)
222
+ iter = reply.get_iter
223
+ iter.append(ret)
224
+ reply
225
+ end
226
+
227
+ # Generate a new error reply from source message +message+, and error string
228
+ # +error_message+.
229
+ def new_error_reply(message, error_message)
230
+ error_name = self.class.to_s.gsub(/::/, '.')
231
+ error_name += '.ERROR'
232
+ DBus::Binding::DBusMessage.new_error(message, error_name, error_message)
233
+ end
234
+
235
+ # Called when this object is unregistered from the connection
236
+ def on_unregister(connection)
237
+ end
238
+
239
+ # Called when a message arrives on the connection for this object
240
+ def on_message(connection, message)
241
+ reply = dispatch_message(message)
242
+ @connection.send(reply)
243
+ HANDLER_RESULT_HANDLED
244
+ end
245
+ end
246
+
247
+ # Base class for exporting your own Objects across the Bus
248
+ #
249
+ # Just inherit from Object and provide a list of the symbol names for methods
250
+ # to share across the bus. These will appear as member functions of your
251
+ # Service object.
252
+ class Object < DBusCallable
253
+ def initialize(object_path, service, dbus_methods=[])
254
+ @connection = service.get_bus.get_connection
255
+ super(@connection, dbus_methods)
256
+ @object_path = object_path
257
+ @service = service
258
+ @connection.register_object_path(@object_path, method(:on_unregister), method(:on_message))
259
+ end
260
+
261
+ # Emit the signal +signal_name+ for interface +interface+, and include +args+ in the
262
+ # signal message.
263
+ def emit_signal(interface, signal_name, *args)
264
+ message = DBus::Binding::DBusMessage.new_signal(@object_path, interface, signal_name)
265
+ iter = message.get_iter
266
+ args.each{|a| iter.append(a)}
267
+ @connection.send(message)
268
+ end
269
+ end
270
+
271
+ # An object tree allows you to register a handler for a tree of object paths.
272
+ # This means that literal Ruby objects do not need to be created for each object
273
+ # over the bus, but you can have a virtual tree of objects handled by a single
274
+ # Ruby object. There are two ways to handle method calls on virtual objects:
275
+ #
276
+ # 1. Pass a list of dbus_methods in to initialize. This works just like
277
+ # DBus::Object, except an object_path is passed as the first argument to
278
+ # each method, denoting which virtual object the call was made on. If all
279
+ # the objects in the tree support the same methods, this is the best
280
+ # approach.
281
+ #
282
+ # 2. Override object_method_called. This allows you to define the valid
283
+ # methods dynamically on an object by object basis. For example, if
284
+ # providing an object tree that represented a filesystem heirarchy, you'd
285
+ # only want an ls method on directory objects, not file objects.
286
+ class ObjectTree < DBusCallable
287
+ def initialize(base_path, service, dbus_methods=[])
288
+ @connection = service.get_bus.get_connection
289
+ super(@connection, dbus_methods)
290
+ @base_path = base_path
291
+ @service = service
292
+ @methods = dbus_methods
293
+ @service.get_bus.get_connection.register_fallback(base_path, method(:on_unregister),
294
+ method(:on_message))
295
+ end
296
+
297
+ # Create a new absolute +ObjectPath+ for the given relative +path+
298
+ def relative_path_to_object_path(path)
299
+ ObjectPath.new(@base_path + path)
300
+ end
301
+
302
+ # Broadcast the signal +signal_name+ for interface +interface+ for
303
+ # the object identified by +relative_path+
304
+ def broadcast_signal(interface, signal_name, relative_path)
305
+ object_path = relative_path_to_object_path(relative_path)
306
+ message = DBus::Binding::DBusMessage.new_signal(object_path, interface, signal_name)
307
+ @connection.send(message)
308
+ end
309
+
310
+ def object_method_called
311
+ raise NotImplementedError, "Not implemented"
312
+ end
313
+
314
+ def on_message(connection, message)
315
+ target_object_full_path = message.get_path
316
+ n = @base_path.length
317
+ unless @base_path == target_object_full_path[0,n]
318
+ @connection.send(new_error_reply(message, "Invalid target path: #{target_object_full_path}"))
319
+ return HANDLER_RESULT_HANDLED
320
+ end
321
+ target_object_path = target_object_full_path[n..-1]
322
+ target_method = message.get_member
323
+ target_args = message.to_a
324
+ args = [target_object_path, *target_args]
325
+ @connection.send(dispatch(target_method, args, message))
326
+ HANDLER_RESULT_HANDLED
327
+ end
328
+ end
329
+
330
+ # A remote service providing objects.
331
+ #
332
+ # A service is typically a process or application that provides
333
+ # remote objects, but can also be the broadcast service that
334
+ # receives signals from all applications on the Bus.
335
+ class RemoteService
336
+ def initialize(bus, service_name)
337
+ @bus = bus
338
+ @service_name = service_name
339
+ end
340
+
341
+ # Bus associated with this service
342
+ def get_bus
343
+ @bus
344
+ end
345
+
346
+ # The service name
347
+ def get_service_name
348
+ @service_name
349
+ end
350
+
351
+ # Creates an object for the specified +object_path+
352
+ def get_object(object_path, interface)
353
+ RemoteObject.new(self, object_path, interface)
354
+ end
355
+ end
356
+
357
+ class ObjectPath < String; end
358
+
359
+ end