dbus 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -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