summaryrefslogtreecommitdiff
path: root/examples/dbus-client.js
blob: e9d1aeafc1eeae6274eff7fce15b37ce3575deea (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
// SPDX-FileCopyrightText: 2020 Andy Holmes <andrew.g.r.holmes@gmail.com>

import GLib from 'gi://GLib';
import Gio from '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 (err) {
        logError(err);
        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 (err) {
        logError(`SimpleMethod: ${err.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
);