[gjs: 1/2] examples: add a dbus-client and dbus-service example
- From: Philip Chimento <pchimento src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gjs: 1/2] examples: add a dbus-client and dbus-service example
- Date: Sun, 23 Feb 2020 00:31:30 +0000 (UTC)
commit 88e1ffd21a7fa49a07cf5b9945e51b9f9ea81f53
Author: Andy Holmes <andrew g r holmes gmail com>
Date: Fri Feb 21 22:45:21 2020 -0800
examples: add a dbus-client and dbus-service example
examples/dbus-client.js | 167 +++++++++++++++++++++++++++++++++++++++++++++++
examples/dbus-service.js | 137 ++++++++++++++++++++++++++++++++++++++
2 files changed, 304 insertions(+)
---
diff --git a/examples/dbus-client.js b/examples/dbus-client.js
new file mode 100644
index 00000000..7cedf69c
--- /dev/null
+++ b/examples/dbus-client.js
@@ -0,0 +1,167 @@
+'use strict';
+
+const GLib = imports.gi.GLib;
+const Gio = imports.gi.Gio;
+
+
+/*
+ * An XML DBus Interface
+ */
+const ifaceXml = `
+<node>
+ <interface name="org.gnome.gjs.Test">
+ <method name="SimpleMethod"/>
+ <method name="ComplexMethod">
+ <arg type="s" direction="in" name="input"/>
+ <arg type="u" direction="out" name="length"/>
+ </method>
+ <signal name="TestSignal">
+ <arg name="type" type="s"/>
+ <arg name="value" type="b"/>
+ </signal>
+ <property name="ReadOnlyProperty" type="s" access="read"/>
+ <property name="ReadWriteProperty" type="b" access="readwrite"/>
+ </interface>
+</node>`;
+
+
+
+// Pass the XML string to make a re-usable proxy class for an interface proxies.
+const TestProxy = Gio.DBusProxy.makeProxyWrapper(ifaceXml);
+
+
+let proxy = null;
+let proxySignalId = 0;
+let proxyPropId = 0;
+
+
+// Watching a name on DBus. Another option is to create a proxy with the
+// `Gio.DBusProxyFlags.DO_NOT_AUTO_START` flag and watch the `g-name-owner`
+// property.
+function onNameAppeared(connection, name, _owner) {
+ print(`"${name}" appeared on the session bus`);
+
+ // If creating a proxy synchronously, errors will be thrown as normal
+ try {
+ proxy = new TestProxy(
+ Gio.DBus.session,
+ 'org.gnome.gjs.Test',
+ '/org/gnome/gjs/Test'
+ );
+ } catch (e) {
+ logError(e);
+ return;
+ }
+
+
+ // Proxy wrapper signals use the special functions `connectSignal()` and
+ // `disconnectSignal()` to avoid conflicting with regular GObject signals.
+ proxySignalId = proxy.connectSignal('TestSignal', (proxy_, name_, args) => {
+ print(`TestSignal: ${args[0]}, ${args[1]}`);
+ });
+
+
+ // To watch property changes, you can connect to the `g-properties-changed`
+ // GObject signal with `connect()`
+ proxyPropId = proxy.connect('g-properties-changed', (proxy_, changed, invalidated) => {
+ for (let [prop, value] of Object.entries(changed.deepUnpack()))
+ print(`Property '${prop}' changed to '${value.deepUnpack()}'`);
+
+ for (let prop of invalidated)
+ print(`Property '${prop}' invalidated`);
+ });
+
+
+ // Reading and writing properties is straight-forward
+ print(`ReadOnlyProperty: ${proxy.ReadOnlyProperty}`);
+
+ print(`ReadWriteProperty: ${proxy.ReadWriteProperty}`);
+
+ proxy.ReadWriteProperty = !proxy.ReadWriteProperty;
+ print(`ReadWriteProperty: ${proxy.ReadWriteProperty}`);
+
+
+ // Both synchronous and asynchronous functions will be generated
+ try {
+ let value = proxy.SimpleMethodSync();
+
+ print(`SimpleMethod: ${value}`);
+ } catch (e) {
+ logError(`SimpleMethod: ${e.message}`);
+ }
+
+ proxy.ComplexMethodRemote('input string', (value, error, fdList) => {
+
+ // If @error is not `null`, then an error occurred
+ if (error !== null) {
+ logError(error);
+ return;
+ }
+
+ print(`ComplexMethod: ${value}`);
+
+ // Methods that return file descriptors are fairly rare, so you should
+ // know to expect one or not.
+ if (fdList !== null) {
+ //
+ }
+ });
+}
+
+function onNameVanished(connection, name) {
+ print(`"${name}" vanished from the session bus`);
+
+ if (proxy !== null) {
+ proxy.disconnectSignal(proxySignalId);
+ proxy.disconnect(proxyPropId);
+ proxy = null;
+ }
+}
+
+let busWatchId = Gio.bus_watch_name(
+ Gio.BusType.SESSION,
+ 'org.gnome.gjs.Test',
+ Gio.BusNameWatcherFlags.NONE,
+ onNameAppeared,
+ onNameVanished
+);
+
+// Start an event loop
+let loop = GLib.MainLoop.new(null, false);
+loop.run();
+
+// Unwatching names works just like disconnecting signal handlers.
+Gio.bus_unown_name(busWatchId);
+
+
+/* Asynchronous Usage
+ *
+ * Below is the alternative, asynchronous usage of proxy wrappers. If creating
+ * a proxy asynchronously, you should not consider the proxy ready to use until
+ * the callback is invoked without error.
+ */
+proxy = null;
+
+new TestProxy(
+ Gio.DBus.session,
+ 'org.gnome.gjs.Test',
+ '/org/gnome/gjs/Test',
+ (sourceObj, error) => {
+ // If @error is not `null` it will be an Error object indicating the
+ // failure. @proxy will be `null` in this case.
+ if (error !== null) {
+ logError(error);
+ return;
+ }
+
+ // At this point the proxy is initialized and you can start calling
+ // functions, using properties and so on.
+ proxy = sourceObj;
+ print(`ReadOnlyProperty: ${proxy.ReadOnlyProperty}`);
+ },
+ // Optional Gio.Cancellable object. Pass `null` if you need to pass flags.
+ null,
+ // Optional flags passed to the Gio.DBusProxy constructor
+ Gio.DBusProxyFlags.NONE
+);
+
diff --git a/examples/dbus-service.js b/examples/dbus-service.js
new file mode 100644
index 00000000..493ebe54
--- /dev/null
+++ b/examples/dbus-service.js
@@ -0,0 +1,137 @@
+'use strict';
+
+const GLib = imports.gi.GLib;
+const Gio = imports.gi.Gio;
+
+
+/*
+ * An XML DBus Interface
+ */
+const ifaceXml = `
+<node>
+ <interface name="org.gnome.gjs.Test">
+ <method name="SimpleMethod"/>
+ <method name="ComplexMethod">
+ <arg type="s" direction="in" name="input"/>
+ <arg type="u" direction="out" name="length"/>
+ </method>
+ <signal name="TestSignal">
+ <arg name="type" type="s"/>
+ <arg name="value" type="b"/>
+ </signal>
+ <property name="ReadOnlyProperty" type="s" access="read"/>
+ <property name="ReadWriteProperty" type="b" access="readwrite"/>
+ </interface>
+</node>`;
+
+
+// An example of the service-side implementation of the above interface.
+class Service {
+
+ constructor() {
+ this.dbus = Gio.DBusExportedObject.wrapJSObject(ifaceXml, this);
+ }
+
+ // Properties
+ get ReadOnlyProperty() {
+ return 'a string';
+ }
+
+ get ReadWriteProperty() {
+ if (this._readWriteProperty === undefined)
+ return false;
+
+ return this._readWriteProperty;
+ }
+
+ set ReadWriteProperty(value) {
+ if (this.ReadWriteProperty !== value) {
+ this._readWriteProperty = value;
+
+ // Emitting property changes over DBus
+ this.dbus.emit_property_changed(
+ 'ReadWriteProperty',
+ new GLib.Variant('b', value)
+ );
+ }
+ }
+
+ // Methods
+ SimpleMethod() {
+ print('SimpleMethod() invoked');
+ }
+
+ ComplexMethod(input) {
+ print(`ComplexMethod() invoked with "${input}"`);
+
+ return input.length;
+ }
+
+ // Signals
+ emitTestSignal() {
+ this.dbus.emit_signal(
+ 'TestSignal',
+ new GLib.Variant('(sb)', ['string', false])
+ );
+ }
+}
+
+
+// Once you've created an instance of your service, you will want to own a name
+// on the bus so clients can connect to it.
+let serviceObj = new Service();
+let serviceSignalId = 0;
+
+
+function onBusAcquired(connection, _name) {
+ // At this point you have acquired a connection to the bus, and you should
+ // export your interfaces now.
+ serviceObj.dbus.export(connection, '/org/gnome/gjs/Test');
+}
+
+function onNameAcquired(_connection, _name) {
+ // Clients will typically start connecting and using your interface now.
+
+ // Emit the TestSignal every few seconds
+ serviceSignalId = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 3, () => {
+ serviceObj.emitTestSignal();
+
+ return GLib.SOURCE_CONTINUE;
+ });
+}
+
+function onNameLost(_connection, _name) {
+ // Clients will know not to call methods on your interface now. Usually this
+ // callback will only be invoked if you try to own a name on DBus that
+ // already has an owner.
+
+ // Stop emitting the test signal
+ if (serviceSignalId > 0) {
+ GLib.Source.remove(serviceSignalId);
+ serviceSignalId = 0;
+ }
+}
+
+let ownerId = Gio.bus_own_name(
+ Gio.BusType.SESSION,
+ 'org.gnome.gjs.Test',
+ Gio.BusNameOwnerFlags.NONE,
+ onBusAcquired,
+ onNameAcquired,
+ onNameLost
+);
+
+
+// Start an event loop
+let loop = GLib.MainLoop.new(null, false);
+loop.run();
+
+// Unowning names works just like disconnecting, but note that `onNameLost()`
+// will not be invoked in this case.
+Gio.bus_unown_name(ownerId);
+
+if (serviceSignalId > 0) {
+ GLib.Source.remove(serviceSignalId);
+ serviceSignalId = 0;
+}
+
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]