ruby-dbus 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYING +504 -0
- data/NEWS +137 -0
- data/README +53 -0
- data/Rakefile +54 -0
- data/VERSION +1 -0
- data/doc/tutorial/index.html +356 -0
- data/doc/tutorial/index.markdown +467 -0
- data/examples/gdbus/gdbus +255 -0
- data/examples/gdbus/gdbus.glade +184 -0
- data/examples/gdbus/launch.sh +4 -0
- data/examples/no-introspect/nm-test.rb +21 -0
- data/examples/no-introspect/tracker-test.rb +16 -0
- data/examples/rhythmbox/playpause.rb +25 -0
- data/examples/service/call_service.rb +25 -0
- data/examples/service/service_newapi.rb +51 -0
- data/examples/simple/call_introspect.rb +34 -0
- data/examples/utils/listnames.rb +11 -0
- data/examples/utils/notify.rb +19 -0
- data/lib/dbus.rb +91 -0
- data/lib/dbus/auth.rb +258 -0
- data/lib/dbus/bus.rb +816 -0
- data/lib/dbus/core_ext/class/attribute.rb +91 -0
- data/lib/dbus/core_ext/kernel/singleton_class.rb +14 -0
- data/lib/dbus/core_ext/module/remove_method.rb +12 -0
- data/lib/dbus/error.rb +44 -0
- data/lib/dbus/export.rb +132 -0
- data/lib/dbus/introspect.rb +553 -0
- data/lib/dbus/marshall.rb +443 -0
- data/lib/dbus/matchrule.rb +100 -0
- data/lib/dbus/message.rb +310 -0
- data/lib/dbus/type.rb +222 -0
- data/ruby-dbus.gemspec +18 -0
- data/test/binding_test.rb +56 -0
- data/test/bus_driver_test.rb +22 -0
- data/test/dbus-launch-simple +35 -0
- data/test/dbus-limited-session.conf +28 -0
- data/test/server_robustness_test.rb +41 -0
- data/test/server_test.rb +53 -0
- data/test/service_newapi.rb +129 -0
- data/test/session_bus_test_manual.rb +20 -0
- data/test/signal_test.rb +64 -0
- data/test/t1 +4 -0
- data/test/t2.rb +66 -0
- data/test/t3-ticket27.rb +18 -0
- data/test/t5-report-dbus-interface.rb +58 -0
- data/test/t6-loop.rb +82 -0
- data/test/test_env +13 -0
- data/test/test_server +39 -0
- data/test/variant_test.rb +66 -0
- metadata +117 -0
data/NEWS
ADDED
@@ -0,0 +1,137 @@
|
|
1
|
+
= Ruby D-Bus NEWS
|
2
|
+
|
3
|
+
Note about bug numbers:
|
4
|
+
Ticket#1 - https://trac.luon.net/ruby-dbus/ticket/1
|
5
|
+
Issue#1 - http://github.com/mvidner/ruby-dbus/issues#issue/1
|
6
|
+
bnc#1 - https://bugzilla.novell.com/show_bug.cgi?id=1
|
7
|
+
|
8
|
+
== Ruby D-Bus 0.5.0 - 2010-11-07
|
9
|
+
|
10
|
+
Features:
|
11
|
+
* Better binding of Ruby Exceptions to D-Bus Errors.
|
12
|
+
* Converted the package to a Gem (Issue#6).
|
13
|
+
* Converted the tutorial from Webgen to Markdown.
|
14
|
+
|
15
|
+
Bug fixes:
|
16
|
+
* Don't pass file descriptors to subprocesses.
|
17
|
+
* Fixed InterfaceElement::validate_name (Ticket#38, by Herwin Weststrate).
|
18
|
+
* Fixed a typo in InvalidDestinationName description (Ticket#40).
|
19
|
+
|
20
|
+
== Ruby D-Bus 0.4.0 - 2010-08-20
|
21
|
+
|
22
|
+
Features:
|
23
|
+
* TCP transport (by pangdudu)
|
24
|
+
* Enabled test code coverage report (rcov)
|
25
|
+
|
26
|
+
Bug fixes:
|
27
|
+
* Classes should not share all interfaces (Ticket#36/Issue#5)
|
28
|
+
* Ruby 1.9 compatibility (Ticket#37, by Myra Nelson)
|
29
|
+
|
30
|
+
== Ruby D-Bus 0.3.1 - 2010-07-22
|
31
|
+
|
32
|
+
Bug fixes:
|
33
|
+
* Many on_signal could cause DBus.Error.LimitsExceeded bnc#617350).
|
34
|
+
Don't add a match rule that already exists, enable removing match
|
35
|
+
rules. Now only one handler for a rule is called (but it is possible
|
36
|
+
for one signal to match more rules). This reverts the half-fix done
|
37
|
+
to fix Issue#3
|
38
|
+
* Re-added InterfaceElement#add_param for compatibility.
|
39
|
+
* Handle more ways which tell us that a bus connection has died.
|
40
|
+
|
41
|
+
== Ruby D-Bus 0.3.0 - 2010-03-28
|
42
|
+
|
43
|
+
Bug fixes:
|
44
|
+
|
45
|
+
* Fixed "undefined method `get_node' for nil:NilClass"
|
46
|
+
on Ubuntu Karmic (Ticket#34).
|
47
|
+
* Get the session bus address even if unset in ENV (Issue#4).
|
48
|
+
* Improved exceptions a bit:
|
49
|
+
UndefinedInterface, InvalidMethodName, NoMethodError, no RuntimeException
|
50
|
+
|
51
|
+
These are by Klaus Kaempf:
|
52
|
+
* Make the signal dispatcher call all handlers (Issue#3).
|
53
|
+
* Run on Ruby < 1.8.7 (Issue#2).
|
54
|
+
* Avoid needless DBus::IncompleteBufferException (Ticket#33).
|
55
|
+
* Don't ignore DBus Errors in request_service, raise them (Ticket#32).
|
56
|
+
|
57
|
+
Features:
|
58
|
+
|
59
|
+
* Automatic signature inference for variants.
|
60
|
+
* Introduced FormalParameter where a plain pair had been used.
|
61
|
+
|
62
|
+
== Ruby D-Bus 0.2.12 - 2010-01-24
|
63
|
+
|
64
|
+
Bug fixes:
|
65
|
+
|
66
|
+
* Fixed a long-standing bug where a service activated by the bus
|
67
|
+
would fail with "undefined method `get_node' for nil:NilClass"
|
68
|
+
(Tickets#25 and #29).
|
69
|
+
|
70
|
+
== Ruby D-Bus 0.2.11 - 2009-11-12
|
71
|
+
|
72
|
+
Features:
|
73
|
+
|
74
|
+
* Added DBus::Service#unexport (da1l6).
|
75
|
+
|
76
|
+
Bug fixes:
|
77
|
+
|
78
|
+
* Return org.freedesktop.DBus.Error.UnknownObject instead of crashing
|
79
|
+
(Ticket#31).
|
80
|
+
* Rescue exceptions in dbus_methods and reply with DBus errors instead of
|
81
|
+
crashing (da1l6).
|
82
|
+
* Better exception messages when sending nil, or mismatched structs.
|
83
|
+
* Call mktemp without --tmpdir, to build on older distros.
|
84
|
+
|
85
|
+
== Ruby D-Bus 0.2.10 - 2009-09-10
|
86
|
+
|
87
|
+
Bug fixes:
|
88
|
+
|
89
|
+
* DBus::Service.exists? fixed (Murat Demirten).
|
90
|
+
* Ruby 1.9 fixes (Jedediah Smith).
|
91
|
+
* Fixed an endless sleep in DBus::Main.run (bnc#537401).
|
92
|
+
* Added details to PacketMarshaller exceptions (bnc#538050).
|
93
|
+
|
94
|
+
(bnc#FOO refers to https://bugzilla.novell.com/show_bug.cgi?id=FOO )
|
95
|
+
|
96
|
+
== Ruby D-Bus "I'm not dead" 0.2.9 - 2009-08-26
|
97
|
+
|
98
|
+
Thank you to Paul and Arnaud for starting the project. I, Martin
|
99
|
+
Vidner, am continuing with it on GitHub.
|
100
|
+
|
101
|
+
* Fixed passing an array through a variant (no ticket).
|
102
|
+
* Fixed marshalling "av" (Ticket #30).
|
103
|
+
* Fixed variant alignment (Ticket #27).
|
104
|
+
* Added DBus::Main.quit.
|
105
|
+
* Mention the DBus interface in a NameError for an unknown method.
|
106
|
+
* Fixed ruby-1.9 "warning: default `to_a' will be obsolete".
|
107
|
+
* Added Rakefile and gemspec.
|
108
|
+
|
109
|
+
== Ruby D-Bus "Thanks for all the fish" 0.2.1 - 2007-12-29
|
110
|
+
|
111
|
+
More bugfixes, mostly supplied by users supplying us with patches. Thanks!
|
112
|
+
|
113
|
+
* Support for new types added:
|
114
|
+
- dict (courtesy of Drake Wilson);
|
115
|
+
- double (courtesy of Patrick Sissons);
|
116
|
+
- variant.
|
117
|
+
* Improved exception raise support (courtesy of Sjoerd Simons,
|
118
|
+
Patrick Sissons).
|
119
|
+
* Some polish (removed debug output, solved unnecessary warnings).
|
120
|
+
* Documentation updates, example fixes and updates.
|
121
|
+
|
122
|
+
== Ruby D-Bus "Almost live from DebConf 7" 0.2.0 - 2007-06-02
|
123
|
+
|
124
|
+
Again a bugfix release, also meant to be the public release
|
125
|
+
for exploratory purposes. New in 0.2.0:
|
126
|
+
|
127
|
+
* Complete tutorial revamp.
|
128
|
+
* Relicensed to the LGPL.
|
129
|
+
|
130
|
+
== Ruby D-Bus "Release Often" 0.1.1 - 2007-04-23
|
131
|
+
|
132
|
+
Bugfix release. Fixes hardcoded string for requesting bus names,
|
133
|
+
found by Rudi Cilibrasi.
|
134
|
+
|
135
|
+
== Ruby D-Bus "Happy Birthday Paul" 0.1.0 - 2007-04-17
|
136
|
+
|
137
|
+
First release. Supports most of D-Bus' features.
|
data/README
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
= Ruby D-Bus README
|
2
|
+
|
3
|
+
Ruby D-Bus provides an implementation of the D-Bus protocol such that the
|
4
|
+
D-Bus system can be used in the Ruby programming language.
|
5
|
+
|
6
|
+
== Requirements
|
7
|
+
|
8
|
+
* Ruby 1.8 (>= 1.8.6?)
|
9
|
+
|
10
|
+
Optionally, for generating the tutorial:
|
11
|
+
* Webgen (>= 0.4)
|
12
|
+
|
13
|
+
== Installation
|
14
|
+
|
15
|
+
1. Decompress the Ruby D-Bus tarball (ruby-dbus-<version>.tar.gz).
|
16
|
+
2. Move to top-level directory and type:
|
17
|
+
|
18
|
+
$ ruby setup.rb config
|
19
|
+
$ ruby setup.rb setup
|
20
|
+
($ su)
|
21
|
+
# ruby setup.rb install
|
22
|
+
|
23
|
+
You can also install files in your favorite directory by
|
24
|
+
supplying setup.rb some options. Try "ruby setup.rb --help".
|
25
|
+
|
26
|
+
== Feature
|
27
|
+
|
28
|
+
Ruby D-Bus currently supports the following features:
|
29
|
+
|
30
|
+
* Connecting to local buses.
|
31
|
+
* Accessing remote services, objects and interfaces.
|
32
|
+
* Invoking methods on remote objects synchronously and asynchronously.
|
33
|
+
* Catch signals on remote objects and handle them via callbacks.
|
34
|
+
* Remote object introspection.
|
35
|
+
* Walking object trees.
|
36
|
+
* Creating services and registering them on the bus.
|
37
|
+
* Exporting objects with interfaces on a bus for remote use.
|
38
|
+
* Rubyish D-Bus object and interface syntax support that automatically
|
39
|
+
allows for introspection.
|
40
|
+
* Emitting signals on exported objects.
|
41
|
+
|
42
|
+
== Usage
|
43
|
+
|
44
|
+
See some of the examples in the examples/ subdirectory of the tarball.
|
45
|
+
Also, check out the included tutorial (in Webgen format) in doc/tutorial/
|
46
|
+
or view it online on http://trac.luon.net/data/ruby-dbus/tutorial/.
|
47
|
+
|
48
|
+
== License
|
49
|
+
|
50
|
+
Ruby D-Bus is free software; you can redistribute it and/or modify it
|
51
|
+
under the terms of the GNU Lesser General Public License as published by the
|
52
|
+
Free Software Foundation; either version 2.1 of the License, or (at
|
53
|
+
your option) any later version.
|
data/Rakefile
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/gempackagetask'
|
4
|
+
require 'fileutils'
|
5
|
+
include FileUtils
|
6
|
+
require 'rake/rdoctask'
|
7
|
+
require 'rake/testtask'
|
8
|
+
|
9
|
+
desc 'Default: run tests in the proper environment'
|
10
|
+
task :default => "env:test"
|
11
|
+
|
12
|
+
def common_test_task(t)
|
13
|
+
t.libs << "lib"
|
14
|
+
t.test_files = FileList['test/*_test.rb', 'test/t*.rb']
|
15
|
+
t.verbose = true
|
16
|
+
end
|
17
|
+
Rake::TestTask.new {|t| common_test_task t }
|
18
|
+
|
19
|
+
begin
|
20
|
+
require 'rcov/rcovtask'
|
21
|
+
Rcov::RcovTask.new {|t| common_test_task t }
|
22
|
+
rescue LoadError
|
23
|
+
# no rcov, never mind
|
24
|
+
end
|
25
|
+
|
26
|
+
%w(test rcov).each do |tname|
|
27
|
+
namespace :env do
|
28
|
+
desc "Run #{tname} in the proper environment"
|
29
|
+
task tname do |t|
|
30
|
+
cd "test" do
|
31
|
+
system "./test_env rake #{tname}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
load "ruby-dbus.gemspec"
|
38
|
+
|
39
|
+
Rake::GemPackageTask.new(GEMSPEC) do |pkg|
|
40
|
+
# no other formats needed
|
41
|
+
end
|
42
|
+
|
43
|
+
Rake::RDocTask.new do |rd|
|
44
|
+
rd.rdoc_dir = 'doc/rdoc'
|
45
|
+
rd.rdoc_files.include("README", "lib/**/*.rb")
|
46
|
+
# rd.options << "--diagram"
|
47
|
+
# rd.options << "--all"
|
48
|
+
end
|
49
|
+
|
50
|
+
desc "Render the tutorial in HTML"
|
51
|
+
task :tutorial => "doc/tutorial/index.html"
|
52
|
+
file "doc/tutorial/index.html" => "doc/tutorial/index.markdown" do |t|
|
53
|
+
sh "markdown #{t.prerequisites[0]} > #{t.name}"
|
54
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.5.0
|
@@ -0,0 +1,356 @@
|
|
1
|
+
<style>
|
2
|
+
code { background-color: #F0E7E7; }
|
3
|
+
pre code { background-color: #F0DDDD; }
|
4
|
+
pre {
|
5
|
+
font-size: 90%;
|
6
|
+
overflow: hidden;
|
7
|
+
padding-left: 10pt;
|
8
|
+
border: thin solid #F0B4B4;
|
9
|
+
background-color: #F0DDDD;
|
10
|
+
}
|
11
|
+
</style>
|
12
|
+
|
13
|
+
<h1>Welcome</h1>
|
14
|
+
<p>This is the Ruby D-Bus tutorial. It aims to show you the features of Ruby
|
15
|
+
D-Bus and as you read through the tutorial also how to use them.</p>
|
16
|
+
<p>© Arnaud Cornet and Paul van Tilburg; this tutorial is part of
|
17
|
+
free software; you can redistribute it and/or modify it under the
|
18
|
+
terms of the <a href="http://www.gnu.org/licenses/lgpl.html">GNU Lesser General Public License,
|
19
|
+
version 2.1</a> as published by the
|
20
|
+
<a href="http://www.fsf.org/">Free Software Foundation</a>.</p>
|
21
|
+
<h1>Introduction</h1>
|
22
|
+
<p>This is a tutorial for Ruby D-Bus, a library to access D-Bus facilities of your
|
23
|
+
system.</p>
|
24
|
+
<h2>What is D-Bus?</h2>
|
25
|
+
<p>D-Bus is an RPC(Remote Procedure Call) protocol. A common setup can have
|
26
|
+
multiple D-Bus daemons running that route procedure calls and signals in
|
27
|
+
the form of messages. Each of these daemons supports a bus. A bus that
|
28
|
+
is often used by modern desktop environments, and is available per session, is
|
29
|
+
called the <em>session bus</em>. Another bus that can be available, but in a
|
30
|
+
system-wide manner, is called the <em>system bus</em>. It is used for example by
|
31
|
+
the <a href="http://hal.freedesktop.org/">Hardware Abstraction Layer</a> daemon. Note
|
32
|
+
that theoretically the D-Bus RPC protocol can be used without a system or
|
33
|
+
session bus. I never came across any actual use of this though.</p>
|
34
|
+
<p>At the desktop level, D-Bus allows some components to interact. Typically
|
35
|
+
if you are writing an application or a personal script that wants to
|
36
|
+
interact with your web browser, your music player, or that simply wants to
|
37
|
+
pop-up a desktop notification, D-Bus comes into play.</p>
|
38
|
+
<p>At the system level, the Hardware Abstraction Layer is a privileged daemon
|
39
|
+
that notifies other software of hardware activities. Typically, if you
|
40
|
+
want to be notified if a CD-ROM has been loaded in, of if you want to
|
41
|
+
explore hardware, the system daemon comes into play.</p>
|
42
|
+
<p>The D-Bus RPC system is as we will see <em>object oriented</em>.</p>
|
43
|
+
<p>Buses provide access to <em>services</em> provided in turn by running or ready to
|
44
|
+
run processes. Let me introduce some D-Bus terminology before we discuss
|
45
|
+
the API of Ruby D-Bus.</p>
|
46
|
+
<h2>Client</h2>
|
47
|
+
<p>A D-Bus client is a process that connects to a D-Bus. They issue method
|
48
|
+
calls and register to the bus for signals and events.</p>
|
49
|
+
<h2>Service</h2>
|
50
|
+
<p>A connected client can export some of its objects and let other clients
|
51
|
+
call some of its methods. Such clients typically register a special name
|
52
|
+
like <code>org.freedesktop.Notifications</code>, the service name.</p>
|
53
|
+
<p>There is slightly different type of service. They are provided by
|
54
|
+
processes that can be launched by a D-Bus daemon on demand. Once they are
|
55
|
+
started by D-Bus they register a service name and behave like another
|
56
|
+
client.</p>
|
57
|
+
<p>Note that the buses themselves provide the <code>org.freedesktop.DBus</code> service,
|
58
|
+
and provide some features through it.</p>
|
59
|
+
<h2>Object path</h2>
|
60
|
+
<p>An object path is the D-Bus way to specify an object <em>instance</em> address. A
|
61
|
+
service can provide different object instances to the outside world, so
|
62
|
+
that external processes can call methods on each of them. An object path
|
63
|
+
is an address of an instance in a very similar way that the path is an
|
64
|
+
address of a file on a file system. For example:
|
65
|
+
<code>/org/freedesktop/Notification</code> is an object path of an object provided by
|
66
|
+
the <code>org.freedesktop.Notification</code> service</p>
|
67
|
+
<p><strong>Beware</strong>: service names and object paths can, but do <em>not</em> have to be
|
68
|
+
related! You'll probably encounter a lot of cases though, where the
|
69
|
+
object path is a slashed version of the dotted service name.</p>
|
70
|
+
<h2>Interface</h2>
|
71
|
+
<p>Classically in an object model, classes can implement interfaces. That is,
|
72
|
+
some method definitions grouped in an interface. This is exactly what a
|
73
|
+
D-Bus interface is as well. In D-Bus interfaces have names. These names must be
|
74
|
+
specified on method calls.</p>
|
75
|
+
<p>The <code>org.freedesktop.Notification</code> service provides an object instance
|
76
|
+
called <code>/org/freedesktop/Notification</code>. This instance object implements an
|
77
|
+
interface called <code>org.freedesktop.Notifications</code>. It also provides two
|
78
|
+
special D-Bus specific interfaces: <code>org.freedesktop.DBus.Introspect</code> and
|
79
|
+
<code>org.freedesktop.DBus.Properties</code>. Again, object paths, service names,
|
80
|
+
and interface names can be related but do not have to be.</p>
|
81
|
+
<p>Basically the <code>org.freedesktop.DBus.Introspect</code> has an <code>Introspect</code> method,
|
82
|
+
that returns XML data describing the <code>/org/freedesktop/Notification</code> object
|
83
|
+
interfaces. This is used heavily internally by Ruby D-Bus.</p>
|
84
|
+
<h2>Method</h2>
|
85
|
+
<p>A method is, well, a method in the classical meaning. It's a function that
|
86
|
+
is called in the context of an object instance. Methods have typed
|
87
|
+
parameters and return typed return values.</p>
|
88
|
+
<h2>Signal</h2>
|
89
|
+
<p>Signals are simplified method calls that do not have a return value. They
|
90
|
+
do have typed parameters though.</p>
|
91
|
+
<h2>Message</h2>
|
92
|
+
<p>Method calls, method returns, signals, errors: all are encoded as D-Bus
|
93
|
+
messages sent over a bus. They are made of a packet header with source and
|
94
|
+
destination address, a type (method call, method reply, signal) and the
|
95
|
+
body containing the parameters (for signals and method calls) or the return
|
96
|
+
values (for a method return message).</p>
|
97
|
+
<h2>Signature</h2>
|
98
|
+
<p>Because D-Bus is typed and dynamic, each message comes with a signature that
|
99
|
+
describes the types of the data that is contained within the message. The
|
100
|
+
signature is a string with an extremely basic language that only describes
|
101
|
+
a data type. You will need to have some knowledge of what a signature
|
102
|
+
looks like if you are setting up a service. If you are just programming a
|
103
|
+
D-Bus client, you can live without knowing about them.</p>
|
104
|
+
<h1>Client Usage</h1>
|
105
|
+
<p>This chapter discusses basic client usage
|
106
|
+
and has the following topics:</p>
|
107
|
+
<h2>Using the library</h2>
|
108
|
+
<p>If you want to use the library, you have to make Ruby load it by issuing:</p>
|
109
|
+
<pre><code>require 'dbus'
|
110
|
+
</code></pre>
|
111
|
+
<p>That's all! Now we can move on to really using it...</p>
|
112
|
+
<h2>Connecting to a bus</h2>
|
113
|
+
<p>On a typical system, two buses are running, the system bus and the session
|
114
|
+
bus. The system bus can be accessed by:</p>
|
115
|
+
<pre><code>bus = DBus::SystemBus.instance
|
116
|
+
</code></pre>
|
117
|
+
<p>Probably you already have guessed how to access the session bus. This
|
118
|
+
can be done by:</p>
|
119
|
+
<pre><code>bus = DBus::SessionBus.instance
|
120
|
+
</code></pre>
|
121
|
+
<h2>Performing method calls</h2>
|
122
|
+
<p>Let me continue this example using the session bus. Let's say that I want
|
123
|
+
to access an object of some client on the session bus. This particular
|
124
|
+
D-Bus client provides a service called <code>org.gnome.Rhythmbox</code>. Let me
|
125
|
+
access this service:</p>
|
126
|
+
<pre><code>rb_service = bus.service("org.gnome.Rhythmbox")
|
127
|
+
</code></pre>
|
128
|
+
<p>In this example I access the <code>org.gnome.Rhythmbox</code> service, which is
|
129
|
+
provided by the application
|
130
|
+
<a href="http://www.gnome.org/projects/rhythmbox/">Rhythmbox</a>.
|
131
|
+
OK, I have a service handle now, and I know that it exports the object
|
132
|
+
"/org/gnome/Rhythmbox/Player". I will trivially access this remote object
|
133
|
+
using:</p>
|
134
|
+
<pre><code>rb_player = rb_service.object("/org/gnome/Rhythmbox/Player")
|
135
|
+
</code></pre>
|
136
|
+
<h2>Introspection</h2>
|
137
|
+
<p>Well, that was easy. Let's say that I know that this particular object is
|
138
|
+
introspectable. In real life most of them are. The <code>rb_object</code> object we
|
139
|
+
have here is just a handle of a remote object, in general they are called
|
140
|
+
<em>proxy objects</em>, because they are the local handle of a remote object. It
|
141
|
+
would be nice to be able to make it have methods, and that its methods send
|
142
|
+
a D-Bus call to remotely execute the actual method in another process.
|
143
|
+
Well, instating these methods for a <em>introspectable</em> object is trivial:</p>
|
144
|
+
<pre><code>rb_player.introspect
|
145
|
+
</code></pre>
|
146
|
+
<p>And there you go. Note that not all services or objects can be
|
147
|
+
introspected, therefore you have to do this manually! Let me remind you
|
148
|
+
that objects in D-Bus have interfaces and interfaces have methods. Let's
|
149
|
+
now access these methods:</p>
|
150
|
+
<pre><code>rb_player_iface = rb_player["org.gnome.Rhythmbox.Player"]
|
151
|
+
puts rb_player_iface.getPlayingUri
|
152
|
+
</code></pre>
|
153
|
+
<p>As you can see, when you want to call a method on an instance object, you have
|
154
|
+
to get the correct interface. It is a bit tedious, so we have the following
|
155
|
+
shortcut that does the same thing as before:</p>
|
156
|
+
<pre><code>rb_player.default_iface = "org.gnome.Rhythmbox.Player"
|
157
|
+
puts rb_player.getPlayingUri
|
158
|
+
</code></pre>
|
159
|
+
<p>The <code>default_iface=</code> call specifies the default interface that should be
|
160
|
+
used when non existing methods are called directly on a proxy object, and
|
161
|
+
not on one of its interfaces.</p>
|
162
|
+
<p>Note that the bus itself has a corresponding introspectable object. You can
|
163
|
+
access it with <code>bus.proxy</code> method. For example, you can retrieve an array of
|
164
|
+
exported service names of a bus like this:</p>
|
165
|
+
<pre><code>bus.proxy.ListNames[0]
|
166
|
+
</code></pre>
|
167
|
+
<h2>Calling a method asynchronously</h2>
|
168
|
+
<p>D-Bus is <em>asynchronous</em>. This means that you do not have to wait for a
|
169
|
+
reply when you send a message. When you call a remote method that takes a
|
170
|
+
lot of time to process remotely, you don't want your application to hang,
|
171
|
+
right? Well the asychronousness exists for this reason. What if you dont'
|
172
|
+
want to wait for the return value of a method, but still you want to take
|
173
|
+
some action when you receive it?</p>
|
174
|
+
<p>There is a classical method to program this event-driven mechanism. You do
|
175
|
+
some computation, perform some method call, and at the same time you setup
|
176
|
+
a callback that will be triggered once you receive a reply. Then you run a
|
177
|
+
main loop that is responsible to call the callbacks properly. Here is how
|
178
|
+
you do it:</p>
|
179
|
+
<pre><code>rb_player.getPlayingUri do |resp|
|
180
|
+
puts "The playing URI is #{resp}"
|
181
|
+
end
|
182
|
+
puts "See, I'm not waiting!"
|
183
|
+
loop = DBus::Main.new
|
184
|
+
loop << bus
|
185
|
+
loop.run
|
186
|
+
</code></pre>
|
187
|
+
<p>This code will print the following:</p>
|
188
|
+
<pre><code>See, I'm not waiting!
|
189
|
+
The playing URI is file:///music/papapingoin.mp3
|
190
|
+
</code></pre>
|
191
|
+
<h2>Waiting for a signal</h2>
|
192
|
+
<p>Signals are calls from the remote object to your program. As a client, you
|
193
|
+
set yourself up to receive a signal and handle it with a callback. Then running
|
194
|
+
the main loop triggers the callback. You can register a callback handler
|
195
|
+
as allows:</p>
|
196
|
+
<pre><code>rb_player.on_signal("elapsedChanged") do |u|
|
197
|
+
puts u
|
198
|
+
end
|
199
|
+
</code></pre>
|
200
|
+
<h2>More about introspection</h2>
|
201
|
+
<p>There are various ways to inspect a remote service. You can simply call
|
202
|
+
<code>Introspect()</code> and read the XML output. However, in this tutorial I assume
|
203
|
+
that you want to do it using the Ruby D-Bus API.</p>
|
204
|
+
<p>Notice that you can introspect a service, and not only objects:</p>
|
205
|
+
<pre><code>rb_service = bus.service("org.gnome.Rhythmbox")
|
206
|
+
rb_service.introspect
|
207
|
+
p rb_service.root
|
208
|
+
</code></pre>
|
209
|
+
<p>This dumps a tree-like structure that represents multiple object paths. In
|
210
|
+
this particular case the output is:</p>
|
211
|
+
<pre><code></: {org => {gnome => {Rhythmbox => {Player => ..fdbe625de {},Shell => ..fdbe6852e {},PlaylistManager => ..fdbe4e340 {}}>
|
212
|
+
</code></pre>
|
213
|
+
<p>Read this left to right: the root node is "/", it has one child node "org",
|
214
|
+
"org" has one child node "gnome", and "gnome" has one child node "Rhythmbox".
|
215
|
+
Rhythmbox has Tree child nodes "Player", "Shell" and "PlaylistManager".
|
216
|
+
These three last child nodes have a weird digit that means it has an object
|
217
|
+
instance. Such object instances are already introspected.</p>
|
218
|
+
<p>If the prose wasn't clear, maybe the following ASCII art will help you:</p>
|
219
|
+
<pre><code>/
|
220
|
+
org
|
221
|
+
gnome
|
222
|
+
Rhythmbox
|
223
|
+
Shell (with object)
|
224
|
+
Player (with object)
|
225
|
+
PlaylistManager (with object)
|
226
|
+
</code></pre>
|
227
|
+
<h3>Walking the object tree</h3>
|
228
|
+
<p>You can have an object on any node, i.e. it is not limited to leaves.
|
229
|
+
You can access a specific node like this:</p>
|
230
|
+
<pre><code>rb_player = rb_service.root["org"]["gnome"]["Rhythmbox"]["Player"]
|
231
|
+
rb_player = rb_service.object("/org/gnome/Rhythmbox/Player")
|
232
|
+
</code></pre>
|
233
|
+
<p>The difference between the two is that for the first one, <code>rb_service</code>
|
234
|
+
needs to have been introspected. Also the obtained <code>rb_player</code> is already
|
235
|
+
introspected whereas the second <code>rb_player</code> isn't yet.</p>
|
236
|
+
<h2>Errors</h2>
|
237
|
+
<p>D-Bus calls can reply with an error instead of a return value. An error is
|
238
|
+
translated to a Ruby exception.</p>
|
239
|
+
<pre><code>begin
|
240
|
+
network_manager.sleep
|
241
|
+
rescue DBus::Error => e
|
242
|
+
puts e unless e.name == "org.freedesktop.NetworkManager.AlreadyAsleepOrAwake"
|
243
|
+
end
|
244
|
+
</code></pre>
|
245
|
+
<h1>Creating a Service</h1>
|
246
|
+
<p>This chapter deals with the opposite side of the basic client usage, namely
|
247
|
+
the creation of a D-Bus service.</p>
|
248
|
+
<h2>Registering a service</h2>
|
249
|
+
<p>Now that you know how to perform D-Bus calls, and how to wait for and
|
250
|
+
handle signals, you might want to learn how to publish some object and
|
251
|
+
interface to provide them to the D-Bus world. Here is how you do that.</p>
|
252
|
+
<p>As you should already know, D-Bus clients that provide some object to be
|
253
|
+
called remotely are services. Here is how to allocate a name on a bus:</p>
|
254
|
+
<pre><code>bus = DBus.session_bus
|
255
|
+
service = bus.request_service("org.ruby.service")
|
256
|
+
</code></pre>
|
257
|
+
<p>Now this client is know to the outside world as <code>org.ruby.service</code>.
|
258
|
+
Note that this is a request and it <em>can</em> be denied! When it
|
259
|
+
is denied, an exception (<code>DBus::NameRequestError</code>) is thrown.</p>
|
260
|
+
<h2>Exporting an object</h2>
|
261
|
+
<p>Now, let's define a class that we want to export:</p>
|
262
|
+
<pre><code>class Test < DBus::Object
|
263
|
+
# Create an interface.
|
264
|
+
dbus_interface "org.ruby.SampleInterface" do
|
265
|
+
# Create a hello method in that interface.
|
266
|
+
dbus_method :hello, "in name:s, in name2:s" do |name, name2|
|
267
|
+
puts "hello(#{name}, #{name2})"
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
271
|
+
</code></pre>
|
272
|
+
<p>As you can see, we define a <code>Test</code> class in which we define a
|
273
|
+
<code>org.ruby.SampleInterface</code> interface. In this interface, we define a
|
274
|
+
method. The given code block is the method's implementation. This will be
|
275
|
+
executed when remote programs performs a D-Bus call. Now the annoying part:
|
276
|
+
the actual method definition. As you can guess the call</p>
|
277
|
+
<pre><code>dbus_method :hello, "in name:s, in name2:s" do ...
|
278
|
+
</code></pre>
|
279
|
+
<p>creates a <code>hello</code> method that takes two parameters both of type string.
|
280
|
+
The <em>:s</em> means "of type string". Let's have a look at some other common
|
281
|
+
parameter types:</p>
|
282
|
+
<ul>
|
283
|
+
<li><em>u</em> means unsigned integer</li>
|
284
|
+
<li><em>i</em> means integer</li>
|
285
|
+
<li><em>y</em> means byte</li>
|
286
|
+
<li><em>(ui)</em> means a structure having a unsigned integer and a signed one.</li>
|
287
|
+
<li><em>a</em> means array, so that "ai" means array of integers<ul>
|
288
|
+
<li><em>as</em> means array of string</li>
|
289
|
+
<li><em>a(is)</em> means array of structures, each having an integer and a string.</li>
|
290
|
+
</ul>
|
291
|
+
</li>
|
292
|
+
</ul>
|
293
|
+
<p>For a full description of the available D-Bus types, please refer to the
|
294
|
+
<a href="http://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-signatures">D-Bus specification</a>.</p>
|
295
|
+
<p>Now that the class has been defined, we can instantiate an object
|
296
|
+
and export it as follows:</p>
|
297
|
+
<pre><code>exported_obj = Test.new("/org/ruby/MyInstance")
|
298
|
+
service.export(exported_obj)
|
299
|
+
</code></pre>
|
300
|
+
<p>This piece of code above instantiates a <code>Test</code> object with a D-Bus object
|
301
|
+
path. This object is reachable from the outside world after
|
302
|
+
<code>service.export(exported_obj)</code> is called.</p>
|
303
|
+
<p>We also need a loop which will read and process the calls coming over the bus:</p>
|
304
|
+
<pre><code>loop = DBus::Main.new
|
305
|
+
loop << bus
|
306
|
+
loop.run
|
307
|
+
</code></pre>
|
308
|
+
<h3>Using the exported object</h3>
|
309
|
+
<p>Now, let's consider another program that will access our newly created service:</p>
|
310
|
+
<pre><code>ruby_service = bus.service("org.ruby.service")
|
311
|
+
obj = ruby_service.object("/org/ruby/MyInstance")
|
312
|
+
obj.introspect
|
313
|
+
obj.default_iface = "org.ruby.SampleInterface"
|
314
|
+
obj.hello("giligiligiligili", "haaaaaaa")
|
315
|
+
</code></pre>
|
316
|
+
<p>As you can see, the object we defined earlier is automatically introspectable.
|
317
|
+
See also "Basic Client Usage".</p>
|
318
|
+
<h2>Emitting a signal</h2>
|
319
|
+
<p>Let's add some example method so you can see how to return a value to the
|
320
|
+
caller and let's also define another example interface that has a signal.</p>
|
321
|
+
<pre><code>class Test2 < DBus::Object
|
322
|
+
# Create an interface
|
323
|
+
dbus_interface "org.ruby.SampleInterface" do
|
324
|
+
# Create a hello method in the interface:
|
325
|
+
dbus_method :hello, "in name:s, in name2:s" do |name, name2|
|
326
|
+
puts "hello(#{name}, #{name2})"
|
327
|
+
end
|
328
|
+
# Define a signal in the interface:
|
329
|
+
dbus_signal :SomethingJustHappened, "toto:s, tutu:u"
|
330
|
+
end
|
331
|
+
|
332
|
+
dbus_interface "org.ruby.AnotherInterface" do
|
333
|
+
dbus_method :ThatsALongMethodNameIThink, "in name:s, out ret:s" do |name|
|
334
|
+
["So your name is #{name}"]
|
335
|
+
end
|
336
|
+
end
|
337
|
+
end
|
338
|
+
</code></pre>
|
339
|
+
<p>Triggering the signal is a easy as calling a method, but then this time on
|
340
|
+
a local (exported) object and not on a remote/proxy object:</p>
|
341
|
+
<pre><code>exported_obj.SomethingJustHappened("blah", 1)
|
342
|
+
</code></pre>
|
343
|
+
<p>Note that the <code>ThatsALongMethodNameIThink</code> method is returning a single
|
344
|
+
value to the caller. Notice that you always have to return an array. If
|
345
|
+
you want to return multiple values, just have an array with multiple
|
346
|
+
values.</p>
|
347
|
+
<h2>Replying with an error</h2>
|
348
|
+
<p>To reply to a dbus_method with a D-Bus error, raise a <code>DBus::Error</code>,
|
349
|
+
as constructed by the <code>error</code> convenience function:</p>
|
350
|
+
<pre><code>raise DBus.error("org.example.Error.SeatOccupied"), "Seat #{seat} is occupied"
|
351
|
+
</code></pre>
|
352
|
+
<p>If the error name is not specified, the generic
|
353
|
+
<code>org.freedesktop.DBus.Error.Failed</code> is used.</p>
|
354
|
+
<pre><code>raise DBus.error, "Seat #{seat} is occupied"
|
355
|
+
raise DBus.error
|
356
|
+
</code></pre>
|