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.
- data/examples/example-client.rb +11 -0
- data/examples/example-service.rb +19 -0
- data/examples/example-signal-emitter.rb +18 -0
- data/examples/example-signal-recipient.rb +17 -0
- data/examples/gconf-proxy-client.rb +11 -0
- data/examples/gconf-proxy-service.rb +39 -0
- data/examples/list-session-services.rb +26 -0
- data/examples/list-system-services.rb +26 -0
- data/examples/volumed.rb +151 -0
- data/ext/MANIFEST +0 -0
- data/ext/extconf.rb +19 -0
- data/ext/ruby-dbus-bus.c +145 -0
- data/ext/ruby-dbus-common.c +105 -0
- data/ext/ruby-dbus-connection.c +704 -0
- data/ext/ruby-dbus-message-iter.c +661 -0
- data/ext/ruby-dbus-message.c +641 -0
- data/ext/ruby-dbus-pending-call.c +98 -0
- data/ext/ruby-dbus-server.c +126 -0
- data/ext/ruby-dbus.h +127 -0
- data/lib/dbus.rb +359 -0
- data/lib/dbus/binding.rb +259 -0
- data/lib/dbus/version.rb +21 -0
- data/test/tc_all.rb +10 -0
- data/test/tc_connection.rb +49 -0
- data/test/tc_message.rb +82 -0
- data/test/tc_server.rb +10 -0
- data/tools/genrdoc +6 -0
- metadata +67 -0
@@ -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
|
+
}
|
data/ext/ruby-dbus.h
ADDED
@@ -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
|
data/lib/dbus.rb
ADDED
@@ -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
|