@gjsify/node-gi 0.26.1 → 0.27.0
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.
- package/README.md +57 -12
- package/cairo.d.ts +121 -121
- package/cairo.js +425 -426
- package/gettext.d.ts +24 -24
- package/gettext.js +42 -42
- package/gi.d.ts +170 -122
- package/gi.js +1449 -1460
- package/globals.d.ts +28 -28
- package/globals.js +103 -97
- package/gtk-runtime.js +9 -5
- package/index.d.ts +133 -152
- package/index.js +82 -100
- package/native-paths.js +104 -0
- package/overrides/_signals.js +108 -111
- package/overrides/byte-array.js +135 -135
- package/overrides/gio-dbus.js +681 -658
- package/overrides/mainloop.js +27 -27
- package/overrides/package.js +128 -128
- package/package.json +5 -2
- package/prebuilds/darwin-arm64/node_gi.node +0 -0
- package/prebuilds/darwin-x64/node_gi.node +0 -0
- package/prebuilds/win32-x64/node_gi.node +0 -0
- package/scripts/install.mjs +238 -0
- package/scripts/win-gi-gyp-flags.mjs +2 -11
- package/system.d.ts +13 -13
- package/system.js +35 -35
package/overrides/gio-dbus.js
CHANGED
|
@@ -34,14 +34,14 @@ import { addSignalMethods, _connect, _disconnect, _emit } from './_signals.js';
|
|
|
34
34
|
// mixin never ReferenceErrors. Idempotent; globals.js's richer versions win if
|
|
35
35
|
// already present.
|
|
36
36
|
function ensureAmbientLog() {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
37
|
+
const g = globalThis;
|
|
38
|
+
if (typeof g.log === 'undefined') g.log = (...args) => console.error(args.map((a) => String(a)).join(' '));
|
|
39
|
+
if (typeof g.logError === 'undefined') {
|
|
40
|
+
g.logError = (error, prefix) => {
|
|
41
|
+
const head = prefix ? `${prefix}: ` : '';
|
|
42
|
+
console.error(head + (error && error.stack ? error.stack : String(error)));
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
/**
|
|
@@ -54,696 +54,719 @@ function ensureAmbientLog() {
|
|
|
54
54
|
* @returns {{ DBus: object, DBusProxy: object, DBusExportedObject: object }}
|
|
55
55
|
*/
|
|
56
56
|
export function createGioDBus(ctx) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
57
|
+
const { Gio, GLib, unwrap, native } = ctx;
|
|
58
|
+
// Scoped to actual DBus usage (first Gio.DBus* access), so a plain non-DBus
|
|
59
|
+
// `import { requireGi }` adds no global side effects.
|
|
60
|
+
ensureAmbientLog();
|
|
61
|
+
|
|
62
|
+
// DBus RequestName reply codes (org.freedesktop.DBus).
|
|
63
|
+
const REQUEST_NAME_PRIMARY_OWNER = 1;
|
|
64
|
+
const REQUEST_NAME_ALREADY_OWNER = 4;
|
|
65
|
+
const DBUS_NAME = 'org.freedesktop.DBus';
|
|
66
|
+
const DBUS_PATH = '/org/freedesktop/DBus';
|
|
67
|
+
|
|
68
|
+
function safeCallback(fn, ...args) {
|
|
69
|
+
try {
|
|
70
|
+
fn(...args);
|
|
71
|
+
} catch (e) {
|
|
72
|
+
logError(e, 'Exception in DBus name callback');
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Is `value` a wrapped node-gi instance of Gio.<typeName>? Used to classify the
|
|
77
|
+
// trailing flags/Cancellable/UnixFDList args a proxy method call may carry.
|
|
78
|
+
function isGioInstance(value, typeName) {
|
|
79
|
+
if (value === null || typeof value !== 'object') return false;
|
|
80
|
+
const handle = unwrap(value);
|
|
81
|
+
if (handle === value) return false; // not a wrapped instance
|
|
82
|
+
try {
|
|
83
|
+
return native.isInstanceOf(handle, 'Gio', typeName);
|
|
84
|
+
} catch {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function _logReply(result, exc) {
|
|
90
|
+
if (exc !== null && exc !== undefined) log(`Ignored exception from dbus method: ${exc}`);
|
|
73
91
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
92
|
+
|
|
93
|
+
// Ported from refs/gjs Gio.js `_proxyInvoker`. `wrappedProxy` is captured (GJS
|
|
94
|
+
// uses `this`) because node-gi delivers a RAW source handle to the async ready
|
|
95
|
+
// callback, so the wrapper must come from the closure to reach the L1 finish.
|
|
96
|
+
function _proxyInvoker(wrappedProxy, methodName, sync, inSignature, args) {
|
|
97
|
+
let replyFunc = _logReply;
|
|
98
|
+
let flags = 0;
|
|
99
|
+
let cancellable = null;
|
|
100
|
+
let fdList = null;
|
|
101
|
+
const argArray = Array.prototype.slice.call(args);
|
|
102
|
+
|
|
103
|
+
const signatureLength = inSignature.length;
|
|
104
|
+
const minNumberArgs = signatureLength;
|
|
105
|
+
const maxNumberArgs = signatureLength + 4;
|
|
106
|
+
if (argArray.length < minNumberArgs) {
|
|
107
|
+
throw new Error(
|
|
108
|
+
`Not enough arguments passed for method: ${methodName}. Expected ${minNumberArgs}, got ${argArray.length}`,
|
|
109
|
+
);
|
|
110
|
+
} else if (argArray.length > maxNumberArgs) {
|
|
111
|
+
throw new Error(
|
|
112
|
+
`Too many arguments passed for method ${methodName}. Maximum is ${maxNumberArgs} ` +
|
|
113
|
+
'including one callback, Gio.Cancellable, Gio.UnixFDList, and/or flags',
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
while (argArray.length > signatureLength) {
|
|
118
|
+
const argNum = argArray.length - 1;
|
|
119
|
+
const arg = argArray.pop();
|
|
120
|
+
if (typeof arg === 'function' && !sync) replyFunc = arg;
|
|
121
|
+
else if (typeof arg === 'number') flags = arg;
|
|
122
|
+
else if (isGioInstance(arg, 'Cancellable')) cancellable = arg;
|
|
123
|
+
else if (isGioInstance(arg, 'UnixFDList')) fdList = arg;
|
|
124
|
+
else {
|
|
125
|
+
throw new Error(
|
|
126
|
+
`Argument ${argNum} of method ${methodName} is ${typeof arg}. It should be a callback, ` +
|
|
127
|
+
'flags, Gio.UnixFDList, or a Gio.Cancellable',
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const inTypeString = `(${inSignature.join('')})`;
|
|
133
|
+
const inVariant = new GLib.Variant(inTypeString, argArray);
|
|
134
|
+
|
|
135
|
+
// GJS always routes through call_with_unix_fd_list[_sync/_finish] (a superset
|
|
136
|
+
// that also carries GUnixFDList fd-passing). Those live in GioUnix and are ABSENT
|
|
137
|
+
// from the Gio typelib on Windows, so fall back to the cross-platform
|
|
138
|
+
// call[_sync/_finish] there — fd-passing is a Unix concept, so there is nothing to
|
|
139
|
+
// carry. Detection is by method presence (node-gi's L1 wrapper returns `undefined`
|
|
140
|
+
// for an absent method), keeping POSIX byte-identical: where the method exists the
|
|
141
|
+
// fd-list path is taken exactly as before.
|
|
142
|
+
if (sync) {
|
|
143
|
+
if (typeof wrappedProxy.call_with_unix_fd_list_sync === 'function') {
|
|
144
|
+
const result = wrappedProxy.call_with_unix_fd_list_sync(
|
|
145
|
+
methodName,
|
|
146
|
+
inVariant,
|
|
147
|
+
flags,
|
|
148
|
+
-1,
|
|
149
|
+
fdList,
|
|
150
|
+
cancellable,
|
|
151
|
+
);
|
|
152
|
+
const outVariant = Array.isArray(result) ? result[0] : result;
|
|
153
|
+
const outFdList = Array.isArray(result) ? result[1] : null;
|
|
154
|
+
if (fdList) return [outVariant.deepUnpack(), outFdList];
|
|
155
|
+
return outVariant.deepUnpack();
|
|
156
|
+
}
|
|
157
|
+
if (fdList) throw new Error(`${methodName}: Gio.UnixFDList fd-passing is not supported on this platform`);
|
|
158
|
+
const outVariant = wrappedProxy.call_sync(methodName, inVariant, flags, -1, cancellable);
|
|
159
|
+
return outVariant.deepUnpack();
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (typeof wrappedProxy.call_with_unix_fd_list === 'function') {
|
|
163
|
+
const asyncCallback = (_rawSource, res) => {
|
|
164
|
+
try {
|
|
165
|
+
const result = wrappedProxy.call_with_unix_fd_list_finish(res);
|
|
166
|
+
const outVariant = Array.isArray(result) ? result[0] : result;
|
|
167
|
+
const outFdList = Array.isArray(result) ? result[1] : null;
|
|
168
|
+
replyFunc(outVariant.deepUnpack(), null, outFdList);
|
|
169
|
+
} catch (e) {
|
|
170
|
+
replyFunc([], e, null);
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
return wrappedProxy.call_with_unix_fd_list(
|
|
174
|
+
methodName,
|
|
175
|
+
inVariant,
|
|
176
|
+
flags,
|
|
177
|
+
-1,
|
|
178
|
+
fdList,
|
|
179
|
+
cancellable,
|
|
180
|
+
asyncCallback,
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
if (fdList) throw new Error(`${methodName}: Gio.UnixFDList fd-passing is not supported on this platform`);
|
|
184
|
+
const asyncCallback = (_rawSource, res) => {
|
|
185
|
+
try {
|
|
186
|
+
const outVariant = wrappedProxy.call_finish(res);
|
|
187
|
+
replyFunc(outVariant.deepUnpack(), null, null);
|
|
188
|
+
} catch (e) {
|
|
189
|
+
replyFunc([], e, null);
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
return wrappedProxy.call(methodName, inVariant, flags, -1, cancellable, asyncCallback);
|
|
86
193
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
const argArray = Array.prototype.slice.call(args);
|
|
102
|
-
|
|
103
|
-
const signatureLength = inSignature.length;
|
|
104
|
-
const minNumberArgs = signatureLength;
|
|
105
|
-
const maxNumberArgs = signatureLength + 4;
|
|
106
|
-
if (argArray.length < minNumberArgs) {
|
|
107
|
-
throw new Error(
|
|
108
|
-
`Not enough arguments passed for method: ${methodName}. Expected ${minNumberArgs}, got ${argArray.length}`,
|
|
109
|
-
);
|
|
110
|
-
} else if (argArray.length > maxNumberArgs) {
|
|
111
|
-
throw new Error(
|
|
112
|
-
`Too many arguments passed for method ${methodName}. Maximum is ${maxNumberArgs} ` +
|
|
113
|
-
'including one callback, Gio.Cancellable, Gio.UnixFDList, and/or flags',
|
|
114
|
-
);
|
|
194
|
+
|
|
195
|
+
function _makeProxyMethod(wrappedProxy, method, sync) {
|
|
196
|
+
const name = method.name;
|
|
197
|
+
const inArgs = method.in_args;
|
|
198
|
+
const inSignature = [];
|
|
199
|
+
for (let i = 0; i < inArgs.length; i++) inSignature.push(inArgs[i].signature);
|
|
200
|
+
return function proxyMethod(...args) {
|
|
201
|
+
return _proxyInvoker(wrappedProxy, name, sync, inSignature, args);
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function _propertyGetter(wrappedProxy, name) {
|
|
206
|
+
const value = wrappedProxy.get_cached_property(name);
|
|
207
|
+
return value ? value.deepUnpack() : null;
|
|
115
208
|
}
|
|
116
209
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
210
|
+
function _propertySetter(wrappedProxy, name, signature, value) {
|
|
211
|
+
const variant = new GLib.Variant(signature, value);
|
|
212
|
+
wrappedProxy.set_cached_property(name, variant);
|
|
213
|
+
wrappedProxy.call(
|
|
214
|
+
'org.freedesktop.DBus.Properties.Set',
|
|
215
|
+
new GLib.Variant('(ssv)', [wrappedProxy.g_interface_name, name, variant]),
|
|
216
|
+
Gio.DBusCallFlags.NONE,
|
|
217
|
+
-1,
|
|
218
|
+
null,
|
|
219
|
+
(_rawSource, res) => {
|
|
220
|
+
try {
|
|
221
|
+
wrappedProxy.call_finish(res);
|
|
222
|
+
} catch (e) {
|
|
223
|
+
log(`Could not set property ${name} on remote object ${wrappedProxy.g_object_path}: ${e.message}`);
|
|
224
|
+
}
|
|
225
|
+
},
|
|
128
226
|
);
|
|
129
|
-
}
|
|
130
227
|
}
|
|
131
228
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
229
|
+
// Attach the method/property/signal convenience to a freshly-inited DBusProxy
|
|
230
|
+
// instance wrapper. Ported from refs/gjs Gio.js `_addDBusConvenience`.
|
|
231
|
+
function _addDBusConvenience(wrappedProxy, info) {
|
|
232
|
+
// The `_signals` mixin bookkeeping lives on a dedicated plain emitter (see the
|
|
233
|
+
// module header): connectSignal/disconnectSignal delegate to it.
|
|
234
|
+
const emitter = {};
|
|
235
|
+
addSignalMethods(emitter);
|
|
236
|
+
wrappedProxy.connectSignal = (name, callback) => _connect.call(emitter, name, callback);
|
|
237
|
+
wrappedProxy.disconnectSignal = (id) => _disconnect.call(emitter, id);
|
|
238
|
+
|
|
239
|
+
const signals = info.signals;
|
|
240
|
+
if (signals && signals.length > 0) {
|
|
241
|
+
wrappedProxy.connect('g-signal', (_proxy, senderName, signalName, parameters) => {
|
|
242
|
+
_emit.call(emitter, signalName, senderName, parameters.deepUnpack());
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const methods = info.methods;
|
|
247
|
+
for (let i = 0; i < methods.length; i++) {
|
|
248
|
+
const method = methods[i];
|
|
249
|
+
const name = method.name;
|
|
250
|
+
const remoteMethod = _makeProxyMethod(wrappedProxy, method, false);
|
|
251
|
+
wrappedProxy[`${name}Remote`] = remoteMethod;
|
|
252
|
+
wrappedProxy[`${name}Sync`] = _makeProxyMethod(wrappedProxy, method, true);
|
|
253
|
+
wrappedProxy[`${name}Async`] = function proxyAsyncMethod(...args) {
|
|
254
|
+
return new Promise((resolve, reject) => {
|
|
255
|
+
args.push((result, error, fdList) => {
|
|
256
|
+
if (error) reject(error);
|
|
257
|
+
else if (fdList) resolve([result, fdList]);
|
|
258
|
+
else resolve(result);
|
|
259
|
+
});
|
|
260
|
+
remoteMethod(...args);
|
|
261
|
+
});
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const properties = info.properties;
|
|
266
|
+
for (let i = 0; i < properties.length; i++) {
|
|
267
|
+
const propInfo = properties[i];
|
|
268
|
+
const name = propInfo.name;
|
|
269
|
+
const signature = propInfo.signature;
|
|
270
|
+
const propFlags = propInfo.flags;
|
|
271
|
+
let getter = () => {
|
|
272
|
+
throw new Error(`Property ${name} is not readable`);
|
|
273
|
+
};
|
|
274
|
+
let setter = () => {
|
|
275
|
+
throw new Error(`Property ${name} is not writable`);
|
|
276
|
+
};
|
|
277
|
+
if (propFlags & Gio.DBusPropertyInfoFlags.READABLE) getter = () => _propertyGetter(wrappedProxy, name);
|
|
278
|
+
if (propFlags & Gio.DBusPropertyInfoFlags.WRITABLE) {
|
|
279
|
+
setter = (value) => _propertySetter(wrappedProxy, name, signature, value);
|
|
280
|
+
}
|
|
281
|
+
Object.defineProperty(wrappedProxy, name, {
|
|
282
|
+
get: getter,
|
|
283
|
+
set: setter,
|
|
284
|
+
configurable: false,
|
|
285
|
+
enumerable: true,
|
|
286
|
+
});
|
|
287
|
+
}
|
|
153
288
|
}
|
|
154
289
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
290
|
+
function _newInterfaceInfo(xml) {
|
|
291
|
+
const nodeInfo = Gio.DBusNodeInfo.new_for_xml(xml);
|
|
292
|
+
return nodeInfo.interfaces[0];
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Gio.DBusProxy.makeProxyWrapper(interfaceXml). Returns a constructor:
|
|
296
|
+
// new ProxyClass(bus, name, objectPath[, asyncCallback[, cancellable[, flags]]])
|
|
297
|
+
// and a static `ProxyClass.newAsync(bus, name, objectPath[, cancellable[, flags]])`.
|
|
298
|
+
function makeProxyWrapper(interfaceXml) {
|
|
299
|
+
const info = _newInterfaceInfo(interfaceXml);
|
|
300
|
+
const iname = info.name;
|
|
301
|
+
|
|
302
|
+
function ProxyClass(bus, name, object, asyncCallback, cancellable = null, flags = Gio.DBusProxyFlags.NONE) {
|
|
303
|
+
const obj = new Gio.DBusProxy({
|
|
304
|
+
g_connection: bus,
|
|
305
|
+
g_interface_name: iname,
|
|
306
|
+
g_interface_info: info,
|
|
307
|
+
g_name: name,
|
|
308
|
+
g_flags: flags,
|
|
309
|
+
g_object_path: object,
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
if (asyncCallback) {
|
|
313
|
+
obj.init_async(GLib.PRIORITY_DEFAULT, cancellable, (_rawSource, res) => {
|
|
314
|
+
try {
|
|
315
|
+
obj.init_finish(res);
|
|
316
|
+
_addDBusConvenience(obj, info);
|
|
317
|
+
asyncCallback(obj, null);
|
|
318
|
+
} catch (e) {
|
|
319
|
+
asyncCallback(null, e);
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
} else {
|
|
323
|
+
obj.init(cancellable);
|
|
324
|
+
_addDBusConvenience(obj, info);
|
|
325
|
+
}
|
|
326
|
+
return obj;
|
|
164
327
|
}
|
|
165
|
-
|
|
166
|
-
|
|
328
|
+
|
|
329
|
+
ProxyClass.newAsync = function newAsync(
|
|
330
|
+
bus,
|
|
331
|
+
name,
|
|
332
|
+
object,
|
|
333
|
+
cancellable = null,
|
|
334
|
+
flags = Gio.DBusProxyFlags.NONE,
|
|
335
|
+
) {
|
|
336
|
+
const obj = new Gio.DBusProxy({
|
|
337
|
+
g_connection: bus,
|
|
338
|
+
g_interface_name: info.name,
|
|
339
|
+
g_interface_info: info,
|
|
340
|
+
g_name: name,
|
|
341
|
+
g_flags: flags,
|
|
342
|
+
g_object_path: object,
|
|
343
|
+
});
|
|
344
|
+
return new Promise((resolve, reject) => {
|
|
345
|
+
obj.init_async(GLib.PRIORITY_DEFAULT, cancellable, (_rawSource, res) => {
|
|
346
|
+
try {
|
|
347
|
+
obj.init_finish(res);
|
|
348
|
+
_addDBusConvenience(obj, info);
|
|
349
|
+
resolve(obj);
|
|
350
|
+
} catch (e) {
|
|
351
|
+
reject(e);
|
|
352
|
+
}
|
|
353
|
+
});
|
|
354
|
+
});
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
return ProxyClass;
|
|
167
358
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
wrappedProxy.set_cached_property(name, variant);
|
|
198
|
-
wrappedProxy.call(
|
|
199
|
-
'org.freedesktop.DBus.Properties.Set',
|
|
200
|
-
new GLib.Variant('(ssv)', [wrappedProxy.g_interface_name, name, variant]),
|
|
201
|
-
Gio.DBusCallFlags.NONE,
|
|
202
|
-
-1,
|
|
203
|
-
null,
|
|
204
|
-
(_rawSource, res) => {
|
|
359
|
+
|
|
360
|
+
// ---- name owning ----
|
|
361
|
+
const ownedNames = new Map();
|
|
362
|
+
const watchedNames = new Map();
|
|
363
|
+
let nextToken = 1;
|
|
364
|
+
|
|
365
|
+
function busConnection(busType) {
|
|
366
|
+
return Gio.bus_get_sync(busType, null);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
function requestName(connection, name, flags) {
|
|
370
|
+
const reply = connection.call_sync(
|
|
371
|
+
DBUS_NAME,
|
|
372
|
+
DBUS_PATH,
|
|
373
|
+
DBUS_NAME,
|
|
374
|
+
'RequestName',
|
|
375
|
+
new GLib.Variant('(su)', [name, flags >>> 0]),
|
|
376
|
+
null,
|
|
377
|
+
Gio.DBusCallFlags.NONE,
|
|
378
|
+
-1,
|
|
379
|
+
null,
|
|
380
|
+
);
|
|
381
|
+
return reply.deepUnpack()[0];
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
function ownNameCore(connection, name, flags, onBusAcquired, onNameAcquired, onNameLost) {
|
|
385
|
+
const token = nextToken++;
|
|
386
|
+
ownedNames.set(token, { connection, name });
|
|
387
|
+
let code;
|
|
205
388
|
try {
|
|
206
|
-
|
|
389
|
+
code = requestName(connection, name, flags);
|
|
207
390
|
} catch (e) {
|
|
208
|
-
|
|
391
|
+
ownedNames.delete(token);
|
|
392
|
+
throw e;
|
|
209
393
|
}
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
wrappedProxy.disconnectSignal = (id) => _disconnect.call(emitter, id);
|
|
223
|
-
|
|
224
|
-
const signals = info.signals;
|
|
225
|
-
if (signals && signals.length > 0) {
|
|
226
|
-
wrappedProxy.connect('g-signal', (_proxy, senderName, signalName, parameters) => {
|
|
227
|
-
_emit.call(emitter, signalName, senderName, parameters.deepUnpack());
|
|
228
|
-
});
|
|
394
|
+
// GJS fires the acquired/lost callbacks from the main loop, never synchronously.
|
|
395
|
+
GLib.idle_add(GLib.PRIORITY_DEFAULT, () => {
|
|
396
|
+
if (typeof onBusAcquired === 'function') safeCallback(onBusAcquired, connection, name);
|
|
397
|
+
const acquired = code === REQUEST_NAME_PRIMARY_OWNER || code === REQUEST_NAME_ALREADY_OWNER;
|
|
398
|
+
if (acquired) {
|
|
399
|
+
if (typeof onNameAcquired === 'function') safeCallback(onNameAcquired, connection, name);
|
|
400
|
+
} else if (typeof onNameLost === 'function') {
|
|
401
|
+
safeCallback(onNameLost, connection, name);
|
|
402
|
+
}
|
|
403
|
+
return false; // G_SOURCE_REMOVE
|
|
404
|
+
});
|
|
405
|
+
return token;
|
|
229
406
|
}
|
|
230
407
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
const method = methods[i];
|
|
234
|
-
const name = method.name;
|
|
235
|
-
const remoteMethod = _makeProxyMethod(wrappedProxy, method, false);
|
|
236
|
-
wrappedProxy[`${name}Remote`] = remoteMethod;
|
|
237
|
-
wrappedProxy[`${name}Sync`] = _makeProxyMethod(wrappedProxy, method, true);
|
|
238
|
-
wrappedProxy[`${name}Async`] = function proxyAsyncMethod(...args) {
|
|
239
|
-
return new Promise((resolve, reject) => {
|
|
240
|
-
args.push((result, error, fdList) => {
|
|
241
|
-
if (error) reject(error);
|
|
242
|
-
else if (fdList) resolve([result, fdList]);
|
|
243
|
-
else resolve(result);
|
|
244
|
-
});
|
|
245
|
-
remoteMethod(...args);
|
|
246
|
-
});
|
|
247
|
-
};
|
|
408
|
+
function own_name(busType, name, flags, onBusAcquired, onNameAcquired, onNameLost) {
|
|
409
|
+
return ownNameCore(busConnection(busType), name, flags, onBusAcquired, onNameAcquired, onNameLost);
|
|
248
410
|
}
|
|
249
411
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
const propInfo = properties[i];
|
|
253
|
-
const name = propInfo.name;
|
|
254
|
-
const signature = propInfo.signature;
|
|
255
|
-
const propFlags = propInfo.flags;
|
|
256
|
-
let getter = () => {
|
|
257
|
-
throw new Error(`Property ${name} is not readable`);
|
|
258
|
-
};
|
|
259
|
-
let setter = () => {
|
|
260
|
-
throw new Error(`Property ${name} is not writable`);
|
|
261
|
-
};
|
|
262
|
-
if (propFlags & Gio.DBusPropertyInfoFlags.READABLE) getter = () => _propertyGetter(wrappedProxy, name);
|
|
263
|
-
if (propFlags & Gio.DBusPropertyInfoFlags.WRITABLE) {
|
|
264
|
-
setter = (value) => _propertySetter(wrappedProxy, name, signature, value);
|
|
265
|
-
}
|
|
266
|
-
Object.defineProperty(wrappedProxy, name, {
|
|
267
|
-
get: getter,
|
|
268
|
-
set: setter,
|
|
269
|
-
configurable: false,
|
|
270
|
-
enumerable: true,
|
|
271
|
-
});
|
|
412
|
+
function own_name_on_connection(connection, name, flags, onNameAcquired, onNameLost) {
|
|
413
|
+
return ownNameCore(connection, name, flags, undefined, onNameAcquired, onNameLost);
|
|
272
414
|
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
415
|
+
|
|
416
|
+
function unown_name(token) {
|
|
417
|
+
const entry = ownedNames.get(token);
|
|
418
|
+
if (!entry) return;
|
|
419
|
+
ownedNames.delete(token);
|
|
420
|
+
try {
|
|
421
|
+
entry.connection.call_sync(
|
|
422
|
+
DBUS_NAME,
|
|
423
|
+
DBUS_PATH,
|
|
424
|
+
DBUS_NAME,
|
|
425
|
+
'ReleaseName',
|
|
426
|
+
new GLib.Variant('(s)', [entry.name]),
|
|
427
|
+
null,
|
|
428
|
+
Gio.DBusCallFlags.NONE,
|
|
429
|
+
-1,
|
|
430
|
+
null,
|
|
431
|
+
);
|
|
432
|
+
} catch {
|
|
433
|
+
/* connection already gone — nothing to release */
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// ---- name watching (NameOwnerChanged subscription + GetNameOwner probe) ----
|
|
438
|
+
function getNameOwner(connection, name) {
|
|
439
|
+
try {
|
|
440
|
+
const reply = connection.call_sync(
|
|
441
|
+
DBUS_NAME,
|
|
442
|
+
DBUS_PATH,
|
|
443
|
+
DBUS_NAME,
|
|
444
|
+
'GetNameOwner',
|
|
445
|
+
new GLib.Variant('(s)', [name]),
|
|
446
|
+
null,
|
|
447
|
+
Gio.DBusCallFlags.NONE,
|
|
448
|
+
-1,
|
|
449
|
+
null,
|
|
450
|
+
);
|
|
451
|
+
return reply.deepUnpack()[0];
|
|
452
|
+
} catch {
|
|
453
|
+
return null; // NameHasNoOwner
|
|
454
|
+
}
|
|
312
455
|
}
|
|
313
456
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
457
|
+
function watchNameCore(connection, name, onNameAppeared, onNameVanished) {
|
|
458
|
+
const token = nextToken++;
|
|
459
|
+
let lastOwner = null;
|
|
460
|
+
|
|
461
|
+
const dispatch = () => {
|
|
462
|
+
const owner = getNameOwner(connection, name);
|
|
463
|
+
if (owner && owner.length > 0) {
|
|
464
|
+
if (owner !== lastOwner && typeof onNameAppeared === 'function')
|
|
465
|
+
safeCallback(onNameAppeared, connection, name, owner);
|
|
466
|
+
lastOwner = owner;
|
|
467
|
+
} else {
|
|
468
|
+
if (lastOwner !== null && typeof onNameVanished === 'function')
|
|
469
|
+
safeCallback(onNameVanished, connection, name);
|
|
470
|
+
lastOwner = null;
|
|
471
|
+
}
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
// arg0-filtered subscription: NameOwnerChanged whose first arg is `name`.
|
|
475
|
+
const subId = connection.signal_subscribe(
|
|
476
|
+
DBUS_NAME,
|
|
477
|
+
DBUS_NAME,
|
|
478
|
+
'NameOwnerChanged',
|
|
479
|
+
DBUS_PATH,
|
|
480
|
+
name,
|
|
481
|
+
Gio.DBusSignalFlags.NONE,
|
|
482
|
+
() => dispatch(),
|
|
483
|
+
);
|
|
484
|
+
watchedNames.set(token, { connection, subId });
|
|
485
|
+
|
|
486
|
+
// Initial state, delivered from the loop like g_bus_watch_name's GetNameOwner.
|
|
487
|
+
GLib.idle_add(GLib.PRIORITY_DEFAULT, () => {
|
|
488
|
+
const owner = getNameOwner(connection, name);
|
|
489
|
+
if (owner && owner.length > 0) {
|
|
490
|
+
lastOwner = owner;
|
|
491
|
+
if (typeof onNameAppeared === 'function') safeCallback(onNameAppeared, connection, name, owner);
|
|
492
|
+
} else if (typeof onNameVanished === 'function') {
|
|
493
|
+
safeCallback(onNameVanished, connection, name);
|
|
494
|
+
}
|
|
495
|
+
return false; // G_SOURCE_REMOVE
|
|
332
496
|
});
|
|
333
|
-
|
|
334
|
-
}
|
|
497
|
+
return token;
|
|
498
|
+
}
|
|
335
499
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
// ---- name owning ----
|
|
340
|
-
const ownedNames = new Map();
|
|
341
|
-
const watchedNames = new Map();
|
|
342
|
-
let nextToken = 1;
|
|
343
|
-
|
|
344
|
-
function busConnection(busType) {
|
|
345
|
-
return Gio.bus_get_sync(busType, null);
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
function requestName(connection, name, flags) {
|
|
349
|
-
const reply = connection.call_sync(
|
|
350
|
-
DBUS_NAME,
|
|
351
|
-
DBUS_PATH,
|
|
352
|
-
DBUS_NAME,
|
|
353
|
-
'RequestName',
|
|
354
|
-
new GLib.Variant('(su)', [name, flags >>> 0]),
|
|
355
|
-
null,
|
|
356
|
-
Gio.DBusCallFlags.NONE,
|
|
357
|
-
-1,
|
|
358
|
-
null,
|
|
359
|
-
);
|
|
360
|
-
return reply.deepUnpack()[0];
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
function ownNameCore(connection, name, flags, onBusAcquired, onNameAcquired, onNameLost) {
|
|
364
|
-
const token = nextToken++;
|
|
365
|
-
ownedNames.set(token, { connection, name });
|
|
366
|
-
let code;
|
|
367
|
-
try {
|
|
368
|
-
code = requestName(connection, name, flags);
|
|
369
|
-
} catch (e) {
|
|
370
|
-
ownedNames.delete(token);
|
|
371
|
-
throw e;
|
|
500
|
+
function watch_name(busType, name, _flags, onNameAppeared, onNameVanished) {
|
|
501
|
+
return watchNameCore(busConnection(busType), name, onNameAppeared, onNameVanished);
|
|
372
502
|
}
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
const acquired = code === REQUEST_NAME_PRIMARY_OWNER || code === REQUEST_NAME_ALREADY_OWNER;
|
|
377
|
-
if (acquired) {
|
|
378
|
-
if (typeof onNameAcquired === 'function') safeCallback(onNameAcquired, connection, name);
|
|
379
|
-
} else if (typeof onNameLost === 'function') {
|
|
380
|
-
safeCallback(onNameLost, connection, name);
|
|
381
|
-
}
|
|
382
|
-
return false; // G_SOURCE_REMOVE
|
|
383
|
-
});
|
|
384
|
-
return token;
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
function own_name(busType, name, flags, onBusAcquired, onNameAcquired, onNameLost) {
|
|
388
|
-
return ownNameCore(busConnection(busType), name, flags, onBusAcquired, onNameAcquired, onNameLost);
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
function own_name_on_connection(connection, name, flags, onNameAcquired, onNameLost) {
|
|
392
|
-
return ownNameCore(connection, name, flags, undefined, onNameAcquired, onNameLost);
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
function unown_name(token) {
|
|
396
|
-
const entry = ownedNames.get(token);
|
|
397
|
-
if (!entry) return;
|
|
398
|
-
ownedNames.delete(token);
|
|
399
|
-
try {
|
|
400
|
-
entry.connection.call_sync(
|
|
401
|
-
DBUS_NAME,
|
|
402
|
-
DBUS_PATH,
|
|
403
|
-
DBUS_NAME,
|
|
404
|
-
'ReleaseName',
|
|
405
|
-
new GLib.Variant('(s)', [entry.name]),
|
|
406
|
-
null,
|
|
407
|
-
Gio.DBusCallFlags.NONE,
|
|
408
|
-
-1,
|
|
409
|
-
null,
|
|
410
|
-
);
|
|
411
|
-
} catch {
|
|
412
|
-
/* connection already gone — nothing to release */
|
|
503
|
+
|
|
504
|
+
function watch_name_on_connection(connection, name, _flags, onNameAppeared, onNameVanished) {
|
|
505
|
+
return watchNameCore(connection, name, onNameAppeared, onNameVanished);
|
|
413
506
|
}
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
new GLib.Variant('(s)', [name]),
|
|
425
|
-
null,
|
|
426
|
-
Gio.DBusCallFlags.NONE,
|
|
427
|
-
-1,
|
|
428
|
-
null,
|
|
429
|
-
);
|
|
430
|
-
return reply.deepUnpack()[0];
|
|
431
|
-
} catch {
|
|
432
|
-
return null; // NameHasNoOwner
|
|
507
|
+
|
|
508
|
+
function unwatch_name(token) {
|
|
509
|
+
const entry = watchedNames.get(token);
|
|
510
|
+
if (!entry) return;
|
|
511
|
+
watchedNames.delete(token);
|
|
512
|
+
try {
|
|
513
|
+
entry.connection.signal_unsubscribe(entry.subId);
|
|
514
|
+
} catch {
|
|
515
|
+
/* connection already gone */
|
|
516
|
+
}
|
|
433
517
|
}
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
if (lastOwner !== null && typeof onNameVanished === 'function') safeCallback(onNameVanished, connection, name);
|
|
447
|
-
lastOwner = null;
|
|
448
|
-
}
|
|
518
|
+
|
|
519
|
+
// ---- Gio.DBus namespace ----
|
|
520
|
+
const DBus = {
|
|
521
|
+
get: Gio.bus_get,
|
|
522
|
+
get_finish: Gio.bus_get_finish,
|
|
523
|
+
get_sync: Gio.bus_get_sync,
|
|
524
|
+
own_name,
|
|
525
|
+
own_name_on_connection,
|
|
526
|
+
unown_name,
|
|
527
|
+
watch_name,
|
|
528
|
+
watch_name_on_connection,
|
|
529
|
+
unwatch_name,
|
|
449
530
|
};
|
|
531
|
+
Object.defineProperty(DBus, 'session', {
|
|
532
|
+
get() {
|
|
533
|
+
return Gio.bus_get_sync(Gio.BusType.SESSION, null);
|
|
534
|
+
},
|
|
535
|
+
enumerable: false,
|
|
536
|
+
});
|
|
537
|
+
Object.defineProperty(DBus, 'system', {
|
|
538
|
+
get() {
|
|
539
|
+
return Gio.bus_get_sync(Gio.BusType.SYSTEM, null);
|
|
540
|
+
},
|
|
541
|
+
enumerable: false,
|
|
542
|
+
});
|
|
450
543
|
|
|
451
|
-
//
|
|
452
|
-
const
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
);
|
|
461
|
-
watchedNames.set(token, { connection, subId });
|
|
462
|
-
|
|
463
|
-
// Initial state, delivered from the loop like g_bus_watch_name's GetNameOwner.
|
|
464
|
-
GLib.idle_add(GLib.PRIORITY_DEFAULT, () => {
|
|
465
|
-
const owner = getNameOwner(connection, name);
|
|
466
|
-
if (owner && owner.length > 0) {
|
|
467
|
-
lastOwner = owner;
|
|
468
|
-
if (typeof onNameAppeared === 'function') safeCallback(onNameAppeared, connection, name, owner);
|
|
469
|
-
} else if (typeof onNameVanished === 'function') {
|
|
470
|
-
safeCallback(onNameVanished, connection, name);
|
|
471
|
-
}
|
|
472
|
-
return false; // G_SOURCE_REMOVE
|
|
544
|
+
// ---- Gio.DBusProxy (adds the static makeProxyWrapper to the introspected class) ----
|
|
545
|
+
const DBusProxy = new Proxy(Gio.DBusProxy, {
|
|
546
|
+
get(target, prop) {
|
|
547
|
+
if (prop === 'makeProxyWrapper') return makeProxyWrapper;
|
|
548
|
+
return target[prop];
|
|
549
|
+
},
|
|
550
|
+
has(target, prop) {
|
|
551
|
+
return prop === 'makeProxyWrapper' || prop in target;
|
|
552
|
+
},
|
|
473
553
|
});
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
554
|
+
|
|
555
|
+
// ---- Gio.DBusExportedObject (export side) ---------------------------------
|
|
556
|
+
//
|
|
557
|
+
// Ported from refs/gjs Gio.js (_wrapJSObject + _handleMethodCall /
|
|
558
|
+
// _handlePropertyGet / _handlePropertySet). GJS builds this on the C type
|
|
559
|
+
// GjsPrivate.DBusImplementation (a GObject with handle-method-call /
|
|
560
|
+
// handle-property-get / handle-property-set signals); node-gi has no such type,
|
|
561
|
+
// so it instead drives the INTROSPECTABLE
|
|
562
|
+
// g_dbus_connection_register_object_with_closures2 — three GClosures whose
|
|
563
|
+
// marshal the engine now supports. The closure param shapes match glib's
|
|
564
|
+
// register_with_closures_on_* trampolines (gio/gdbusconnection.c):
|
|
565
|
+
// method_call: (conn, sender, path, iface, method, params:Variant, invocation)
|
|
566
|
+
// get_property: (conn, sender, path, iface, prop) -> Variant|null
|
|
567
|
+
// set_property: (conn, sender, path, iface, prop, value:Variant) -> boolean
|
|
568
|
+
// Ownership: register_object_data_new refs+sinks its own copy of each closure
|
|
569
|
+
// and frees them on unregister_object, so the JS handler (and everything it
|
|
570
|
+
// captures) lives exactly as long as the registration. The closures2 variant
|
|
571
|
+
// does NOT transfer the invocation ref to the closure — the engine's
|
|
572
|
+
// transfer-full-instance handling refs the invocation before a return_* call,
|
|
573
|
+
// matching gjs's introspection-annotation-driven behaviour.
|
|
574
|
+
|
|
575
|
+
function _makeOutSignature(args) {
|
|
576
|
+
let ret = '(';
|
|
577
|
+
for (let i = 0; i < args.length; i++) ret += args[i].signature;
|
|
578
|
+
return `${ret})`;
|
|
493
579
|
}
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
enumerable: false,
|
|
513
|
-
});
|
|
514
|
-
Object.defineProperty(DBus, 'system', {
|
|
515
|
-
get() {
|
|
516
|
-
return Gio.bus_get_sync(Gio.BusType.SYSTEM, null);
|
|
517
|
-
},
|
|
518
|
-
enumerable: false,
|
|
519
|
-
});
|
|
520
|
-
|
|
521
|
-
// ---- Gio.DBusProxy (adds the static makeProxyWrapper to the introspected class) ----
|
|
522
|
-
const DBusProxy = new Proxy(Gio.DBusProxy, {
|
|
523
|
-
get(target, prop) {
|
|
524
|
-
if (prop === 'makeProxyWrapper') return makeProxyWrapper;
|
|
525
|
-
return target[prop];
|
|
526
|
-
},
|
|
527
|
-
has(target, prop) {
|
|
528
|
-
return prop === 'makeProxyWrapper' || prop in target;
|
|
529
|
-
},
|
|
530
|
-
});
|
|
531
|
-
|
|
532
|
-
// ---- Gio.DBusExportedObject (export side) ---------------------------------
|
|
533
|
-
//
|
|
534
|
-
// Ported from refs/gjs Gio.js (_wrapJSObject + _handleMethodCall /
|
|
535
|
-
// _handlePropertyGet / _handlePropertySet). GJS builds this on the C type
|
|
536
|
-
// GjsPrivate.DBusImplementation (a GObject with handle-method-call /
|
|
537
|
-
// handle-property-get / handle-property-set signals); node-gi has no such type,
|
|
538
|
-
// so it instead drives the INTROSPECTABLE
|
|
539
|
-
// g_dbus_connection_register_object_with_closures2 — three GClosures whose
|
|
540
|
-
// marshal the engine now supports. The closure param shapes match glib's
|
|
541
|
-
// register_with_closures_on_* trampolines (gio/gdbusconnection.c):
|
|
542
|
-
// method_call: (conn, sender, path, iface, method, params:Variant, invocation)
|
|
543
|
-
// get_property: (conn, sender, path, iface, prop) -> Variant|null
|
|
544
|
-
// set_property: (conn, sender, path, iface, prop, value:Variant) -> boolean
|
|
545
|
-
// Ownership: register_object_data_new refs+sinks its own copy of each closure
|
|
546
|
-
// and frees them on unregister_object, so the JS handler (and everything it
|
|
547
|
-
// captures) lives exactly as long as the registration. The closures2 variant
|
|
548
|
-
// does NOT transfer the invocation ref to the closure — the engine's
|
|
549
|
-
// transfer-full-instance handling refs the invocation before a return_* call,
|
|
550
|
-
// matching gjs's introspection-annotation-driven behaviour.
|
|
551
|
-
|
|
552
|
-
function _makeOutSignature(args) {
|
|
553
|
-
let ret = '(';
|
|
554
|
-
for (let i = 0; i < args.length; i++) ret += args[i].signature;
|
|
555
|
-
return `${ret})`;
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
function _handleDBusReply(invocation, ret) {
|
|
559
|
-
if (ret === undefined) ret = new GLib.Variant('()', []);
|
|
560
|
-
try {
|
|
561
|
-
if (!(ret instanceof GLib.Variant)) {
|
|
562
|
-
const outArgs = invocation.get_method_info().out_args;
|
|
563
|
-
const outSignature = _makeOutSignature(outArgs);
|
|
564
|
-
if (outArgs.length === 1) ret = [ret];
|
|
565
|
-
ret = new GLib.Variant(outSignature, ret);
|
|
566
|
-
}
|
|
567
|
-
invocation.return_value(ret);
|
|
568
|
-
} catch (e) {
|
|
569
|
-
logError(e, `Exception in method call: ${invocation.get_method_name()}`);
|
|
570
|
-
invocation.return_dbus_error(
|
|
571
|
-
'org.gnome.gjs.JSError.ValueError',
|
|
572
|
-
'Service implementation returned an incorrect value type',
|
|
573
|
-
);
|
|
580
|
+
|
|
581
|
+
function _handleDBusReply(invocation, ret) {
|
|
582
|
+
if (ret === undefined) ret = new GLib.Variant('()', []);
|
|
583
|
+
try {
|
|
584
|
+
if (!(ret instanceof GLib.Variant)) {
|
|
585
|
+
const outArgs = invocation.get_method_info().out_args;
|
|
586
|
+
const outSignature = _makeOutSignature(outArgs);
|
|
587
|
+
if (outArgs.length === 1) ret = [ret];
|
|
588
|
+
ret = new GLib.Variant(outSignature, ret);
|
|
589
|
+
}
|
|
590
|
+
invocation.return_value(ret);
|
|
591
|
+
} catch (e) {
|
|
592
|
+
logError(e, `Exception in method call: ${invocation.get_method_name()}`);
|
|
593
|
+
invocation.return_dbus_error(
|
|
594
|
+
'org.gnome.gjs.JSError.ValueError',
|
|
595
|
+
'Service implementation returned an incorrect value type',
|
|
596
|
+
);
|
|
597
|
+
}
|
|
574
598
|
}
|
|
575
|
-
}
|
|
576
599
|
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
600
|
+
function _handleDBusError(invocation, e) {
|
|
601
|
+
if (e instanceof GLib.Error) {
|
|
602
|
+
invocation.return_gerror(e);
|
|
603
|
+
return;
|
|
604
|
+
}
|
|
605
|
+
let name = e && e.name ? e.name : 'Error';
|
|
606
|
+
if (!name.includes('.')) name = `org.gnome.gjs.JSError.${name}`;
|
|
607
|
+
logError(e, `Exception in method call: ${invocation.get_method_name()}`);
|
|
608
|
+
invocation.return_dbus_error(name, e && e.message ? e.message : String(e));
|
|
581
609
|
}
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
610
|
+
|
|
611
|
+
function _handleMethodCall(jsObj, methodName, parameters, invocation) {
|
|
612
|
+
// GJS appends the message's Gio.UnixFDList as a trailing argument (null when
|
|
613
|
+
// the call carries no fds) — mirror it so fd-aware services see the same
|
|
614
|
+
// shape (refs/gjs Gio.js _handleMethodCall). Gio.DBusMessage.get_unix_fd_list
|
|
615
|
+
// is GioUnix (fd-passing) — absent from the Gio typelib on Windows — so guard by
|
|
616
|
+
// method presence and pass null there (no fds to carry). POSIX byte-identical:
|
|
617
|
+
// where the method exists it is taken exactly as before.
|
|
618
|
+
const msg = invocation.get_message();
|
|
619
|
+
const fdList = typeof msg.get_unix_fd_list === 'function' ? msg.get_unix_fd_list() : null;
|
|
620
|
+
const method = jsObj[methodName];
|
|
621
|
+
if (typeof method === 'function') {
|
|
622
|
+
let retval;
|
|
623
|
+
try {
|
|
624
|
+
const args = parameters.deepUnpack();
|
|
625
|
+
args.push(fdList);
|
|
626
|
+
retval = method.apply(jsObj, args);
|
|
627
|
+
} catch (e) {
|
|
628
|
+
_handleDBusError(invocation, e);
|
|
629
|
+
return;
|
|
630
|
+
}
|
|
631
|
+
// A Promise-returning (async) method resolves to the reply.
|
|
632
|
+
if (retval && typeof retval.then === 'function') {
|
|
633
|
+
retval.then(
|
|
634
|
+
(r) => _handleDBusReply(invocation, r),
|
|
635
|
+
(e) => _handleDBusError(invocation, e),
|
|
636
|
+
);
|
|
637
|
+
return;
|
|
638
|
+
}
|
|
639
|
+
_handleDBusReply(invocation, retval);
|
|
640
|
+
return;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
const asyncMethod = jsObj[`${methodName}Async`];
|
|
644
|
+
if (typeof asyncMethod === 'function') {
|
|
645
|
+
let ret;
|
|
646
|
+
try {
|
|
647
|
+
ret = asyncMethod.call(jsObj, parameters.deepUnpack(), invocation, fdList);
|
|
648
|
+
} catch (e) {
|
|
649
|
+
_handleDBusError(invocation, e);
|
|
650
|
+
return;
|
|
651
|
+
}
|
|
652
|
+
if (ret && typeof ret.catch === 'function') ret.catch((e) => _handleDBusError(invocation, e));
|
|
653
|
+
return;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
logError(new Error(), `Missing handler for DBus method ${methodName}`);
|
|
657
|
+
invocation.return_dbus_error(
|
|
658
|
+
'org.freedesktop.DBus.Error.UnknownMethod',
|
|
659
|
+
`Method ${methodName} is not implemented`,
|
|
613
660
|
);
|
|
614
|
-
return;
|
|
615
|
-
}
|
|
616
|
-
_handleDBusReply(invocation, retval);
|
|
617
|
-
return;
|
|
618
661
|
}
|
|
619
662
|
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
_handleDBusError(invocation, e);
|
|
627
|
-
return;
|
|
628
|
-
}
|
|
629
|
-
if (ret && typeof ret.catch === 'function') ret.catch((e) => _handleDBusError(invocation, e));
|
|
630
|
-
return;
|
|
663
|
+
function _handlePropertyGet(jsObj, info, propertyName) {
|
|
664
|
+
const propInfo = info.lookup_property(propertyName);
|
|
665
|
+
const jsval = jsObj[propertyName];
|
|
666
|
+
if (jsval === undefined || jsval === null) return null;
|
|
667
|
+
if (jsval instanceof GLib.Variant) return jsval;
|
|
668
|
+
return new GLib.Variant(propInfo.signature, jsval);
|
|
631
669
|
}
|
|
632
670
|
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
);
|
|
638
|
-
}
|
|
639
|
-
|
|
640
|
-
function _handlePropertyGet(jsObj, info, propertyName) {
|
|
641
|
-
const propInfo = info.lookup_property(propertyName);
|
|
642
|
-
const jsval = jsObj[propertyName];
|
|
643
|
-
if (jsval === undefined || jsval === null) return null;
|
|
644
|
-
if (jsval instanceof GLib.Variant) return jsval;
|
|
645
|
-
return new GLib.Variant(propInfo.signature, jsval);
|
|
646
|
-
}
|
|
647
|
-
|
|
648
|
-
function _handlePropertySet(jsObj, propertyName, newValue) {
|
|
649
|
-
jsObj[propertyName] = newValue.deepUnpack();
|
|
650
|
-
return true;
|
|
651
|
-
}
|
|
652
|
-
|
|
653
|
-
function wrapJSObject(interfaceInfo, jsObj) {
|
|
654
|
-
let info;
|
|
655
|
-
if (isGioInstance(interfaceInfo, 'DBusInterfaceInfo')) info = interfaceInfo;
|
|
656
|
-
else info = _newInterfaceInfo(interfaceInfo);
|
|
657
|
-
// cache_build is required so lookup_property/method work off the info.
|
|
658
|
-
info.cache_build();
|
|
659
|
-
const ifaceName = info.name;
|
|
660
|
-
|
|
661
|
-
// Every connection this object is exported on: { connection, registrationId }.
|
|
662
|
-
const registrations = [];
|
|
663
|
-
|
|
664
|
-
const impl = {
|
|
665
|
-
get_info: () => info,
|
|
666
|
-
get_object_path: () => impl._objectPath,
|
|
667
|
-
get_connection: () => impl._connection,
|
|
668
|
-
_objectPath: null,
|
|
669
|
-
_connection: null,
|
|
670
|
-
|
|
671
|
-
export(connection, path) {
|
|
672
|
-
// register_object_with_closures2 (since 2.84): closures for method call /
|
|
673
|
-
// get property / set property. Passing JS functions marshals each to a
|
|
674
|
-
// real GClosure (the engine's GClosure IN-arg support). Returns the
|
|
675
|
-
// registration id (throws on error).
|
|
676
|
-
const id = connection.register_object_with_closures2(
|
|
677
|
-
path,
|
|
678
|
-
info,
|
|
679
|
-
(_conn, _sender, _path, _iface, method, params, invocation) =>
|
|
680
|
-
_handleMethodCall(jsObj, method, params, invocation),
|
|
681
|
-
(_conn, _sender, _path, _iface, prop) => _handlePropertyGet(jsObj, info, prop),
|
|
682
|
-
(_conn, _sender, _path, _iface, prop, value) => _handlePropertySet(jsObj, prop, value),
|
|
683
|
-
);
|
|
684
|
-
registrations.push({ connection, id });
|
|
685
|
-
impl._connection = connection;
|
|
686
|
-
impl._objectPath = path;
|
|
687
|
-
return id;
|
|
688
|
-
},
|
|
689
|
-
|
|
690
|
-
unexport() {
|
|
691
|
-
for (const { connection, id } of registrations.splice(0)) {
|
|
692
|
-
connection.unregister_object(id);
|
|
693
|
-
}
|
|
694
|
-
impl._connection = null;
|
|
695
|
-
},
|
|
696
|
-
|
|
697
|
-
unexport_from_connection(connection) {
|
|
698
|
-
const handle = unwrap(connection);
|
|
699
|
-
for (let i = registrations.length - 1; i >= 0; i--) {
|
|
700
|
-
if (unwrap(registrations[i].connection) === handle) {
|
|
701
|
-
registrations[i].connection.unregister_object(registrations[i].id);
|
|
702
|
-
registrations.splice(i, 1);
|
|
703
|
-
}
|
|
704
|
-
}
|
|
705
|
-
if (registrations.length === 0) impl._connection = null;
|
|
706
|
-
},
|
|
671
|
+
function _handlePropertySet(jsObj, propertyName, newValue) {
|
|
672
|
+
jsObj[propertyName] = newValue.deepUnpack();
|
|
673
|
+
return true;
|
|
674
|
+
}
|
|
707
675
|
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
const
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
impl._objectPath,
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
676
|
+
function wrapJSObject(interfaceInfo, jsObj) {
|
|
677
|
+
let info;
|
|
678
|
+
if (isGioInstance(interfaceInfo, 'DBusInterfaceInfo')) info = interfaceInfo;
|
|
679
|
+
else info = _newInterfaceInfo(interfaceInfo);
|
|
680
|
+
// cache_build is required so lookup_property/method work off the info.
|
|
681
|
+
info.cache_build();
|
|
682
|
+
const ifaceName = info.name;
|
|
683
|
+
|
|
684
|
+
// Every connection this object is exported on: { connection, registrationId }.
|
|
685
|
+
const registrations = [];
|
|
686
|
+
|
|
687
|
+
const impl = {
|
|
688
|
+
get_info: () => info,
|
|
689
|
+
get_object_path: () => impl._objectPath,
|
|
690
|
+
get_connection: () => impl._connection,
|
|
691
|
+
_objectPath: null,
|
|
692
|
+
_connection: null,
|
|
693
|
+
|
|
694
|
+
export(connection, path) {
|
|
695
|
+
// register_object_with_closures2 (since 2.84): closures for method call /
|
|
696
|
+
// get property / set property. Passing JS functions marshals each to a
|
|
697
|
+
// real GClosure (the engine's GClosure IN-arg support). Returns the
|
|
698
|
+
// registration id (throws on error).
|
|
699
|
+
const id = connection.register_object_with_closures2(
|
|
700
|
+
path,
|
|
701
|
+
info,
|
|
702
|
+
(_conn, _sender, _path, _iface, method, params, invocation) =>
|
|
703
|
+
_handleMethodCall(jsObj, method, params, invocation),
|
|
704
|
+
(_conn, _sender, _path, _iface, prop) => _handlePropertyGet(jsObj, info, prop),
|
|
705
|
+
(_conn, _sender, _path, _iface, prop, value) => _handlePropertySet(jsObj, prop, value),
|
|
706
|
+
);
|
|
707
|
+
registrations.push({ connection, id });
|
|
708
|
+
impl._connection = connection;
|
|
709
|
+
impl._objectPath = path;
|
|
710
|
+
return id;
|
|
711
|
+
},
|
|
712
|
+
|
|
713
|
+
unexport() {
|
|
714
|
+
for (const { connection, id } of registrations.splice(0)) {
|
|
715
|
+
connection.unregister_object(id);
|
|
716
|
+
}
|
|
717
|
+
impl._connection = null;
|
|
718
|
+
},
|
|
719
|
+
|
|
720
|
+
unexport_from_connection(connection) {
|
|
721
|
+
const handle = unwrap(connection);
|
|
722
|
+
for (let i = registrations.length - 1; i >= 0; i--) {
|
|
723
|
+
if (unwrap(registrations[i].connection) === handle) {
|
|
724
|
+
registrations[i].connection.unregister_object(registrations[i].id);
|
|
725
|
+
registrations.splice(i, 1);
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
if (registrations.length === 0) impl._connection = null;
|
|
729
|
+
},
|
|
730
|
+
|
|
731
|
+
emit_signal(name, parameters = null) {
|
|
732
|
+
for (const { connection } of registrations) {
|
|
733
|
+
connection.emit_signal(null, impl._objectPath, ifaceName, name, parameters);
|
|
734
|
+
}
|
|
735
|
+
},
|
|
736
|
+
|
|
737
|
+
emit_property_changed(name, value) {
|
|
738
|
+
const changed = {};
|
|
739
|
+
changed[name] = value;
|
|
740
|
+
const params = new GLib.Variant('(sa{sv}as)', [ifaceName, changed, []]);
|
|
741
|
+
for (const { connection } of registrations) {
|
|
742
|
+
connection.emit_signal(
|
|
743
|
+
null,
|
|
744
|
+
impl._objectPath,
|
|
745
|
+
'org.freedesktop.DBus.Properties',
|
|
746
|
+
'PropertiesChanged',
|
|
747
|
+
params,
|
|
748
|
+
);
|
|
749
|
+
}
|
|
750
|
+
},
|
|
751
|
+
|
|
752
|
+
flush() {
|
|
753
|
+
for (const { connection } of registrations) connection.flush(null);
|
|
754
|
+
},
|
|
755
|
+
};
|
|
756
|
+
// camelCase aliases (GJS's DBusImplementation exposes snake_case; keep both
|
|
757
|
+
// so either spelling works, matching the rest of the node-gi surface).
|
|
758
|
+
impl.getInfo = impl.get_info;
|
|
759
|
+
impl.getObjectPath = impl.get_object_path;
|
|
760
|
+
impl.getConnection = impl.get_connection;
|
|
761
|
+
impl.unexportFromConnection = impl.unexport_from_connection;
|
|
762
|
+
impl.emitSignal = impl.emit_signal;
|
|
763
|
+
impl.emitPropertyChanged = impl.emit_property_changed;
|
|
764
|
+
return impl;
|
|
765
|
+
}
|
|
728
766
|
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
};
|
|
733
|
-
// camelCase aliases (GJS's DBusImplementation exposes snake_case; keep both
|
|
734
|
-
// so either spelling works, matching the rest of the node-gi surface).
|
|
735
|
-
impl.getInfo = impl.get_info;
|
|
736
|
-
impl.getObjectPath = impl.get_object_path;
|
|
737
|
-
impl.getConnection = impl.get_connection;
|
|
738
|
-
impl.unexportFromConnection = impl.unexport_from_connection;
|
|
739
|
-
impl.emitSignal = impl.emit_signal;
|
|
740
|
-
impl.emitPropertyChanged = impl.emit_property_changed;
|
|
741
|
-
return impl;
|
|
742
|
-
}
|
|
743
|
-
|
|
744
|
-
const DBusExportedObject = { wrapJSObject };
|
|
745
|
-
|
|
746
|
-
return { DBus, DBusProxy, DBusExportedObject };
|
|
767
|
+
const DBusExportedObject = { wrapJSObject };
|
|
768
|
+
|
|
769
|
+
return { DBus, DBusProxy, DBusExportedObject };
|
|
747
770
|
}
|
|
748
771
|
|
|
749
772
|
export default createGioDBus;
|