@gjsify/node-gi 0.26.0 → 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 +6 -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 +160 -0
- package/system.d.ts +13 -13
- package/system.js +35 -35
package/gi.js
CHANGED
|
@@ -54,11 +54,14 @@ const RESERVED = new Set(['then', 'toString', 'valueOf', 'constructor', 'inspect
|
|
|
54
54
|
// accessor to the GI method name (snake_case) and to a GObject property name
|
|
55
55
|
// (kebab-case); a name that is already in the target case passes through.
|
|
56
56
|
function camelToSnake(name) {
|
|
57
|
-
|
|
57
|
+
return name.replace(/([A-Z])/g, '_$1').toLowerCase();
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
function toKebab(name) {
|
|
61
|
-
|
|
61
|
+
return name
|
|
62
|
+
.replace(/([A-Z])/g, '-$1')
|
|
63
|
+
.replace(/_/g, '-')
|
|
64
|
+
.toLowerCase();
|
|
62
65
|
}
|
|
63
66
|
|
|
64
67
|
// Per-user-function shim cache so repeated passes of the same callback marshal to
|
|
@@ -76,24 +79,24 @@ const callbackShims = new WeakMap();
|
|
|
76
79
|
// engine as the raw variant handle).
|
|
77
80
|
// Primitives and plain objects pass through both directions untouched.
|
|
78
81
|
function wrapCallbackFn(fn) {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
82
|
+
let shim = callbackShims.get(fn);
|
|
83
|
+
if (shim === undefined) {
|
|
84
|
+
shim = (...args) => unwrapArg(fn(...args.map(wrapReturn)));
|
|
85
|
+
callbackShims.set(fn, shim);
|
|
86
|
+
}
|
|
87
|
+
return shim;
|
|
85
88
|
}
|
|
86
89
|
|
|
87
90
|
function unwrapArg(value) {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
91
|
+
if (value !== null && typeof value === 'object' && value[HANDLE] !== undefined) {
|
|
92
|
+
return value[HANDLE];
|
|
93
|
+
}
|
|
94
|
+
if (typeof value === 'function') return wrapCallbackFn(value);
|
|
95
|
+
return value;
|
|
93
96
|
}
|
|
94
97
|
|
|
95
98
|
function unwrapArgs(args) {
|
|
96
|
-
|
|
99
|
+
return args.map(unwrapArg);
|
|
97
100
|
}
|
|
98
101
|
|
|
99
102
|
// Normalize a construct-property dict: each KEY to the GObject canonical (kebab)
|
|
@@ -110,9 +113,9 @@ function unwrapArgs(args) {
|
|
|
110
113
|
// `new Ns.Class({...})`, and constructType for a registerClass'd subclass plus its
|
|
111
114
|
// `super({...})` chain-up — so all three accept camelCase identically to GJS.
|
|
112
115
|
function unwrapProps(props) {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
+
const out = {};
|
|
117
|
+
for (const key of Object.keys(props)) out[toKebab(key)] = unwrapArg(props[key]);
|
|
118
|
+
return out;
|
|
116
119
|
}
|
|
117
120
|
|
|
118
121
|
// Symbol carrying an enum object's GError-domain descriptor ({ name, quark }) so
|
|
@@ -138,48 +141,48 @@ const ERROR_DOMAIN = Symbol('nodeGiErrorDomain');
|
|
|
138
141
|
|
|
139
142
|
// Resolve a `matches` domain argument to a { name?, quark? } descriptor.
|
|
140
143
|
function errorDomainOf(domain) {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
144
|
+
if (domain === null || domain === undefined) return null;
|
|
145
|
+
if (typeof domain === 'object' && domain[ERROR_DOMAIN] !== undefined) return domain[ERROR_DOMAIN];
|
|
146
|
+
if (typeof domain === 'string') return { name: domain };
|
|
147
|
+
if (typeof domain === 'number') return { quark: domain };
|
|
148
|
+
return null;
|
|
146
149
|
}
|
|
147
150
|
|
|
148
151
|
class GLibError extends Error {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
152
|
+
// GJS-shaped public constructor: `new GLib.Error(domain, code, message)` where
|
|
153
|
+
// `domain` may be an error-enum object, a quark name string, or a numeric quark.
|
|
154
|
+
constructor(domain, code, message) {
|
|
155
|
+
super(typeof message === 'string' ? message : '');
|
|
156
|
+
this.name = 'GLib.Error';
|
|
157
|
+
const d = errorDomainOf(domain);
|
|
158
|
+
this.domain = d !== null && d.name !== undefined ? d.name : typeof domain === 'string' ? domain : undefined;
|
|
159
|
+
this.domainQuark =
|
|
160
|
+
d !== null && d.quark !== undefined ? d.quark : typeof domain === 'number' ? domain : undefined;
|
|
161
|
+
this.code = code;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// g_error_matches: true when the error's domain + code both match. Accepts the
|
|
165
|
+
// domain as an error-enum object / name string / numeric quark (errorDomainOf).
|
|
166
|
+
matches(domain, code) {
|
|
167
|
+
const d = errorDomainOf(domain);
|
|
168
|
+
if (d === null) return false;
|
|
169
|
+
const domainMatch =
|
|
170
|
+
(d.name !== undefined && d.name === this.domain) || (d.quark !== undefined && d.quark === this.domainQuark);
|
|
171
|
+
return domainMatch && code === this.code;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
toString() {
|
|
175
|
+
return `GLib.Error ${this.domain}: ${this.message}`;
|
|
176
|
+
}
|
|
174
177
|
}
|
|
175
178
|
|
|
176
179
|
// The engine calls this on a failed GI invoke with the GError's authoritative
|
|
177
180
|
// fields (quark name + numeric quark + code + message) to build the thrown error.
|
|
178
181
|
function buildGError(domainName, domainQuark, code, message) {
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
182
|
+
const error = new GLibError(domainName, code, message);
|
|
183
|
+
error.domain = domainName;
|
|
184
|
+
error.domainQuark = domainQuark;
|
|
185
|
+
return error;
|
|
183
186
|
}
|
|
184
187
|
native.setErrorBuilder(buildGError);
|
|
185
188
|
|
|
@@ -201,25 +204,25 @@ const promisifiedMethods = new Map();
|
|
|
201
204
|
// handles (e.g. GMainLoop) become method-routing proxies; everything else
|
|
202
205
|
// (primitives, strings, null) passes through.
|
|
203
206
|
function wrapReturn(value) {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
207
|
+
if (native.isGObjectHandle(value)) return wrapInstance(value);
|
|
208
|
+
// A GLib.Variant is ALSO a boxed handle (by tag), so it must be checked first
|
|
209
|
+
// to give it the Variant ergonomics rather than a plain method-routing proxy.
|
|
210
|
+
if (native.isVariantHandle(value)) return wrapVariant(value);
|
|
211
|
+
// A GParamSpec is a distinct GObject fundamental (tagged separately from boxed),
|
|
212
|
+
// surfaced with the GObject.ParamSpec ergonomics (name/value_type/get_name()/…).
|
|
213
|
+
if (native.isParamSpecHandle(value)) return wrapParamSpec(value);
|
|
214
|
+
// A non-GObject GObject-fundamental (e.g. a GskRenderNode from Gtk.Snapshot.to_node):
|
|
215
|
+
// an opaque, round-trippable pass-through handle (see wrapFundamental).
|
|
216
|
+
if (native.isFundamentalHandle(value)) return wrapFundamental(value);
|
|
217
|
+
if (native.isBoxedHandle(value)) return wrapBoxed(value);
|
|
218
|
+
// A multi-value OUT tuple (return value + OUT params) — or a container OUT — is a
|
|
219
|
+
// plain JS Array whose ELEMENTS may themselves be GObject/boxed/variant handles
|
|
220
|
+
// (e.g. GLib.Regex.match → [matched, GLib.MatchInfo]; an array-of-objects OUT).
|
|
221
|
+
// Recurse so a handle sitting inside the tuple is wrapped into a usable proxy,
|
|
222
|
+
// not left as a raw External. A Node Buffer (byte array) is NOT Array.isArray, so
|
|
223
|
+
// it passes through untouched; primitives/strings map to themselves.
|
|
224
|
+
if (Array.isArray(value)) return value.map(wrapReturn);
|
|
225
|
+
return value;
|
|
223
226
|
}
|
|
224
227
|
|
|
225
228
|
// Wrap a user's signal callback so each native signal argument is passed through
|
|
@@ -231,7 +234,7 @@ function wrapReturn(value) {
|
|
|
231
234
|
// arg, so wrapReturn turns it into the cached, toggle-ref-canonical instance. For
|
|
232
235
|
// `notify::x` the args are (object, pspec).
|
|
233
236
|
function wrapSignalCallback(cb) {
|
|
234
|
-
|
|
237
|
+
return (...args) => cb(...args.map(wrapReturn));
|
|
235
238
|
}
|
|
236
239
|
|
|
237
240
|
// Resolve a Gtk.Template `<signal handler="…">` handler NAME to a dispatcher that
|
|
@@ -252,17 +255,17 @@ function wrapSignalCallback(cb) {
|
|
|
252
255
|
// handler name with no matching user-prototype method throws a clear error when
|
|
253
256
|
// the signal fires.
|
|
254
257
|
function resolveTemplateCallback(handle, handlerName) {
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
258
|
+
return (...args) => {
|
|
259
|
+
const proxy = wrapInstance(handle);
|
|
260
|
+
const userProto = proxy[USER_PROTO];
|
|
261
|
+
const desc = userProto !== undefined ? findProtoDescriptor(userProto, handlerName) : undefined;
|
|
262
|
+
if (desc === undefined || typeof desc.value !== 'function') {
|
|
263
|
+
throw new Error(
|
|
264
|
+
`Gtk.Template: signal handler '${handlerName}' is not defined on ${proxy.constructor?.name ?? 'the instance'}`,
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
return desc.value.apply(proxy, args.map(wrapReturn));
|
|
268
|
+
};
|
|
266
269
|
}
|
|
267
270
|
native.setTemplateCallbackResolver(resolveTemplateCallback);
|
|
268
271
|
|
|
@@ -277,17 +280,17 @@ native.setTemplateCallbackResolver(resolveTemplateCallback);
|
|
|
277
280
|
// one wrapper GtkBuilder-fetched children later resolve to). GJS-faithful
|
|
278
281
|
// (gjs_object_constructor's native-construction branch runs the JS constructor).
|
|
279
282
|
function runCtorForCObject(handle, gtypeName) {
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
283
|
+
const cls = classesByGType.get(gtypeName);
|
|
284
|
+
if (cls === undefined) return; // not a node-gi-registered class (shouldn't happen)
|
|
285
|
+
const prev = adoptHandle;
|
|
286
|
+
adoptHandle = handle;
|
|
287
|
+
try {
|
|
288
|
+
// No props: GtkBuilder already applied the construct properties to the C object;
|
|
289
|
+
// the ctor's `params` arg is conventionally optional for a template child.
|
|
290
|
+
Reflect.construct(cls, []);
|
|
291
|
+
} finally {
|
|
292
|
+
adoptHandle = prev;
|
|
293
|
+
}
|
|
291
294
|
}
|
|
292
295
|
native.setConstructCallback(runCtorForCObject);
|
|
293
296
|
|
|
@@ -301,7 +304,7 @@ native.setConstructCallback(runCtorForCObject);
|
|
|
301
304
|
// documented follow-up; today they are pass-through intermediates (build → hand to
|
|
302
305
|
// a consumer → drop), which is what the GSK screenshot/render path needs.
|
|
303
306
|
function wrapFundamental(handle) {
|
|
304
|
-
|
|
307
|
+
return { [HANDLE]: handle, [Symbol.toStringTag]: 'GIFundamental' };
|
|
305
308
|
}
|
|
306
309
|
|
|
307
310
|
// Wrap a boxed/struct/union handle so its methods are callable GJS-style
|
|
@@ -314,54 +317,56 @@ function wrapFundamental(handle) {
|
|
|
314
317
|
// setBoxedField), 0 = neither (fall back to a method dispatcher, so an unknown
|
|
315
318
|
// name still throws a clear "no method" at call time — the pre-fields behaviour).
|
|
316
319
|
function wrapBoxed(handle) {
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
320
|
+
const target = { [HANDLE]: handle };
|
|
321
|
+
const methodDispatch =
|
|
322
|
+
(prop) =>
|
|
323
|
+
(...args) =>
|
|
324
|
+
wrapReturn(native.callBoxedMethod(handle, camelToSnake(prop), unwrapArgs(args)));
|
|
325
|
+
return new Proxy(target, {
|
|
326
|
+
get(t, prop) {
|
|
327
|
+
if (prop === HANDLE) return handle;
|
|
328
|
+
if (typeof prop !== 'string' || RESERVED.has(prop)) return t[prop];
|
|
329
|
+
// A field set through the set trap below is stored as an expando on the
|
|
330
|
+
// target (gjs allows writing a boxed field that GI declares unwritable to a
|
|
331
|
+
// JS expando); surface it back before consulting introspection.
|
|
332
|
+
if (Object.hasOwn(t, prop)) return t[prop];
|
|
333
|
+
// GBytes convenience: `.toArray()` → the raw bytes as a Uint8Array (Node
|
|
334
|
+
// Buffer). GBytes has no introspected `to_array`; gjs adds it to
|
|
335
|
+
// GLib.Bytes.prototype (imports._byteArrayNative.fromGBytes), so mirror it
|
|
336
|
+
// via the introspected get_data(). Gated on the boxed type so it never
|
|
337
|
+
// shadows a same-named method on some other struct.
|
|
338
|
+
if (prop === 'toArray' && native.boxedTypeName(handle) === 'GBytes') {
|
|
339
|
+
return () => wrapReturn(native.callBoxedMethod(handle, 'get_data', []));
|
|
340
|
+
}
|
|
341
|
+
const snake = camelToSnake(prop);
|
|
342
|
+
// boxedMemberKind: 0 = not a member (type info resolved) → `undefined`, so
|
|
343
|
+
// `typeof boxed.noSuchName === 'undefined'` matches gjs (a fabricated
|
|
344
|
+
// dispatcher would read `'function'` and break JS duck-typing such as
|
|
345
|
+
// `if (typeof value.toArray === 'function')`); 1 = method; 2 = field; 3 =
|
|
346
|
+
// undecidable (unregistered struct, no static info) → keep the dispatcher
|
|
347
|
+
// fallback so a genuine method still resolves + throws a clear error at call.
|
|
348
|
+
const kind = native.boxedMemberKind(handle, snake);
|
|
349
|
+
if (kind === 2) return wrapReturn(native.getBoxedField(handle, snake));
|
|
350
|
+
if (kind === 0) return undefined;
|
|
351
|
+
return methodDispatch(prop);
|
|
352
|
+
},
|
|
353
|
+
set(t, prop, value) {
|
|
354
|
+
if (prop === HANDLE || typeof prop !== 'string' || RESERVED.has(prop)) {
|
|
355
|
+
return Reflect.set(t, prop, value);
|
|
356
|
+
}
|
|
357
|
+
const snake = camelToSnake(prop);
|
|
358
|
+
if (native.boxedMemberKind(handle, snake) === 2) {
|
|
359
|
+
native.setBoxedField(handle, snake, unwrapArg(value));
|
|
360
|
+
return true;
|
|
361
|
+
}
|
|
362
|
+
return Reflect.set(t, prop, value);
|
|
363
|
+
},
|
|
364
|
+
has(t, prop) {
|
|
365
|
+
if (prop === HANDLE || prop in t) return true;
|
|
366
|
+
if (typeof prop !== 'string') return false;
|
|
367
|
+
return native.boxedMemberKind(handle, camelToSnake(prop)) !== 0;
|
|
368
|
+
},
|
|
369
|
+
});
|
|
365
370
|
}
|
|
366
371
|
|
|
367
372
|
// Wrap a GParamSpec handle (phase 2.7a) with the GJS-shaped GObject.ParamSpec
|
|
@@ -374,36 +379,36 @@ function wrapBoxed(handle) {
|
|
|
374
379
|
// value_type/owner_type are native GType handles (like gjs's GType objects); the
|
|
375
380
|
// GTypes' `.name` reads their type name via the introspected surface.
|
|
376
381
|
function wrapParamSpec(handle) {
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
382
|
+
const prop = (which) => native.paramSpecProp(handle, which);
|
|
383
|
+
const pspec = {
|
|
384
|
+
[HANDLE]: handle,
|
|
385
|
+
get name() {
|
|
386
|
+
return prop('name');
|
|
387
|
+
},
|
|
388
|
+
get nick() {
|
|
389
|
+
return prop('nick');
|
|
390
|
+
},
|
|
391
|
+
get blurb() {
|
|
392
|
+
return prop('blurb');
|
|
393
|
+
},
|
|
394
|
+
get flags() {
|
|
395
|
+
return prop('flags');
|
|
396
|
+
},
|
|
397
|
+
get value_type() {
|
|
398
|
+
return prop('valueType');
|
|
399
|
+
},
|
|
400
|
+
get owner_type() {
|
|
401
|
+
return prop('ownerType');
|
|
402
|
+
},
|
|
403
|
+
get default_value() {
|
|
404
|
+
return wrapReturn(prop('defaultValue'));
|
|
405
|
+
},
|
|
406
|
+
get_name: () => prop('name'),
|
|
407
|
+
get_nick: () => prop('nick'),
|
|
408
|
+
get_blurb: () => prop('blurb'),
|
|
409
|
+
get_default_value: () => wrapReturn(prop('defaultValue')),
|
|
410
|
+
};
|
|
411
|
+
return pspec;
|
|
407
412
|
}
|
|
408
413
|
|
|
409
414
|
// ---- GLib.Variant ergonomics (the GJS GLib.Variant override, L1) ----
|
|
@@ -424,42 +429,41 @@ function wrapParamSpec(handle) {
|
|
|
424
429
|
// unpacker left in place (an `a{sv}` value under deepUnpack, every child under
|
|
425
430
|
// unpack(), …) becomes a GLib.Variant wrapper; arrays/plain dicts are walked.
|
|
426
431
|
function wrapVariantResult(value) {
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
432
|
+
if (value === null || typeof value !== 'object') return value;
|
|
433
|
+
if (native.isVariantHandle(value)) return wrapVariant(value);
|
|
434
|
+
if (value instanceof Uint8Array) return value; // `ay` bytes
|
|
435
|
+
if (Array.isArray(value)) return value.map(wrapVariantResult);
|
|
436
|
+
const out = {};
|
|
437
|
+
for (const key of Object.keys(value)) out[key] = wrapVariantResult(value[key]);
|
|
438
|
+
return out;
|
|
434
439
|
}
|
|
435
440
|
|
|
436
441
|
// Wrap a boxed GLib.Variant handle with the GJS-shaped Variant surface. Carries
|
|
437
442
|
// [HANDLE] so it round-trips back into the engine as a GVariant IN argument
|
|
438
443
|
// (action.activate(variant), new_stateful state, change_state value, …).
|
|
439
444
|
function wrapVariant(handle) {
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
}
|
|
462
|
-
});
|
|
445
|
+
const target = { [HANDLE]: handle };
|
|
446
|
+
const api = {
|
|
447
|
+
unpack: () => wrapVariantResult(native.variantUnpack(handle, false, false)),
|
|
448
|
+
deepUnpack: () => wrapVariantResult(native.variantUnpack(handle, true, false)),
|
|
449
|
+
deep_unpack: () => wrapVariantResult(native.variantUnpack(handle, true, false)),
|
|
450
|
+
recursiveUnpack: () => native.variantUnpack(handle, true, true),
|
|
451
|
+
get_type_string: () => native.variantGetTypeString(handle),
|
|
452
|
+
toString: () => `[object variant of type "${native.variantGetTypeString(handle)}"]`,
|
|
453
|
+
};
|
|
454
|
+
return new Proxy(target, {
|
|
455
|
+
get(t, prop) {
|
|
456
|
+
if (prop === HANDLE) return handle;
|
|
457
|
+
if (typeof prop !== 'string') return t[prop];
|
|
458
|
+
if (Object.hasOwn(api, prop)) return api[prop];
|
|
459
|
+
if (RESERVED.has(prop)) return t[prop];
|
|
460
|
+
// Any other GVariant method (n_children, get_child_value, print, …).
|
|
461
|
+
return (...args) => wrapReturn(native.callBoxedMethod(handle, camelToSnake(prop), unwrapArgs(args)));
|
|
462
|
+
},
|
|
463
|
+
has(t, prop) {
|
|
464
|
+
return prop === HANDLE || (typeof prop === 'string' && Object.hasOwn(api, prop)) || prop in t;
|
|
465
|
+
},
|
|
466
|
+
});
|
|
463
467
|
}
|
|
464
468
|
|
|
465
469
|
// Deep-unwrap a pack value so any nested GLib.Variant wrapper (carried at a `v`
|
|
@@ -468,52 +472,51 @@ function wrapVariant(handle) {
|
|
|
468
472
|
// byte arrays, plain arrays and dict objects are walked; HANDLE-carrying
|
|
469
473
|
// wrappers collapse to their handle.
|
|
470
474
|
function unwrapVariantValue(value) {
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
475
|
+
if (value === null || typeof value !== 'object') return value;
|
|
476
|
+
if (value[HANDLE] !== undefined) return value[HANDLE];
|
|
477
|
+
if (value instanceof Uint8Array) return value; // `ay` bytes
|
|
478
|
+
if (Array.isArray(value)) return value.map(unwrapVariantValue);
|
|
479
|
+
const out = {};
|
|
480
|
+
for (const key of Object.keys(value)) out[key] = unwrapVariantValue(value[key]);
|
|
481
|
+
return out;
|
|
478
482
|
}
|
|
479
483
|
|
|
480
484
|
function packVariant(signature, value) {
|
|
481
|
-
|
|
485
|
+
return wrapVariant(native.variantNew(signature, unwrapVariantValue(value)));
|
|
482
486
|
}
|
|
483
487
|
|
|
484
488
|
// `GLib.Variant` as a class-like object: `new GLib.Variant(sig, value)` and the
|
|
485
489
|
// deprecated `GLib.Variant.new(sig, value)` both pack via the native engine.
|
|
486
490
|
function makeVariantClass() {
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
}
|
|
516
|
-
});
|
|
491
|
+
const ctor = function Variant(signature, value) {
|
|
492
|
+
return packVariant(signature, value);
|
|
493
|
+
};
|
|
494
|
+
Object.defineProperty(ctor, 'name', { value: 'Variant', configurable: true });
|
|
495
|
+
ctor.$gtypeName = 'GLib.Variant';
|
|
496
|
+
ctor.new = (signature, value) => packVariant(signature, value);
|
|
497
|
+
// `value instanceof GLib.Variant` — a wrapped Variant is a Proxy over a bare
|
|
498
|
+
// `{[HANDLE]}` target (no class prototype), so the default instanceof always
|
|
499
|
+
// returned false. Recognise any wrapper whose [HANDLE] is a native GVariant
|
|
500
|
+
// boxed handle. Real GAction/GSettings/GLib.log_structured code branches on it.
|
|
501
|
+
Object.defineProperty(ctor, Symbol.hasInstance, {
|
|
502
|
+
value(instance) {
|
|
503
|
+
if (instance === null || typeof instance !== 'object') return false;
|
|
504
|
+
const handle = instance[HANDLE];
|
|
505
|
+
return handle !== undefined && native.isVariantHandle(handle);
|
|
506
|
+
},
|
|
507
|
+
configurable: true,
|
|
508
|
+
});
|
|
509
|
+
return new Proxy(ctor, {
|
|
510
|
+
get(t, prop) {
|
|
511
|
+
if (typeof prop !== 'string' || prop in t || RESERVED.has(prop)) return t[prop];
|
|
512
|
+
// Other static constructors (e.g. new_from_bytes) route through the engine.
|
|
513
|
+
const giName = camelToSnake(prop);
|
|
514
|
+
return (...args) => wrapReturn(native.callStaticMethod('GLib', 'Variant', giName, unwrapArgs(args)));
|
|
515
|
+
},
|
|
516
|
+
construct(_t, args) {
|
|
517
|
+
return packVariant(args[0], args[1]);
|
|
518
|
+
},
|
|
519
|
+
});
|
|
517
520
|
}
|
|
518
521
|
|
|
519
522
|
const variantClass = makeVariantClass();
|
|
@@ -534,55 +537,55 @@ const variantClass = makeVariantClass();
|
|
|
534
537
|
// convenience. Non-primitive types (enum/flags/boxed/object/param/variant) are
|
|
535
538
|
// matched by g_type_is_a below, exactly like gjs's default switch branch.
|
|
536
539
|
const GVALUE_PRIMITIVE_SETTERS = {
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
540
|
+
gboolean: 'set_boolean',
|
|
541
|
+
gchar: 'set_schar',
|
|
542
|
+
guchar: 'set_uchar',
|
|
543
|
+
gint: 'set_int',
|
|
544
|
+
guint: 'set_uint',
|
|
545
|
+
glong: 'set_long',
|
|
546
|
+
gulong: 'set_ulong',
|
|
547
|
+
gint64: 'set_int64',
|
|
548
|
+
guint64: 'set_uint64',
|
|
549
|
+
gfloat: 'set_float',
|
|
550
|
+
gdouble: 'set_double',
|
|
551
|
+
gchararray: 'set_string',
|
|
552
|
+
GType: 'set_gtype',
|
|
553
|
+
GVariant: 'set_variant',
|
|
554
|
+
GParam: 'set_param',
|
|
552
555
|
};
|
|
553
556
|
|
|
554
557
|
// Pick the GValue setter for a (resolved) GType handle, mirroring gjs's constructor
|
|
555
558
|
// switch: an exact fundamental → its setter, else g_type_is_a → flags/enum/boxed/…
|
|
556
559
|
// (introspected GObject.type_name / type_is_a — both verified on node-gi).
|
|
557
560
|
function gvalueSetterFor(gt) {
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
561
|
+
const G = requireGi('GObject', '2.0');
|
|
562
|
+
const prim = GVALUE_PRIMITIVE_SETTERS[G.type_name(gt)];
|
|
563
|
+
if (prim !== undefined) return prim;
|
|
564
|
+
if (G.type_is_a(gt, G.TYPE_FLAGS)) return 'set_flags';
|
|
565
|
+
if (G.type_is_a(gt, G.TYPE_ENUM)) return 'set_enum';
|
|
566
|
+
if (G.type_is_a(gt, G.TYPE_VARIANT)) return 'set_variant';
|
|
567
|
+
if (G.type_is_a(gt, G.TYPE_PARAM)) return 'set_param';
|
|
568
|
+
if (G.type_is_a(gt, G.TYPE_BOXED)) return 'set_boxed';
|
|
569
|
+
if (G.type_is_a(gt, G.TYPE_OBJECT)) return 'set_object';
|
|
570
|
+
throw new TypeError(`Invalid type argument '${G.type_name(gt)}' to the GObject.Value constructor`);
|
|
568
571
|
}
|
|
569
572
|
|
|
570
573
|
// Build the empty/convenience GValue: a zeroed GValue boxed handle wrapped with the
|
|
571
574
|
// GValue struct methods; the 2-arg form inits it to `gtype` (a class ctor's $gtype
|
|
572
575
|
// or a GType handle) and sets `value` via the matching setter.
|
|
573
576
|
function buildValue(args) {
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
577
|
+
const v = wrapBoxed(native.newGValue());
|
|
578
|
+
if (args.length >= 2) {
|
|
579
|
+
const gt = resolveGTypeArg(args[0]);
|
|
580
|
+
// Resolve the setter BEFORE init so an unsupported type throws a clean JS
|
|
581
|
+
// TypeError rather than driving g_value_init with a value-table-less type (a
|
|
582
|
+
// GLib-CRITICAL that would abort under G_DEBUG=fatal-criticals) — same net
|
|
583
|
+
// result as gjs's constructor, minus the noisy failed init.
|
|
584
|
+
const setter = gvalueSetterFor(gt);
|
|
585
|
+
v.init(gt);
|
|
586
|
+
v[setter](args[1]);
|
|
587
|
+
}
|
|
588
|
+
return v;
|
|
586
589
|
}
|
|
587
590
|
|
|
588
591
|
// `GObject.Value` as a class-like object: `new GObject.Value()` /
|
|
@@ -590,34 +593,29 @@ function buildValue(args) {
|
|
|
590
593
|
// (type_compatible / type_transformable + camelCase) route through the engine, and
|
|
591
594
|
// `value instanceof GObject.Value` recognises any wrapper over a GValue boxed handle.
|
|
592
595
|
function makeValueClass() {
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
},
|
|
617
|
-
construct(_t, args) {
|
|
618
|
-
return buildValue(args);
|
|
619
|
-
},
|
|
620
|
-
});
|
|
596
|
+
const ctor = function Value(...args) {
|
|
597
|
+
return buildValue(args);
|
|
598
|
+
};
|
|
599
|
+
Object.defineProperty(ctor, 'name', { value: 'Value', configurable: true });
|
|
600
|
+
ctor.$gtypeName = 'GObject.Value';
|
|
601
|
+
Object.defineProperty(ctor, Symbol.hasInstance, {
|
|
602
|
+
value(instance) {
|
|
603
|
+
if (instance === null || typeof instance !== 'object') return false;
|
|
604
|
+
const handle = instance[HANDLE];
|
|
605
|
+
return handle !== undefined && native.isBoxedHandle(handle) && native.boxedTypeName(handle) === 'GValue';
|
|
606
|
+
},
|
|
607
|
+
configurable: true,
|
|
608
|
+
});
|
|
609
|
+
return new Proxy(ctor, {
|
|
610
|
+
get(t, prop) {
|
|
611
|
+
if (typeof prop !== 'string' || prop in t || RESERVED.has(prop)) return t[prop];
|
|
612
|
+
const giName = camelToSnake(prop);
|
|
613
|
+
return (...args) => wrapReturn(native.callStaticMethod('GObject', 'Value', giName, unwrapArgs(args)));
|
|
614
|
+
},
|
|
615
|
+
construct(_t, args) {
|
|
616
|
+
return buildValue(args);
|
|
617
|
+
},
|
|
618
|
+
});
|
|
621
619
|
}
|
|
622
620
|
|
|
623
621
|
const valueClass = makeValueClass();
|
|
@@ -636,12 +634,12 @@ const valueClass = makeValueClass();
|
|
|
636
634
|
// of the field bytes (or null for empty fields) — byte-for-byte the shape gjs's
|
|
637
635
|
// `{...stringFields.recursiveUnpack()}` produces (verified vs gjs 1.88).
|
|
638
636
|
const GLIB_OVERLAY_NAMES = new Set([
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
637
|
+
'log_structured',
|
|
638
|
+
'idle_add_once',
|
|
639
|
+
'timeout_add_once',
|
|
640
|
+
'timeout_add_seconds_once',
|
|
641
|
+
'log_set_writer_func',
|
|
642
|
+
'log_set_writer_default',
|
|
645
643
|
]);
|
|
646
644
|
|
|
647
645
|
// GLib.log_structured(domain, level, fields): pack `fields` into an `a{sv}` and hand
|
|
@@ -649,86 +647,86 @@ const GLIB_OVERLAY_NAMES = new Set([
|
|
|
649
647
|
// log_structured. Each field value is `ay` (Uint8Array), `s` (string) or a Variant
|
|
650
648
|
// passed through; anything else is a TypeError, matching gjs.
|
|
651
649
|
function makeLogStructured(baseNs) {
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
650
|
+
return (logDomain, logLevel, fields) => {
|
|
651
|
+
const variantFields = {};
|
|
652
|
+
for (const key of Object.keys(fields)) {
|
|
653
|
+
const field = fields[key];
|
|
654
|
+
if (field instanceof Uint8Array) variantFields[key] = variantClass('ay', field);
|
|
655
|
+
else if (typeof field === 'string') variantFields[key] = variantClass('s', field);
|
|
656
|
+
else if (field instanceof variantClass) variantFields[key] = field;
|
|
657
|
+
else {
|
|
658
|
+
throw new TypeError(
|
|
659
|
+
`Unsupported value ${field}, log_structured supports GLib.Variant, Uint8Array, and string values.`,
|
|
660
|
+
);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
baseNs.log_variant(logDomain, logLevel, variantClass('a{sv}', variantFields));
|
|
664
|
+
};
|
|
667
665
|
}
|
|
668
666
|
|
|
669
667
|
function decorateGLibNamespace(baseNs) {
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
668
|
+
// Per-namespace cache of the overlay functions that need the introspected baseNs
|
|
669
|
+
// (log_structured → log_variant; the *_once helpers → idle_add/timeout_add).
|
|
670
|
+
const cache = new Map();
|
|
671
|
+
const overlay = (prop) => {
|
|
672
|
+
if (cache.has(prop)) return cache.get(prop);
|
|
673
|
+
let value;
|
|
674
|
+
if (prop === 'log_structured') value = makeLogStructured(baseNs);
|
|
675
|
+
// The one-shot idle/timeout conveniences (gjs GLib.js): auto-remove the source
|
|
676
|
+
// after the callback runs, so the callback needs no `return SOURCE_REMOVE`.
|
|
677
|
+
else if (prop === 'idle_add_once') {
|
|
678
|
+
value = (priority, callback) =>
|
|
679
|
+
baseNs.idle_add(priority, () => {
|
|
680
|
+
callback();
|
|
681
|
+
return baseNs.SOURCE_REMOVE;
|
|
682
|
+
});
|
|
683
|
+
} else if (prop === 'timeout_add_once') {
|
|
684
|
+
value = (priority, interval, callback) =>
|
|
685
|
+
baseNs.timeout_add(priority, interval, () => {
|
|
686
|
+
callback();
|
|
687
|
+
return baseNs.SOURCE_REMOVE;
|
|
688
|
+
});
|
|
689
|
+
} else if (prop === 'timeout_add_seconds_once') {
|
|
690
|
+
value = (priority, interval, callback) =>
|
|
691
|
+
baseNs.timeout_add_seconds(priority, interval, () => {
|
|
692
|
+
callback();
|
|
693
|
+
return baseNs.SOURCE_REMOVE;
|
|
694
|
+
});
|
|
695
|
+
} else if (prop === 'log_set_writer_func') {
|
|
696
|
+
// The gjs GLib.js shape: a non-function clears the JS writer (logs fall
|
|
697
|
+
// back to the default writer); a function becomes the structured-log
|
|
698
|
+
// writer. NOTE (GLib contract, identical under gjs): the underlying
|
|
699
|
+
// g_log_set_writer_func may only ever be installed ONCE per process — a
|
|
700
|
+
// second install aborts inside GLib itself.
|
|
701
|
+
value = (writerFunc) => {
|
|
702
|
+
if (typeof writerFunc !== 'function') native.logSetWriterFunc(null);
|
|
703
|
+
else native.logSetWriterFunc(writerFunc);
|
|
704
|
+
};
|
|
705
|
+
} else if (prop === 'log_set_writer_default') {
|
|
706
|
+
value = () => native.logSetWriterDefault();
|
|
707
|
+
}
|
|
708
|
+
cache.set(prop, value);
|
|
709
|
+
return value;
|
|
710
|
+
};
|
|
711
|
+
return new Proxy(baseNs, {
|
|
712
|
+
get(t, prop) {
|
|
713
|
+
if (prop === 'Variant') return variantClass;
|
|
714
|
+
// GLib.Error is the L1 GError subclass (the engine throws instances of it,
|
|
715
|
+
// and `new GLib.Error(domain, code, message)` constructs one), shadowing the
|
|
716
|
+
// introspected boxed type so `instanceof GLib.Error` + `.matches()` work.
|
|
717
|
+
if (prop === 'Error') return GLibError;
|
|
718
|
+
if (typeof prop === 'string' && GLIB_OVERLAY_NAMES.has(prop)) return overlay(prop);
|
|
719
|
+
return t[prop];
|
|
720
|
+
},
|
|
721
|
+
has(t, prop) {
|
|
722
|
+
return (
|
|
723
|
+
prop === 'Variant' ||
|
|
724
|
+
prop === 'Error' ||
|
|
725
|
+
(typeof prop === 'string' && GLIB_OVERLAY_NAMES.has(prop)) ||
|
|
726
|
+
prop in t
|
|
727
|
+
);
|
|
728
|
+
},
|
|
729
|
+
});
|
|
732
730
|
}
|
|
733
731
|
|
|
734
732
|
// Overlay the GJS Gio runtime statics on the introspected Gio namespace —
|
|
@@ -737,45 +735,45 @@ function decorateGLibNamespace(baseNs) {
|
|
|
737
735
|
// is built by createGioDBus (overrides/gio-dbus.js) over the introspected Gio +
|
|
738
736
|
// the ergonomic GLib. Every other member keeps resolving from introspection.
|
|
739
737
|
function decorateGioNamespace(baseNs) {
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
738
|
+
// Lazily construct the DBus surface: it needs the ergonomic GLib (`new
|
|
739
|
+
// GLib.Variant`), and building it only on first `Gio.DBus*` access keeps a plain
|
|
740
|
+
// `import Gio` (no DBus) from pulling GLib in.
|
|
741
|
+
let dbus;
|
|
742
|
+
const getDBus = () => {
|
|
743
|
+
if (dbus === undefined) {
|
|
744
|
+
dbus = createGioDBus({ Gio: baseNs, GLib: requireGi('GLib', '2.0'), unwrap: unwrapArg, native });
|
|
745
|
+
}
|
|
746
|
+
return dbus;
|
|
747
|
+
};
|
|
748
|
+
return new Proxy(baseNs, {
|
|
749
|
+
get(t, prop) {
|
|
750
|
+
if (prop === '_promisify') return promisify;
|
|
751
|
+
if (prop === 'DBus') return getDBus().DBus;
|
|
752
|
+
if (prop === 'DBusProxy') return getDBus().DBusProxy;
|
|
753
|
+
if (prop === 'DBusExportedObject') return getDBus().DBusExportedObject;
|
|
754
|
+
return t[prop];
|
|
755
|
+
},
|
|
756
|
+
has(t, prop) {
|
|
757
|
+
return (
|
|
758
|
+
prop === '_promisify' ||
|
|
759
|
+
prop === 'DBus' ||
|
|
760
|
+
prop === 'DBusProxy' ||
|
|
761
|
+
prop === 'DBusExportedObject' ||
|
|
762
|
+
prop in t
|
|
763
|
+
);
|
|
764
|
+
},
|
|
765
|
+
});
|
|
768
766
|
}
|
|
769
767
|
|
|
770
768
|
// Walk a JS prototype chain (excluding Object.prototype) for an own property
|
|
771
769
|
// descriptor of `prop` — used to surface a registerClass subclass's own methods
|
|
772
770
|
// /getters/setters on the GObject wrapper.
|
|
773
771
|
function findProtoDescriptor(proto, prop) {
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
772
|
+
for (let p = proto; p !== null && p !== Object.prototype; p = Object.getPrototypeOf(p)) {
|
|
773
|
+
const d = Object.getOwnPropertyDescriptor(p, prop);
|
|
774
|
+
if (d !== undefined) return d;
|
|
775
|
+
}
|
|
776
|
+
return undefined;
|
|
779
777
|
}
|
|
780
778
|
|
|
781
779
|
// Whether `proto` is the SAME as, or a more-derived descendant of, `maybeAncestor`
|
|
@@ -785,10 +783,10 @@ function findProtoDescriptor(proto, prop) {
|
|
|
785
783
|
// prototype before the leaf ctor wraps it with the leaf's, so the leaf (descendant)
|
|
786
784
|
// proto must be allowed to supersede the ancestor's — but never the reverse.
|
|
787
785
|
function isProtoSameOrDescendantOf(proto, maybeAncestor) {
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
786
|
+
for (let p = proto; p !== null; p = Object.getPrototypeOf(p)) {
|
|
787
|
+
if (p === maybeAncestor) return true;
|
|
788
|
+
}
|
|
789
|
+
return false;
|
|
792
790
|
}
|
|
793
791
|
|
|
794
792
|
// L1 proxy-identity cache (the user-visible half of the toggle-ref bridge). The
|
|
@@ -818,34 +816,34 @@ const instanceCache = new WeakMap();
|
|
|
818
816
|
const signalHandlerIds = new WeakMap(); // handle → Map<jsFunc, Set<handlerId>>
|
|
819
817
|
|
|
820
818
|
function recordSignalHandler(handle, fn, id) {
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
819
|
+
let byFn = signalHandlerIds.get(handle);
|
|
820
|
+
if (byFn === undefined) {
|
|
821
|
+
byFn = new Map();
|
|
822
|
+
signalHandlerIds.set(handle, byFn);
|
|
823
|
+
}
|
|
824
|
+
let ids = byFn.get(fn);
|
|
825
|
+
if (ids === undefined) {
|
|
826
|
+
ids = new Set();
|
|
827
|
+
byFn.set(fn, ids);
|
|
828
|
+
}
|
|
829
|
+
ids.add(id);
|
|
832
830
|
}
|
|
833
831
|
|
|
834
832
|
// Drop a handler id from the per-instance registry (on disconnect), pruning an
|
|
835
833
|
// emptied function entry so a later by-func lookup reports zero matches.
|
|
836
834
|
function forgetSignalHandlerId(handle, id) {
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
835
|
+
const byFn = signalHandlerIds.get(handle);
|
|
836
|
+
if (byFn === undefined) return;
|
|
837
|
+
for (const [fn, ids] of byFn) {
|
|
838
|
+
if (ids.delete(id) && ids.size === 0) byFn.delete(fn);
|
|
839
|
+
}
|
|
842
840
|
}
|
|
843
841
|
|
|
844
842
|
// The connected handler ids for a given JS function on an instance (a fresh array;
|
|
845
843
|
// empty when the function was never connected on this instance).
|
|
846
844
|
function handlerIdsForFunc(handle, fn) {
|
|
847
|
-
|
|
848
|
-
|
|
845
|
+
const ids = signalHandlerIds.get(handle)?.get(fn);
|
|
846
|
+
return ids === undefined ? [] : [...ids];
|
|
849
847
|
}
|
|
850
848
|
|
|
851
849
|
// ---- G3 mutate-in-place registry ----
|
|
@@ -889,16 +887,16 @@ let adoptHandle;
|
|
|
889
887
|
// resolves each. Assigned through the proxy (set trap → stored as an instance
|
|
890
888
|
// expando), exactly as the old Subclass did.
|
|
891
889
|
function assignTemplateChildren(instance, handle, reg) {
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
890
|
+
if (reg.children !== undefined) {
|
|
891
|
+
for (const childName of reg.children) {
|
|
892
|
+
instance[childName.replace(/-/g, '_')] = wrapReturn(native.getTemplateChild(handle, childName));
|
|
893
|
+
}
|
|
895
894
|
}
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
895
|
+
if (reg.internalChildren !== undefined) {
|
|
896
|
+
for (const childName of reg.internalChildren) {
|
|
897
|
+
instance[`_${childName.replace(/-/g, '_')}`] = wrapReturn(native.getTemplateChild(handle, childName));
|
|
898
|
+
}
|
|
900
899
|
}
|
|
901
|
-
}
|
|
902
900
|
}
|
|
903
901
|
|
|
904
902
|
// Push construct-time GObject-property values through the class's own JS SETTERS.
|
|
@@ -919,14 +917,14 @@ function assignTemplateChildren(instance, handle, reg) {
|
|
|
919
917
|
// touched, so a plain GObject property (no JS accessor) keeps the store as its single
|
|
920
918
|
// backing store — unchanged.
|
|
921
919
|
function flushConstructProperties(instance, handle, reg) {
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
920
|
+
if (reg.constructPropertyNames === undefined) return;
|
|
921
|
+
const up = instance[USER_PROTO];
|
|
922
|
+
if (up === undefined) return;
|
|
923
|
+
for (const name of reg.constructPropertyNames) {
|
|
924
|
+
const desc = findProtoDescriptor(up, name);
|
|
925
|
+
if (desc === undefined || typeof desc.set !== 'function') continue;
|
|
926
|
+
desc.set.call(instance, wrapReturn(native.getProperty(handle, name)));
|
|
927
|
+
}
|
|
930
928
|
}
|
|
931
929
|
|
|
932
930
|
// ---- Gio.Application.runAsync (L1, GJS-shaped) ----
|
|
@@ -966,15 +964,15 @@ function flushConstructProperties(instance, handle, reg) {
|
|
|
966
964
|
// process loop) — for concurrent runtime-loop + GLib work there, drive GLib with
|
|
967
965
|
// startMainContextPump instead of a blocking run.
|
|
968
966
|
function applicationRunAsync(handle) {
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
967
|
+
return new Promise((resolve, reject) => {
|
|
968
|
+
setImmediate(() => {
|
|
969
|
+
try {
|
|
970
|
+
resolve(wrapReturn(native.callMethod(handle, 'run', unwrapArgs([[]]))));
|
|
971
|
+
} catch (error) {
|
|
972
|
+
reject(error);
|
|
973
|
+
}
|
|
974
|
+
});
|
|
976
975
|
});
|
|
977
|
-
});
|
|
978
976
|
}
|
|
979
977
|
|
|
980
978
|
// ---- portable GLib main-context pump (Bun/Deno) ----
|
|
@@ -997,10 +995,10 @@ let pumpHoldsRuntime = false;
|
|
|
997
995
|
|
|
998
996
|
/** Dispatch every currently-ready GLib source, then return. */
|
|
999
997
|
function drainReadySources() {
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
998
|
+
let guard = 0;
|
|
999
|
+
while (native.iterateMainContext(false) && guard++ < 100000) {
|
|
1000
|
+
/* drain ready sources */
|
|
1001
|
+
}
|
|
1004
1002
|
}
|
|
1005
1003
|
|
|
1006
1004
|
// Zero-N-API view over the JS-armed-work counter — `pendingView[0] > 0` is
|
|
@@ -1036,10 +1034,10 @@ let pendingView = null;
|
|
|
1036
1034
|
* sources, C-armed ones included.
|
|
1037
1035
|
*/
|
|
1038
1036
|
function pumpBeat() {
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1037
|
+
if (pumpRefCount > pumpAutoRefs || pendingView[0] > 0) {
|
|
1038
|
+
drainReadySources();
|
|
1039
|
+
}
|
|
1040
|
+
syncPumpKeepAlive();
|
|
1043
1041
|
}
|
|
1044
1042
|
|
|
1045
1043
|
/**
|
|
@@ -1059,12 +1057,12 @@ function pumpBeat() {
|
|
|
1059
1057
|
* and it is now the SAME on node, bun and deno rather than node-only.
|
|
1060
1058
|
*/
|
|
1061
1059
|
function syncPumpKeepAlive() {
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1060
|
+
if (pumpTimer === null) return;
|
|
1061
|
+
const hold = pendingView[0] > 0; // zero-N-API — see pendingView above
|
|
1062
|
+
if (hold === pumpHoldsRuntime) return;
|
|
1063
|
+
if (hold) pumpTimer.ref?.();
|
|
1064
|
+
else pumpTimer.unref?.();
|
|
1065
|
+
pumpHoldsRuntime = hold;
|
|
1068
1066
|
}
|
|
1069
1067
|
|
|
1070
1068
|
/**
|
|
@@ -1079,41 +1077,41 @@ function syncPumpKeepAlive() {
|
|
|
1079
1077
|
* @returns {() => void}
|
|
1080
1078
|
*/
|
|
1081
1079
|
export function startMainContextPump() {
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1080
|
+
// Node: the libuv↔GLib bridge already co-pumps. GJS: the host loop IS
|
|
1081
|
+
// GLib's default main context — a JS-timer pump would only iterate the
|
|
1082
|
+
// context recursively from within its own dispatch.
|
|
1083
|
+
if (native.isNodeRuntime || native.RUNTIME === 'gjs') return () => {};
|
|
1084
|
+
pumpRefCount++;
|
|
1085
|
+
if (pumpTimer === null) {
|
|
1086
|
+
if (pendingView === null) pendingView = native.makePumpPendingCount();
|
|
1087
|
+
// ~4 ms cadence: low latency without busy-spinning. Each beat drains every
|
|
1088
|
+
// currently-ready source (iterateMainContext(false) returns false when none
|
|
1089
|
+
// remain, bounding the inner loop) — gated on the program's own GLib work
|
|
1090
|
+
// for the auto-armed pump, see pumpBeat — then re-evaluates the hold.
|
|
1091
|
+
pumpTimer = setInterval(pumpBeat, 4);
|
|
1092
|
+
// Start UNREF'd: the pump alone must never keep a finished program alive.
|
|
1093
|
+
// syncPumpKeepAlive() refs it for exactly as long as GLib has work.
|
|
1094
|
+
pumpTimer.unref?.();
|
|
1095
|
+
pumpHoldsRuntime = false;
|
|
1096
|
+
syncPumpKeepAlive();
|
|
1097
|
+
}
|
|
1098
|
+
let disposed = false;
|
|
1099
|
+
return () => {
|
|
1100
|
+
if (disposed) return;
|
|
1101
|
+
disposed = true;
|
|
1102
|
+
stopMainContextPump();
|
|
1103
|
+
};
|
|
1106
1104
|
}
|
|
1107
1105
|
|
|
1108
1106
|
/** Decrement the main-context pump reference count; clears the timer at zero. */
|
|
1109
1107
|
export function stopMainContextPump() {
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1108
|
+
if (native.isNodeRuntime || native.RUNTIME === 'gjs' || pumpRefCount === 0) return;
|
|
1109
|
+
pumpRefCount--;
|
|
1110
|
+
if (pumpRefCount === 0 && pumpTimer !== null) {
|
|
1111
|
+
clearInterval(pumpTimer);
|
|
1112
|
+
pumpTimer = null;
|
|
1113
|
+
pumpHoldsRuntime = false;
|
|
1114
|
+
}
|
|
1117
1115
|
}
|
|
1118
1116
|
|
|
1119
1117
|
// bind_property_full / BindingGroup.bind_full: the transform functions are driven
|
|
@@ -1127,12 +1125,12 @@ export function stopMainContextPump() {
|
|
|
1127
1125
|
// the incoming binding/source into chainable L1 values and unwraps the returned
|
|
1128
1126
|
// target value back to a native handle (object/variant-valued properties).
|
|
1129
1127
|
function wrapBindingTransform(fn) {
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1128
|
+
if (typeof fn !== 'function') return null;
|
|
1129
|
+
return (binding, value) => {
|
|
1130
|
+
const result = fn(wrapReturn(binding), wrapReturn(value));
|
|
1131
|
+
if (!Array.isArray(result)) return result; // native reports + treats as FALSE
|
|
1132
|
+
return [result[0], unwrap(result[1])];
|
|
1133
|
+
};
|
|
1136
1134
|
}
|
|
1137
1135
|
|
|
1138
1136
|
// gjs's GObject.Object.prototype signal-convenience methods + bind_property_full
|
|
@@ -1142,59 +1140,59 @@ function wrapBindingTransform(fn) {
|
|
|
1142
1140
|
// single-handler g_signal_handler_* functions (verified to marshal a node-gi
|
|
1143
1141
|
// GObject IN-arg); stop_emission_by_name through g_signal_stop_emission_by_name.
|
|
1144
1142
|
function objectPrototypeShim(prop) {
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1143
|
+
switch (prop) {
|
|
1144
|
+
case 'block_signal_handler':
|
|
1145
|
+
case 'blockSignalHandler':
|
|
1146
|
+
return (handle, args) =>
|
|
1147
|
+
wrapReturn(native.callFunction('GObject', 'signal_handler_block', [handle, args[0]]));
|
|
1148
|
+
case 'unblock_signal_handler':
|
|
1149
|
+
case 'unblockSignalHandler':
|
|
1150
|
+
return (handle, args) =>
|
|
1151
|
+
wrapReturn(native.callFunction('GObject', 'signal_handler_unblock', [handle, args[0]]));
|
|
1152
|
+
case 'stop_emission_by_name':
|
|
1153
|
+
case 'stopEmissionByName':
|
|
1154
|
+
return (handle, args) =>
|
|
1155
|
+
wrapReturn(native.callFunction('GObject', 'signal_stop_emission_by_name', [handle, args[0]]));
|
|
1156
|
+
case 'bind_property_full':
|
|
1157
|
+
case 'bindPropertyFull':
|
|
1158
|
+
return (handle, args) =>
|
|
1159
|
+
wrapReturn(
|
|
1160
|
+
native.bindPropertyFull(
|
|
1161
|
+
handle,
|
|
1162
|
+
args[0],
|
|
1163
|
+
unwrap(args[1]),
|
|
1164
|
+
args[2],
|
|
1165
|
+
args[3],
|
|
1166
|
+
wrapBindingTransform(args[4]),
|
|
1167
|
+
wrapBindingTransform(args[5]),
|
|
1168
|
+
),
|
|
1169
|
+
);
|
|
1170
|
+
case 'bind_full':
|
|
1171
|
+
case 'bindFull':
|
|
1172
|
+
// GObject.BindingGroup.bind_full (gjs: GjsPrivate.g_binding_group_bind_full).
|
|
1173
|
+
// Gated per instance: on any OTHER class a genuine `bind_full` method keeps
|
|
1174
|
+
// resolving through the normal GI route. NOTE: without this shim the
|
|
1175
|
+
// introspected name would resolve to bind_with_closures (its GIR shadow),
|
|
1176
|
+
// whose write-back GValue contract a marshaled GClosure cannot satisfy.
|
|
1177
|
+
return (handle, args) => {
|
|
1178
|
+
if (!native.isInstanceOf(handle, 'GObject', 'BindingGroup')) {
|
|
1179
|
+
return wrapReturn(native.callMethod(handle, 'bind_full', unwrapArgs(args)));
|
|
1180
|
+
}
|
|
1181
|
+
return wrapReturn(
|
|
1182
|
+
native.bindingGroupBindFull(
|
|
1183
|
+
handle,
|
|
1184
|
+
args[0],
|
|
1185
|
+
unwrap(args[1]),
|
|
1186
|
+
args[2],
|
|
1187
|
+
args[3],
|
|
1188
|
+
wrapBindingTransform(args[4]),
|
|
1189
|
+
wrapBindingTransform(args[5]),
|
|
1190
|
+
),
|
|
1191
|
+
);
|
|
1192
|
+
};
|
|
1193
|
+
default:
|
|
1194
|
+
return undefined;
|
|
1195
|
+
}
|
|
1198
1196
|
}
|
|
1199
1197
|
|
|
1200
1198
|
// Per-GType method feature-detection cache backing the wrapper's GJS-parity
|
|
@@ -1205,18 +1203,18 @@ function objectPrototypeShim(prop) {
|
|
|
1205
1203
|
const methodPresenceCache = new Map(); // typeName -> Map<methodName, boolean>
|
|
1206
1204
|
|
|
1207
1205
|
function instanceHasMethod(handle, name) {
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1206
|
+
const typeName = native.getTypeName(handle);
|
|
1207
|
+
let perType = methodPresenceCache.get(typeName);
|
|
1208
|
+
if (perType === undefined) {
|
|
1209
|
+
perType = new Map();
|
|
1210
|
+
methodPresenceCache.set(typeName, perType);
|
|
1211
|
+
}
|
|
1212
|
+
let present = perType.get(name);
|
|
1213
|
+
if (present === undefined) {
|
|
1214
|
+
present = native.hasMethod(handle, name);
|
|
1215
|
+
perType.set(name, present);
|
|
1216
|
+
}
|
|
1217
|
+
return present;
|
|
1220
1218
|
}
|
|
1221
1219
|
|
|
1222
1220
|
// Wrap a live GObject handle as a GJS-shaped instance. When `userProto` is given
|
|
@@ -1225,171 +1223,170 @@ function instanceHasMethod(handle, name) {
|
|
|
1225
1223
|
// wrapper as `this` — then falls back to GObject property get/set and GI method
|
|
1226
1224
|
// routing. `.connect()/.emit()/.disconnect()` work in both modes.
|
|
1227
1225
|
function wrapInstance(handle, userProto) {
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
}
|
|
1245
|
-
return cached;
|
|
1246
|
-
}
|
|
1247
|
-
const target = { [HANDLE]: handle };
|
|
1248
|
-
if (userProto !== undefined) target[USER_PROTO] = userProto;
|
|
1249
|
-
const proxy = new Proxy(target, {
|
|
1250
|
-
get(t, prop) {
|
|
1251
|
-
if (prop === HANDLE) return handle;
|
|
1252
|
-
if (typeof prop !== 'string' || RESERVED.has(prop)) return t[prop];
|
|
1253
|
-
switch (prop) {
|
|
1254
|
-
case '$typeName':
|
|
1255
|
-
// The instance's concrete RUNTIME GType name —
|
|
1256
|
-
// g_type_name(G_OBJECT_TYPE(obj)) via native.getTypeName. node-gi hands
|
|
1257
|
-
// back a GENERIC wrapper for a returned handle (it does not downcast to
|
|
1258
|
-
// the runtime GType), so `constructor.$gtype` is the STATIC declared
|
|
1259
|
-
// type; this getter reads the TRUE runtime type. It is the portable seam
|
|
1260
|
-
// @gjsify/devtools' widget-tree DumpTree uses (GJS instead reads the
|
|
1261
|
-
// concrete type off its already-downcast `constructor.$gtype.name`).
|
|
1262
|
-
// Distinct from the CLASS-level `$gtypeName`, which is the DECLARED
|
|
1263
|
-
// namespaced string (e.g. 'Gtk.Widget'); this is the raw runtime GType
|
|
1264
|
-
// name (e.g. 'GtkWidget', 'AdwBin', 'FireworksWindow').
|
|
1265
|
-
return native.getTypeName(handle);
|
|
1266
|
-
case 'connect':
|
|
1267
|
-
// Record the (user fn → id) mapping so signal_handlers_*_by_func can
|
|
1268
|
-
// resolve the private-closure handler back to its ids (see the registry).
|
|
1269
|
-
return (signal, cb) => {
|
|
1270
|
-
const id = native.connectSignal(handle, signal, wrapSignalCallback(cb), false);
|
|
1271
|
-
recordSignalHandler(handle, cb, id);
|
|
1272
|
-
return id;
|
|
1273
|
-
};
|
|
1274
|
-
case 'connect_after':
|
|
1275
|
-
return (signal, cb) => {
|
|
1276
|
-
const id = native.connectSignal(handle, signal, wrapSignalCallback(cb), true);
|
|
1277
|
-
recordSignalHandler(handle, cb, id);
|
|
1278
|
-
return id;
|
|
1279
|
-
};
|
|
1280
|
-
case 'emit':
|
|
1281
|
-
return (signal, ...args) =>
|
|
1282
|
-
wrapReturn(native.emitSignal(handle, signal, unwrapArgs(args)));
|
|
1283
|
-
case 'disconnect':
|
|
1284
|
-
return (id) => {
|
|
1285
|
-
forgetSignalHandlerId(handle, id);
|
|
1286
|
-
return native.disconnectSignal(handle, id);
|
|
1287
|
-
};
|
|
1288
|
-
default:
|
|
1289
|
-
break;
|
|
1290
|
-
}
|
|
1291
|
-
const up = t[USER_PROTO];
|
|
1292
|
-
if (up !== undefined) {
|
|
1293
|
-
const desc = findProtoDescriptor(up, prop);
|
|
1294
|
-
if (desc !== undefined) {
|
|
1295
|
-
if (typeof desc.value === 'function') return (...args) => desc.value.apply(proxy, args);
|
|
1296
|
-
if (typeof desc.get === 'function') return desc.get.call(proxy);
|
|
1297
|
-
return desc.value;
|
|
1226
|
+
const cached = instanceCache.get(handle);
|
|
1227
|
+
if (cached !== undefined) {
|
|
1228
|
+
// UPGRADE a cached wrapper's user prototype when this wrap supplies a MORE
|
|
1229
|
+
// DERIVED one: an undefined cache (a subclass instance first seen generically —
|
|
1230
|
+
// returned from store.get_item / a signal sender / resurrection), OR a cached
|
|
1231
|
+
// ANCESTOR prototype (a multi-level chain where a vfunc fired during constructType
|
|
1232
|
+
// wrapped the instance with the OVERRIDING ancestor's prototype, before the leaf
|
|
1233
|
+
// ctor's wrapInstance(handle, leaf.prototype) runs — the leaf proto must win so
|
|
1234
|
+
// the leaf's OWN methods resolve). Never DOWNGRADE: a more-derived cached proto is
|
|
1235
|
+
// kept. The userProto lives on the target (read via the proxy here), so the
|
|
1236
|
+
// upgrade is in place — identity (===) and any expando fields are preserved.
|
|
1237
|
+
if (userProto !== undefined && userProto !== cached[USER_PROTO]) {
|
|
1238
|
+
const current = cached[USER_PROTO];
|
|
1239
|
+
if (current === undefined || isProtoSameOrDescendantOf(userProto, current)) {
|
|
1240
|
+
cached[USER_PROTO] = userProto;
|
|
1241
|
+
}
|
|
1298
1242
|
}
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1243
|
+
return cached;
|
|
1244
|
+
}
|
|
1245
|
+
const target = { [HANDLE]: handle };
|
|
1246
|
+
if (userProto !== undefined) target[USER_PROTO] = userProto;
|
|
1247
|
+
const proxy = new Proxy(target, {
|
|
1248
|
+
get(t, prop) {
|
|
1249
|
+
if (prop === HANDLE) return handle;
|
|
1250
|
+
if (typeof prop !== 'string' || RESERVED.has(prop)) return t[prop];
|
|
1251
|
+
switch (prop) {
|
|
1252
|
+
case '$typeName':
|
|
1253
|
+
// The instance's concrete RUNTIME GType name —
|
|
1254
|
+
// g_type_name(G_OBJECT_TYPE(obj)) via native.getTypeName. node-gi hands
|
|
1255
|
+
// back a GENERIC wrapper for a returned handle (it does not downcast to
|
|
1256
|
+
// the runtime GType), so `constructor.$gtype` is the STATIC declared
|
|
1257
|
+
// type; this getter reads the TRUE runtime type. It is the portable seam
|
|
1258
|
+
// @gjsify/devtools' widget-tree DumpTree uses (GJS instead reads the
|
|
1259
|
+
// concrete type off its already-downcast `constructor.$gtype.name`).
|
|
1260
|
+
// Distinct from the CLASS-level `$gtypeName`, which is the DECLARED
|
|
1261
|
+
// namespaced string (e.g. 'Gtk.Widget'); this is the raw runtime GType
|
|
1262
|
+
// name (e.g. 'GtkWidget', 'AdwBin', 'FireworksWindow').
|
|
1263
|
+
return native.getTypeName(handle);
|
|
1264
|
+
case 'connect':
|
|
1265
|
+
// Record the (user fn → id) mapping so signal_handlers_*_by_func can
|
|
1266
|
+
// resolve the private-closure handler back to its ids (see the registry).
|
|
1267
|
+
return (signal, cb) => {
|
|
1268
|
+
const id = native.connectSignal(handle, signal, wrapSignalCallback(cb), false);
|
|
1269
|
+
recordSignalHandler(handle, cb, id);
|
|
1270
|
+
return id;
|
|
1271
|
+
};
|
|
1272
|
+
case 'connect_after':
|
|
1273
|
+
return (signal, cb) => {
|
|
1274
|
+
const id = native.connectSignal(handle, signal, wrapSignalCallback(cb), true);
|
|
1275
|
+
recordSignalHandler(handle, cb, id);
|
|
1276
|
+
return id;
|
|
1277
|
+
};
|
|
1278
|
+
case 'emit':
|
|
1279
|
+
return (signal, ...args) => wrapReturn(native.emitSignal(handle, signal, unwrapArgs(args)));
|
|
1280
|
+
case 'disconnect':
|
|
1281
|
+
return (id) => {
|
|
1282
|
+
forgetSignalHandlerId(handle, id);
|
|
1283
|
+
return native.disconnectSignal(handle, id);
|
|
1284
|
+
};
|
|
1285
|
+
default:
|
|
1286
|
+
break;
|
|
1287
|
+
}
|
|
1288
|
+
const up = t[USER_PROTO];
|
|
1289
|
+
if (up !== undefined) {
|
|
1290
|
+
const desc = findProtoDescriptor(up, prop);
|
|
1291
|
+
if (desc !== undefined) {
|
|
1292
|
+
if (typeof desc.value === 'function') return (...args) => desc.value.apply(proxy, args);
|
|
1293
|
+
if (typeof desc.get === 'function') return desc.get.call(proxy);
|
|
1294
|
+
return desc.value;
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
// Gio.Application.runAsync — the GJS override (refs/gjs Gio.js + GLib.js):
|
|
1298
|
+
// a Promise-returning run() that does NOT block the Node microtask/event
|
|
1299
|
+
// loop (see applicationRunAsync). Gated to GApplication instances
|
|
1300
|
+
// (g_type_is_a, so Gtk/Adw.Application and registerClass subclasses qualify);
|
|
1301
|
+
// placed AFTER the userProto lookup so a subclass may still override it, and
|
|
1302
|
+
// before the GI-method fallback since there is no introspected `run_async`.
|
|
1303
|
+
if (prop === 'runAsync' && native.isInstanceOf(handle, 'Gio', 'Application')) {
|
|
1304
|
+
return () => applicationRunAsync(handle);
|
|
1305
|
+
}
|
|
1306
|
+
// gjs's GObject.Object.prototype signal conveniences (block_signal_handler /
|
|
1307
|
+
// unblock_signal_handler / stop_emission_by_name) + bind_property_full and
|
|
1308
|
+
// BindingGroup.bind_full (refs/gjs GObject.js). These are NOT introspected instance
|
|
1309
|
+
// methods — they are gjs shims over the namespace-level g_signal_* functions —
|
|
1310
|
+
// so route them explicitly before property / GI-method resolution.
|
|
1311
|
+
const shim = objectPrototypeShim(prop);
|
|
1312
|
+
if (shim !== undefined) return (...args) => shim(handle, args);
|
|
1313
|
+
const propName = toKebab(prop);
|
|
1314
|
+
if (native.hasProperty(handle, propName)) {
|
|
1315
|
+
return wrapReturn(native.getProperty(handle, propName));
|
|
1316
|
+
}
|
|
1317
|
+
// Surface a plain JS field previously written on THIS wrapper. With the
|
|
1318
|
+
// toggle-ref bridge the wrapper is now CANONICAL (one proxy per GObject,
|
|
1319
|
+
// cached by the canonical native handle), so a plain field IS shared across
|
|
1320
|
+
// the vfunc<->instance boundary and survives a round-trip + GC while C owns
|
|
1321
|
+
// the object — a vfunc's `this` resolves to the same cached proxy as
|
|
1322
|
+
// construct, and `store.get_item(x)` returns the same proxy a setter wrote
|
|
1323
|
+
// to. Own-property only (set via the `set` trap), so an introspected GI
|
|
1324
|
+
// method of the same name is never shadowed unless the user explicitly
|
|
1325
|
+
// assigned an expando. (GObject PROPERTIES remain the right choice for state
|
|
1326
|
+
// that must also be visible to C / other language bindings.)
|
|
1327
|
+
if (Object.prototype.hasOwnProperty.call(t, prop)) return t[prop];
|
|
1328
|
+
// LAST resort before treating an unknown name as a GI method: an INHERITED
|
|
1329
|
+
// member (Object.prototype.hasOwnProperty / isPrototypeOf / … — RESERVED
|
|
1330
|
+
// already covers toString/valueOf/etc.) must resolve to the real function,
|
|
1331
|
+
// not a GI callMethod thunk that would throw on `inst.hasOwnProperty('x')`.
|
|
1332
|
+
if (prop in t) return t[prop];
|
|
1333
|
+
// A Gio._promisify'd async method (registerClass subclasses pick it up via
|
|
1334
|
+
// their userProto above; introspected instances have no prototype chain, so
|
|
1335
|
+
// they resolve it here from the registry, per-class). Bound to this instance.
|
|
1336
|
+
const promisified = resolvePromisified(handle, camelToSnake(prop));
|
|
1337
|
+
if (promisified !== undefined) return (...args) => promisified.apply(proxy, args);
|
|
1338
|
+
// GJS parity: an UNKNOWN member is `undefined`, never a throw-on-call
|
|
1339
|
+
// thunk — real consumers feature-detect optional native methods
|
|
1340
|
+
// (`typeof gl.clearBufferfv === 'function'` gates `@gjsify/webgl`'s
|
|
1341
|
+
// clearBuffer emulation; Excalibur's RenderTarget.blitToScreen was the
|
|
1342
|
+
// exposing call on node-gi: the old unconditional thunk made the
|
|
1343
|
+
// detection lie, then threw mid-frame). Presence is resolved via the
|
|
1344
|
+
// SAME native walk callMethod uses (literal-first, snake alias second)
|
|
1345
|
+
// and memoized per concrete GType — method GETs happen on every call.
|
|
1346
|
+
// The LITERAL accessor name is passed through — the engine resolves it
|
|
1347
|
+
// verbatim first (GJS fidelity: GIR names are exposed as-is, incl. Vala
|
|
1348
|
+
// camelCase like Gwebgl's `getString`) and only falls back to the
|
|
1349
|
+
// snake_case alias. Converting here would destroy a literal camelCase
|
|
1350
|
+
// GI name.
|
|
1351
|
+
if (instanceHasMethod(handle, prop)) {
|
|
1352
|
+
return (...args) => wrapReturn(native.callMethod(handle, prop, unwrapArgs(args)));
|
|
1353
|
+
}
|
|
1354
|
+
return undefined;
|
|
1355
|
+
},
|
|
1356
|
+
set(t, prop, value) {
|
|
1357
|
+
if (typeof prop === 'string') {
|
|
1358
|
+
const up = t[USER_PROTO];
|
|
1359
|
+
if (up !== undefined) {
|
|
1360
|
+
const desc = findProtoDescriptor(up, prop);
|
|
1361
|
+
if (desc !== undefined && typeof desc.set === 'function') {
|
|
1362
|
+
desc.set.call(proxy, value);
|
|
1363
|
+
return true;
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
const propName = toKebab(prop);
|
|
1367
|
+
if (native.hasProperty(handle, propName)) {
|
|
1368
|
+
native.setProperty(handle, propName, unwrapArg(value));
|
|
1369
|
+
return true;
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
t[prop] = value;
|
|
1366
1373
|
return true;
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
// GI methods are resolved dynamically and are intentionally not reported.)
|
|
1384
|
-
const up = t[USER_PROTO];
|
|
1385
|
-
if (up !== undefined && findProtoDescriptor(up, prop) !== undefined) return true;
|
|
1386
|
-
if (native.hasProperty(handle, toKebab(prop))) return true;
|
|
1387
|
-
}
|
|
1388
|
-
return false;
|
|
1389
|
-
},
|
|
1390
|
-
});
|
|
1391
|
-
instanceCache.set(handle, proxy);
|
|
1392
|
-
return proxy;
|
|
1374
|
+
},
|
|
1375
|
+
has(t, prop) {
|
|
1376
|
+
if (prop === HANDLE || prop in t) return true;
|
|
1377
|
+
if (typeof prop === 'string') {
|
|
1378
|
+
// A subclass's own prototype member (method/getter) and any GObject
|
|
1379
|
+
// property of the instance both count as `in` the wrapper. (Introspected
|
|
1380
|
+
// GI methods are resolved dynamically and are intentionally not reported.)
|
|
1381
|
+
const up = t[USER_PROTO];
|
|
1382
|
+
if (up !== undefined && findProtoDescriptor(up, prop) !== undefined) return true;
|
|
1383
|
+
if (native.hasProperty(handle, toKebab(prop))) return true;
|
|
1384
|
+
}
|
|
1385
|
+
return false;
|
|
1386
|
+
},
|
|
1387
|
+
});
|
|
1388
|
+
instanceCache.set(handle, proxy);
|
|
1389
|
+
return proxy;
|
|
1393
1390
|
}
|
|
1394
1391
|
|
|
1395
1392
|
// Shared object installed at the BASE of every introspected class's prototype
|
|
@@ -1403,19 +1400,19 @@ function wrapInstance(handle, userProto) {
|
|
|
1403
1400
|
// Non-`vfunc_` lookups fall through to Object.prototype (so the base prototype
|
|
1404
1401
|
// keeps its normal object behaviour).
|
|
1405
1402
|
const vfuncChainProto = new Proxy(Object.create(Object.prototype), {
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1403
|
+
get(target, prop, receiver) {
|
|
1404
|
+
if (typeof prop === 'string' && prop.startsWith('vfunc_')) {
|
|
1405
|
+
const vfuncName = prop.slice('vfunc_'.length);
|
|
1406
|
+
return function chainUpToParentVfunc(...args) {
|
|
1407
|
+
const handle = this !== null && this !== undefined ? this[HANDLE] : undefined;
|
|
1408
|
+
if (handle === undefined) {
|
|
1409
|
+
throw new TypeError(`super.${prop}(): chain-up requires a node-gi instance as \`this\``);
|
|
1410
|
+
}
|
|
1411
|
+
return wrapReturn(native.callParentVfunc(handle, vfuncName, unwrapArgs(args)));
|
|
1412
|
+
};
|
|
1413
1413
|
}
|
|
1414
|
-
return
|
|
1415
|
-
|
|
1416
|
-
}
|
|
1417
|
-
return Reflect.get(target, prop, receiver);
|
|
1418
|
-
},
|
|
1414
|
+
return Reflect.get(target, prop, receiver);
|
|
1415
|
+
},
|
|
1419
1416
|
});
|
|
1420
1417
|
|
|
1421
1418
|
// Surface a lazy `$gtype` getter on an introspected class/struct ctor: the real
|
|
@@ -1426,144 +1423,144 @@ const vfuncChainProto = new Proxy(Object.create(Object.prototype), {
|
|
|
1426
1423
|
// is also cached. The makeClass/makeStruct Proxy forwards `'$gtype' in t` → the
|
|
1427
1424
|
// target getter (it returns `t[prop]` when `prop in t`).
|
|
1428
1425
|
function defineLazyGType(ctor, namespace, typeName) {
|
|
1429
|
-
|
|
1430
|
-
configurable: true,
|
|
1431
|
-
enumerable: false,
|
|
1432
|
-
get() {
|
|
1433
|
-
const gt = native.getGType(namespace, typeName);
|
|
1434
|
-
Object.defineProperty(ctor, '$gtype', {
|
|
1435
|
-
value: gt,
|
|
1426
|
+
Object.defineProperty(ctor, '$gtype', {
|
|
1436
1427
|
configurable: true,
|
|
1437
1428
|
enumerable: false,
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1429
|
+
get() {
|
|
1430
|
+
const gt = native.getGType(namespace, typeName);
|
|
1431
|
+
Object.defineProperty(ctor, '$gtype', {
|
|
1432
|
+
value: gt,
|
|
1433
|
+
configurable: true,
|
|
1434
|
+
enumerable: false,
|
|
1435
|
+
writable: false,
|
|
1436
|
+
});
|
|
1437
|
+
return gt;
|
|
1438
|
+
},
|
|
1439
|
+
});
|
|
1443
1440
|
}
|
|
1444
1441
|
|
|
1445
1442
|
function makeClass(namespace, typeName) {
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1443
|
+
const ctor = function ctor(props) {
|
|
1444
|
+
// G3/G2 construction routing. `new.target` is the leaf class the `new` was
|
|
1445
|
+
// applied to and is preserved through every super() hop, so when a registerClass'd
|
|
1446
|
+
// subclass is being constructed (directly or up a super() chain — at ANY depth,
|
|
1447
|
+
// since the leaf's registered GType already IS the full multi-level type) we build
|
|
1448
|
+
// the REGISTERED GType and return its canonical toggle-ref wrapper (carrying the
|
|
1449
|
+
// leaf's prototype as USER_PROTO) — which then substitutes `this` in every derived
|
|
1450
|
+
// ctor body. `new.target === undefined` (a call without `new`) or `=== proxy` (a
|
|
1451
|
+
// direct `new Ns.Class(...)` on the introspected class itself) falls through to the
|
|
1452
|
+
// unchanged introspected construction.
|
|
1453
|
+
const nt = new.target;
|
|
1454
|
+
if (nt !== undefined && nt !== proxy) {
|
|
1455
|
+
const reg = registeredClasses.get(nt);
|
|
1456
|
+
if (reg !== undefined) {
|
|
1457
|
+
let handle;
|
|
1458
|
+
if (adoptHandle !== undefined) {
|
|
1459
|
+
// C-created adoption (runCtorForCObject): the engine already built the
|
|
1460
|
+
// GObject; use its canonical handle instead of constructing another.
|
|
1461
|
+
// Consume the latch atomically so a nested `new` in the ctor body takes
|
|
1462
|
+
// the normal constructType path.
|
|
1463
|
+
handle = adoptHandle;
|
|
1464
|
+
adoptHandle = undefined;
|
|
1465
|
+
} else {
|
|
1466
|
+
handle = native.constructType(reg.typeHandle, props ? unwrapProps(props) : {});
|
|
1467
|
+
}
|
|
1468
|
+
const instance = wrapInstance(handle, nt.prototype);
|
|
1469
|
+
assignTemplateChildren(instance, handle, reg);
|
|
1470
|
+
// Route construct-time property values through the class's JS setters now
|
|
1471
|
+
// that USER_PROTO is attached — before the user ctor body runs (GJS order).
|
|
1472
|
+
flushConstructProperties(instance, handle, reg);
|
|
1473
|
+
return instance;
|
|
1474
|
+
}
|
|
1475
|
+
// `nt` is a subclass of this introspected GObject class but was NEVER passed to
|
|
1476
|
+
// GObject.registerClass(): silently building the introspected BASE type would
|
|
1477
|
+
// produce the wrong GType (missing the subclass's props/signals/vfuncs). Throw
|
|
1478
|
+
// the GJS-shaped error instead of constructing a misleading instance — the L1
|
|
1479
|
+
// analogue of GJS's "are you using GObject.registerClass()?" construct guard
|
|
1480
|
+
// (refs/gjs/gi/object.cpp).
|
|
1481
|
+
throw new Error(`Object ${nt.name || '(anonymous)'} is not registered with GObject.registerClass()`);
|
|
1470
1482
|
}
|
|
1471
|
-
const
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
if (typeof prop !== 'string' || prop in t || RESERVED.has(prop)) return t[prop];
|
|
1553
|
-
// GObject.Object.new(gtype, props) / .new_with_properties(gtype, names,
|
|
1554
|
-
// values): gjs's GObject.js statics that construct a GObject from a runtime
|
|
1555
|
-
// GType. Not introspected (g_object_new is variadic), so route them to the
|
|
1556
|
-
// by-GType constructor (native.constructType accepts any GType handle).
|
|
1557
|
-
if (namespace === 'GObject' && typeName === 'Object' &&
|
|
1558
|
-
(prop === 'new' || prop === 'new_with_properties' || prop === 'newWithProperties')) {
|
|
1559
|
-
return objectStaticConstructor(prop);
|
|
1560
|
-
}
|
|
1561
|
-
const giName = camelToSnake(prop);
|
|
1562
|
-
return (...args) =>
|
|
1563
|
-
wrapReturn(native.callStaticMethod(namespace, typeName, giName, unwrapArgs(args)));
|
|
1564
|
-
},
|
|
1565
|
-
});
|
|
1566
|
-
return proxy;
|
|
1483
|
+
const handle = native.newObject(namespace, typeName, props ? unwrapProps(props) : {});
|
|
1484
|
+
return wrapInstance(handle);
|
|
1485
|
+
};
|
|
1486
|
+
Object.defineProperty(ctor, 'name', { value: typeName, configurable: true });
|
|
1487
|
+
ctor.$gtypeName = `${namespace}.${typeName}`;
|
|
1488
|
+
defineLazyGType(ctor, namespace, typeName);
|
|
1489
|
+
// Put the vfunc chain-up Proxy beneath this base class's prototype so a
|
|
1490
|
+
// registerClass subclass's `super.vfunc_<name>(...)` resolves (see above).
|
|
1491
|
+
Object.setPrototypeOf(ctor.prototype, vfuncChainProto);
|
|
1492
|
+
// Stamp the prototype with its class identity so Gio._promisify(Cls.prototype, …)
|
|
1493
|
+
// can record which class a registration belongs to (non-enumerable).
|
|
1494
|
+
Object.defineProperty(ctor.prototype, CLASS_INFO, {
|
|
1495
|
+
value: { namespace, typeName },
|
|
1496
|
+
enumerable: false,
|
|
1497
|
+
configurable: true,
|
|
1498
|
+
});
|
|
1499
|
+
// `inst instanceof Ns.Class` — GJS parity for the WHOLE GObject hierarchy, not
|
|
1500
|
+
// just the leaf class. An instance is a Proxy over a bare `{[HANDLE]}` (no live JS
|
|
1501
|
+
// prototype chain linking Adw.ApplicationWindow → Gtk.ApplicationWindow → …), so
|
|
1502
|
+
// the default instanceof (a prototype-chain walk) reported `false` for every base
|
|
1503
|
+
// class. Resolve it by the GObject type system instead: `native.isInstanceOf`
|
|
1504
|
+
// (g_type_is_a) recognises subclasses AND implemented interfaces, exactly like GJS.
|
|
1505
|
+
// Guarded by `isGObjectHandle` first — a bare/boxed/variant handle (or any non-node-gi
|
|
1506
|
+
// value) is NOT a GObject, so it must be `false`, never reach `isInstanceOf` (which
|
|
1507
|
+
// throws on a non-GObject handle). The makeClass Proxy `get` trap forwards this
|
|
1508
|
+
// symbol to the target (`typeof prop !== 'string'` → `t[prop]`), so `instanceof`
|
|
1509
|
+
// finds it on the proxy. Overlay-built classes (GLib.Variant / GObject.Value /
|
|
1510
|
+
// GObject.ParamSpec) bypass makeClass and keep their own dedicated hasInstance.
|
|
1511
|
+
Object.defineProperty(ctor, Symbol.hasInstance, {
|
|
1512
|
+
configurable: true,
|
|
1513
|
+
value(instance) {
|
|
1514
|
+
if (instance === null || typeof instance !== 'object') return false;
|
|
1515
|
+
const handle = instance[HANDLE];
|
|
1516
|
+
if (handle === undefined || !native.isGObjectHandle(handle)) return false;
|
|
1517
|
+
// The introspected class ITSELF → resolve by name: g_type_is_a recognises every
|
|
1518
|
+
// subclass AND implemented interface, matching GJS exactly.
|
|
1519
|
+
if (this === proxy) return native.isInstanceOf(handle, namespace, typeName);
|
|
1520
|
+
// A registerClass'd subclass INHERITS this method (it `extends` the proxy) but
|
|
1521
|
+
// must match ITS OWN registered GType, not the shared introspected base — else a
|
|
1522
|
+
// sibling subclass or a bare-base instance would wrongly test true. Resolve the
|
|
1523
|
+
// instance's runtime GType against the subclass's own `$gtype` through the GObject
|
|
1524
|
+
// type system (a live JS prototype chain the subclass instance lacks — the L1
|
|
1525
|
+
// wrapper carries the user prototype as a symbol, not as [[Prototype]]).
|
|
1526
|
+
const subGType = this.$gtype;
|
|
1527
|
+
if (subGType !== undefined && subGType !== null) {
|
|
1528
|
+
const G = requireGi('GObject', '2.0');
|
|
1529
|
+
return G.type_is_a(G.type_from_name(native.getTypeName(handle)), subGType);
|
|
1530
|
+
}
|
|
1531
|
+
// A raw (unregistered) subclass / unrelated ctor: ordinary prototype-chain.
|
|
1532
|
+
return Function.prototype[Symbol.hasInstance].call(this, instance);
|
|
1533
|
+
},
|
|
1534
|
+
});
|
|
1535
|
+
// Expose constructor/static methods lazily: Ns.Class.new(...) /
|
|
1536
|
+
// Ns.Class.new_for_path(...) (and the camelCase aliases). `new Ns.Class({...})`
|
|
1537
|
+
// still goes through the target's [[Construct]] (the default Proxy behaviour).
|
|
1538
|
+
const proxy = new Proxy(ctor, {
|
|
1539
|
+
get(t, prop, receiver) {
|
|
1540
|
+
// #667: a raw (UNregistered) subclass that `extends` this introspected class
|
|
1541
|
+
// must NOT inherit-read the parent's GType. A registered subclass has its OWN
|
|
1542
|
+
// `$gtype` (set in place by registerClass, so it shadows + never reaches this
|
|
1543
|
+
// trap); a raw subclass is not a GType, so an inherited read (receiver is the
|
|
1544
|
+
// subclass, not this proxy) resolves to undefined. A direct read on the
|
|
1545
|
+
// introspected class itself (receiver === proxy) resolves the real lazy GType.
|
|
1546
|
+
if (prop === '$gtype' && receiver !== proxy) return undefined;
|
|
1547
|
+
if (typeof prop !== 'string' || prop in t || RESERVED.has(prop)) return t[prop];
|
|
1548
|
+
// GObject.Object.new(gtype, props) / .new_with_properties(gtype, names,
|
|
1549
|
+
// values): gjs's GObject.js statics that construct a GObject from a runtime
|
|
1550
|
+
// GType. Not introspected (g_object_new is variadic), so route them to the
|
|
1551
|
+
// by-GType constructor (native.constructType accepts any GType handle).
|
|
1552
|
+
if (
|
|
1553
|
+
namespace === 'GObject' &&
|
|
1554
|
+
typeName === 'Object' &&
|
|
1555
|
+
(prop === 'new' || prop === 'new_with_properties' || prop === 'newWithProperties')
|
|
1556
|
+
) {
|
|
1557
|
+
return objectStaticConstructor(prop);
|
|
1558
|
+
}
|
|
1559
|
+
const giName = camelToSnake(prop);
|
|
1560
|
+
return (...args) => wrapReturn(native.callStaticMethod(namespace, typeName, giName, unwrapArgs(args)));
|
|
1561
|
+
},
|
|
1562
|
+
});
|
|
1563
|
+
return proxy;
|
|
1567
1564
|
}
|
|
1568
1565
|
|
|
1569
1566
|
// The GObject.Object.new / new_with_properties statics (gjs GObject.js). `new`
|
|
@@ -1573,24 +1570,22 @@ function makeClass(namespace, typeName) {
|
|
|
1573
1570
|
// arrays into a prop dict and delegates. Mirrors GJS's `GObject.Object.new`, which
|
|
1574
1571
|
// looks up the constructor for `gtype` and `new`s it with the prop dict.
|
|
1575
1572
|
function objectStaticConstructor(prop) {
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
return objectNew(gtype, props);
|
|
1593
|
-
};
|
|
1573
|
+
const objectNew = (gtype, props = {}) => {
|
|
1574
|
+
const gt = resolveGTypeArg(gtype);
|
|
1575
|
+
if (gt === undefined || gt === null) {
|
|
1576
|
+
throw new TypeError('GObject.Object.new: a GType (class or Class.$gtype) is required');
|
|
1577
|
+
}
|
|
1578
|
+
return wrapInstance(native.constructType(gt, props ? unwrapProps(props) : {}));
|
|
1579
|
+
};
|
|
1580
|
+
if (prop === 'new') return objectNew;
|
|
1581
|
+
return (gtype, names, values) => {
|
|
1582
|
+
if (!Array.isArray(names) || !Array.isArray(values) || names.length !== values.length) {
|
|
1583
|
+
throw new Error('GObject.Object.new_with_properties takes two equal-length arrays (names, values)');
|
|
1584
|
+
}
|
|
1585
|
+
const props = {};
|
|
1586
|
+
for (let i = 0; i < names.length; i++) props[names[i]] = values[i];
|
|
1587
|
+
return objectNew(gtype, props);
|
|
1588
|
+
};
|
|
1594
1589
|
}
|
|
1595
1590
|
|
|
1596
1591
|
// Surface a boxed/struct type (e.g. GLib.MainLoop) as a class-like object whose
|
|
@@ -1601,43 +1596,42 @@ function objectStaticConstructor(prop) {
|
|
|
1601
1596
|
// (`new Graphene.Rect()`, `new Gdk.RGBA()` — the devtools screenshot chain).
|
|
1602
1597
|
// Returned boxed instances are wrapped by {@link wrapBoxed}.
|
|
1603
1598
|
function makeStruct(namespace, typeName) {
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
}
|
|
1621
|
-
|
|
1622
|
-
return proxy;
|
|
1599
|
+
const base = function () {};
|
|
1600
|
+
Object.defineProperty(base, 'name', { value: typeName, configurable: true });
|
|
1601
|
+
base.$gtypeName = `${namespace}.${typeName}`;
|
|
1602
|
+
defineLazyGType(base, namespace, typeName);
|
|
1603
|
+
const proxy = new Proxy(base, {
|
|
1604
|
+
get(t, prop, receiver) {
|
|
1605
|
+
// #667: guard inherited `$gtype` reads (see makeClass) — a raw subclass of a
|
|
1606
|
+
// boxed/struct type does not inherit the parent's GType.
|
|
1607
|
+
if (prop === '$gtype' && receiver !== proxy) return undefined;
|
|
1608
|
+
if (typeof prop !== 'string' || prop in t || RESERVED.has(prop)) return t[prop];
|
|
1609
|
+
const giName = camelToSnake(prop);
|
|
1610
|
+
return (...args) => wrapReturn(native.callStaticMethod(namespace, typeName, giName, unwrapArgs(args)));
|
|
1611
|
+
},
|
|
1612
|
+
construct(_t, args) {
|
|
1613
|
+
return wrapReturn(native.constructStruct(namespace, typeName, unwrapArgs(args)));
|
|
1614
|
+
},
|
|
1615
|
+
});
|
|
1616
|
+
return proxy;
|
|
1623
1617
|
}
|
|
1624
1618
|
|
|
1625
1619
|
// Build a frozen enum/flags object keyed GJS-style: member names UPPER_CASED
|
|
1626
1620
|
// with '-' → '_' (e.g. Gio.BusType.SYSTEM, Gio.ApplicationFlags.HANDLES_OPEN).
|
|
1627
1621
|
function makeEnum(namespace, typeName) {
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1622
|
+
const raw = native.getEnumValues(namespace, typeName);
|
|
1623
|
+
const out = {};
|
|
1624
|
+
for (const key of Object.keys(raw)) {
|
|
1625
|
+
out[key.toUpperCase().replace(/-/g, '_')] = raw[key];
|
|
1626
|
+
}
|
|
1627
|
+
// If this enum is registered as a GError domain (Gio.IOErrorEnum, …), attach
|
|
1628
|
+
// its domain descriptor (non-enumerable) so GLib.Error.matches(enum, code) can
|
|
1629
|
+
// resolve the enum to its domain. A plain enum gets nothing (getErrorDomain → null).
|
|
1630
|
+
const domain = native.getErrorDomain(namespace, typeName);
|
|
1631
|
+
if (domain !== null) {
|
|
1632
|
+
Object.defineProperty(out, ERROR_DOMAIN, { value: domain, enumerable: false });
|
|
1633
|
+
}
|
|
1634
|
+
return Object.freeze(out);
|
|
1641
1635
|
}
|
|
1642
1636
|
|
|
1643
1637
|
// ---- Gio._promisify (L1, GJS-shaped) ----
|
|
@@ -1658,53 +1652,53 @@ function makeEnum(namespace, typeName) {
|
|
|
1658
1652
|
|
|
1659
1653
|
// _async / _begin → _finish; otherwise <name>_finish (GJS's convention).
|
|
1660
1654
|
function deriveFinishName(asyncName) {
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1655
|
+
if (asyncName.endsWith('_begin') || asyncName.endsWith('_async')) {
|
|
1656
|
+
return `${asyncName.slice(0, -5)}finish`;
|
|
1657
|
+
}
|
|
1658
|
+
return `${asyncName}_finish`;
|
|
1665
1659
|
}
|
|
1666
1660
|
|
|
1667
1661
|
// Build the promisified wrapper for `asyncName`. Uses `this` (the instance proxy)
|
|
1668
1662
|
// so it works both bound from the registry and as a prototype method.
|
|
1669
1663
|
function makePromisified(asyncName, finishName) {
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
}
|
|
1681
|
-
const self = this;
|
|
1682
|
-
return new Promise((resolve, reject) => {
|
|
1683
|
-
// wrapSignalCallback marshals each native callback arg (source, res) through
|
|
1684
|
-
// wrapReturn → wrapped instances, so both `source[finishName](res)` and the
|
|
1685
|
-
// captured-instance fallback round-trip cleanly.
|
|
1686
|
-
const onReady = wrapSignalCallback((source, res) => {
|
|
1687
|
-
try {
|
|
1688
|
-
const finisher =
|
|
1689
|
-
source !== null && source !== undefined && typeof source[finishName] === 'function'
|
|
1690
|
-
? source[finishName].bind(source)
|
|
1691
|
-
: self[finishName].bind(self);
|
|
1692
|
-
const value = finisher(res);
|
|
1693
|
-
// GJS's _promisify drops a leading `true` ok-flag so the dominant
|
|
1694
|
-
// finish shape (load_contents_finish, query_info_finish,
|
|
1695
|
-
// communicate_utf8_finish, … → [true, …]) resolves to just the
|
|
1696
|
-
// payload — keeping one source byte-identical on gjs + node.
|
|
1697
|
-
if (Array.isArray(value) && value.length > 1 && value[0] === true) {
|
|
1698
|
-
value.shift();
|
|
1699
|
-
}
|
|
1700
|
-
resolve(value);
|
|
1701
|
-
} catch (error) {
|
|
1702
|
-
reject(error);
|
|
1664
|
+
const giAsync = camelToSnake(asyncName);
|
|
1665
|
+
return function promisified(...args) {
|
|
1666
|
+
const handle = this[HANDLE];
|
|
1667
|
+
// A trailing callback → plain async method (the caller drives the callback).
|
|
1668
|
+
// Wrap it so its (source, result) args are marshalled to wrapped instances —
|
|
1669
|
+
// matching the Promise path and GJS, so `source.finish(result)` works.
|
|
1670
|
+
if (args.length > 0 && typeof args[args.length - 1] === 'function') {
|
|
1671
|
+
const userCb = args[args.length - 1];
|
|
1672
|
+
const forwarded = [...args.slice(0, -1), wrapSignalCallback(userCb)];
|
|
1673
|
+
return wrapReturn(native.callMethod(handle, giAsync, unwrapArgs(forwarded)));
|
|
1703
1674
|
}
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1675
|
+
const self = this;
|
|
1676
|
+
return new Promise((resolve, reject) => {
|
|
1677
|
+
// wrapSignalCallback marshals each native callback arg (source, res) through
|
|
1678
|
+
// wrapReturn → wrapped instances, so both `source[finishName](res)` and the
|
|
1679
|
+
// captured-instance fallback round-trip cleanly.
|
|
1680
|
+
const onReady = wrapSignalCallback((source, res) => {
|
|
1681
|
+
try {
|
|
1682
|
+
const finisher =
|
|
1683
|
+
source !== null && source !== undefined && typeof source[finishName] === 'function'
|
|
1684
|
+
? source[finishName].bind(source)
|
|
1685
|
+
: self[finishName].bind(self);
|
|
1686
|
+
const value = finisher(res);
|
|
1687
|
+
// GJS's _promisify drops a leading `true` ok-flag so the dominant
|
|
1688
|
+
// finish shape (load_contents_finish, query_info_finish,
|
|
1689
|
+
// communicate_utf8_finish, … → [true, …]) resolves to just the
|
|
1690
|
+
// payload — keeping one source byte-identical on gjs + node.
|
|
1691
|
+
if (Array.isArray(value) && value.length > 1 && value[0] === true) {
|
|
1692
|
+
value.shift();
|
|
1693
|
+
}
|
|
1694
|
+
resolve(value);
|
|
1695
|
+
} catch (error) {
|
|
1696
|
+
reject(error);
|
|
1697
|
+
}
|
|
1698
|
+
});
|
|
1699
|
+
native.callMethod(handle, giAsync, unwrapArgs([...args, onReady]));
|
|
1700
|
+
});
|
|
1701
|
+
};
|
|
1708
1702
|
}
|
|
1709
1703
|
|
|
1710
1704
|
/**
|
|
@@ -1719,61 +1713,61 @@ function makePromisified(asyncName, finishName) {
|
|
|
1719
1713
|
* @returns {void}
|
|
1720
1714
|
*/
|
|
1721
1715
|
function promisify(proto, asyncName, finishName = undefined) {
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1716
|
+
if (proto === null || typeof proto !== 'object') {
|
|
1717
|
+
throw new TypeError('Gio._promisify: prototype must be an object');
|
|
1718
|
+
}
|
|
1719
|
+
if (typeof asyncName !== 'string') {
|
|
1720
|
+
throw new TypeError('Gio._promisify: asyncName must be a string');
|
|
1721
|
+
}
|
|
1722
|
+
const finish = finishName === undefined ? deriveFinishName(asyncName) : finishName;
|
|
1723
|
+
const wrapper = makePromisified(asyncName, finish);
|
|
1724
|
+
// Install on the prototype (so proto[asyncName] IS the promisified method, and a
|
|
1725
|
+
// registerClass subclass — which resolves its userProto — picks it up) AND in
|
|
1726
|
+
// the global registry (introspected instances resolve methods dynamically, with
|
|
1727
|
+
// no live prototype chain to walk).
|
|
1728
|
+
try {
|
|
1729
|
+
proto[asyncName] = wrapper;
|
|
1730
|
+
} catch {
|
|
1731
|
+
// A frozen/sealed prototype — the registry still makes it work.
|
|
1732
|
+
}
|
|
1733
|
+
// Record the registration keyed by the GI method name, tagged with the class it
|
|
1734
|
+
// was registered on (CLASS_INFO from makeClass) so two classes promisifying the
|
|
1735
|
+
// same method name with different finish methods stay disambiguated per-class.
|
|
1736
|
+
const info = proto[CLASS_INFO];
|
|
1737
|
+
const registration = {
|
|
1738
|
+
namespace: info ? info.namespace : undefined,
|
|
1739
|
+
typeName: info ? info.typeName : undefined,
|
|
1740
|
+
wrapper,
|
|
1741
|
+
};
|
|
1742
|
+
const key = camelToSnake(asyncName);
|
|
1743
|
+
const existing = promisifiedMethods.get(key);
|
|
1744
|
+
if (existing === undefined) {
|
|
1745
|
+
promisifiedMethods.set(key, [registration]);
|
|
1746
|
+
} else {
|
|
1747
|
+
// Replace a same-class registration in place; otherwise append (a second class).
|
|
1748
|
+
const i = existing.findIndex(
|
|
1749
|
+
(r) => r.namespace === registration.namespace && r.typeName === registration.typeName,
|
|
1750
|
+
);
|
|
1751
|
+
if (i >= 0) existing[i] = registration;
|
|
1752
|
+
else existing.push(registration);
|
|
1753
|
+
}
|
|
1760
1754
|
}
|
|
1761
1755
|
|
|
1762
1756
|
// Resolve the promisified wrapper for `methodName` on an instance handle. The
|
|
1763
1757
|
// common case is a single registration (fast path); when several classes
|
|
1764
1758
|
// promisified the same method name, pick the one whose class the instance is-a.
|
|
1765
1759
|
function resolvePromisified(handle, methodName) {
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1760
|
+
const registrations = promisifiedMethods.get(methodName);
|
|
1761
|
+
if (registrations === undefined) return undefined;
|
|
1762
|
+
if (registrations.length === 1) return registrations[0].wrapper;
|
|
1763
|
+
for (const r of registrations) {
|
|
1764
|
+
if (r.namespace !== undefined && native.isInstanceOf(handle, r.namespace, r.typeName)) {
|
|
1765
|
+
return r.wrapper;
|
|
1766
|
+
}
|
|
1772
1767
|
}
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
return registrations[registrations.length - 1].wrapper;
|
|
1768
|
+
// No class matched (or a registerClass registration with no CLASS_INFO) — fall
|
|
1769
|
+
// back to the last registration so a single-class misconfig still resolves.
|
|
1770
|
+
return registrations[registrations.length - 1].wrapper;
|
|
1777
1771
|
}
|
|
1778
1772
|
|
|
1779
1773
|
// ---- GObject.registerClass decorator (L1, GJS-shaped) ----
|
|
@@ -1817,18 +1811,18 @@ function resolvePromisified(handle, methodName) {
|
|
|
1817
1811
|
|
|
1818
1812
|
// GObject.ParamFlags — the GParamFlags bits the native property builder consumes.
|
|
1819
1813
|
const ParamFlags = Object.freeze({
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1814
|
+
READABLE: 1,
|
|
1815
|
+
WRITABLE: 2,
|
|
1816
|
+
READWRITE: 3,
|
|
1817
|
+
CONSTRUCT: 4,
|
|
1818
|
+
CONSTRUCT_ONLY: 8,
|
|
1825
1819
|
});
|
|
1826
1820
|
|
|
1827
1821
|
// GObject.SignalFlags — the GSignalFlags bits (native defaults to RUN_LAST).
|
|
1828
1822
|
const SignalFlags = Object.freeze({
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1823
|
+
RUN_FIRST: 1,
|
|
1824
|
+
RUN_LAST: 2,
|
|
1825
|
+
RUN_CLEANUP: 4,
|
|
1832
1826
|
});
|
|
1833
1827
|
|
|
1834
1828
|
// A ParamSpec factory returns a plain descriptor the decorator maps to the
|
|
@@ -1836,29 +1830,29 @@ const SignalFlags = Object.freeze({
|
|
|
1836
1830
|
// default); the numeric kinds capture (name, nick, blurb, flags, min, max,
|
|
1837
1831
|
// default) — matching GJS's GObject.ParamSpec.* argument order.
|
|
1838
1832
|
function paramSpecPlain(type) {
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1833
|
+
return (name, nick, blurb, flags, defaultValue) => ({
|
|
1834
|
+
$paramSpec: true,
|
|
1835
|
+
type,
|
|
1836
|
+
name,
|
|
1837
|
+
nick,
|
|
1838
|
+
blurb,
|
|
1839
|
+
flags,
|
|
1840
|
+
default: defaultValue,
|
|
1841
|
+
});
|
|
1848
1842
|
}
|
|
1849
1843
|
|
|
1850
1844
|
function paramSpecRanged(type) {
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1845
|
+
return (name, nick, blurb, flags, minimum, maximum, defaultValue) => ({
|
|
1846
|
+
$paramSpec: true,
|
|
1847
|
+
type,
|
|
1848
|
+
name,
|
|
1849
|
+
nick,
|
|
1850
|
+
blurb,
|
|
1851
|
+
flags,
|
|
1852
|
+
minimum,
|
|
1853
|
+
maximum,
|
|
1854
|
+
default: defaultValue,
|
|
1855
|
+
});
|
|
1862
1856
|
}
|
|
1863
1857
|
|
|
1864
1858
|
// Resolve a GObject.ParamSpec.object/.boxed `gtype` argument to a native GType
|
|
@@ -1866,12 +1860,12 @@ function paramSpecRanged(type) {
|
|
|
1866
1860
|
// ctor (introspected `Adw.Bin` / `GObject.Object`, or a registered class) whose
|
|
1867
1861
|
// real `$gtype` is the GType handle (G4), or an already-resolved GType handle.
|
|
1868
1862
|
function resolveGTypeArg(gtype) {
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1863
|
+
if (gtype === null || gtype === undefined) return gtype;
|
|
1864
|
+
if (typeof gtype === 'function' || typeof gtype === 'object') {
|
|
1865
|
+
const gt = gtype.$gtype;
|
|
1866
|
+
if (gt !== undefined) return gt;
|
|
1867
|
+
}
|
|
1868
|
+
return gtype; // assume it is already a GType handle
|
|
1875
1869
|
}
|
|
1876
1870
|
|
|
1877
1871
|
// object/boxed ParamSpec factories: `(name, nick, blurb, flags, gtype)` — matching
|
|
@@ -1879,37 +1873,37 @@ function resolveGTypeArg(gtype) {
|
|
|
1879
1873
|
// (its `$gtype` is read) or a GType handle. Produces a real g_param_spec_object /
|
|
1880
1874
|
// g_param_spec_boxed via the engine's BuildParamSpec.
|
|
1881
1875
|
function paramSpecGTyped(type) {
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1876
|
+
return (name, nick, blurb, flags, gtype) => ({
|
|
1877
|
+
$paramSpec: true,
|
|
1878
|
+
type,
|
|
1879
|
+
name,
|
|
1880
|
+
nick,
|
|
1881
|
+
blurb,
|
|
1882
|
+
flags,
|
|
1883
|
+
gtype: resolveGTypeArg(gtype),
|
|
1884
|
+
});
|
|
1891
1885
|
}
|
|
1892
1886
|
|
|
1893
1887
|
const ParamSpec = Object.freeze({
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1888
|
+
string: paramSpecPlain('string'),
|
|
1889
|
+
boolean: paramSpecPlain('boolean'),
|
|
1890
|
+
int: paramSpecRanged('int'),
|
|
1891
|
+
uint: paramSpecRanged('uint'),
|
|
1892
|
+
int64: paramSpecRanged('int64'),
|
|
1893
|
+
uint64: paramSpecRanged('uint64'),
|
|
1894
|
+
double: paramSpecRanged('double'),
|
|
1895
|
+
float: paramSpecRanged('float'),
|
|
1896
|
+
object: paramSpecGTyped('object'),
|
|
1897
|
+
boxed: paramSpecGTyped('boxed'),
|
|
1898
|
+
// `wrappedPspec instanceof GObject.ParamSpec` — a wrapped GParamSpec (from a
|
|
1899
|
+
// notify handler / a GParamSpec-typed value) carries [HANDLE], recognised via
|
|
1900
|
+
// the native tag. `instanceof` honours a `[Symbol.hasInstance]` on the RHS even
|
|
1901
|
+
// though ParamSpec is a factory object rather than a constructor.
|
|
1902
|
+
[Symbol.hasInstance](instance) {
|
|
1903
|
+
if (instance === null || typeof instance !== 'object') return false;
|
|
1904
|
+
const handle = instance[HANDLE];
|
|
1905
|
+
return handle !== undefined && native.isParamSpecHandle(handle);
|
|
1906
|
+
},
|
|
1913
1907
|
});
|
|
1914
1908
|
|
|
1915
1909
|
// Walk up the JS class's prototype chain to the nearest node-gi base and describe
|
|
@@ -1924,69 +1918,65 @@ const ParamSpec = Object.freeze({
|
|
|
1924
1918
|
// A registered class carries BOTH a dotless `$gtypeName` AND a registeredClasses
|
|
1925
1919
|
// entry, so the registry check MUST come first.
|
|
1926
1920
|
function findParentGType(klass) {
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
);
|
|
1921
|
+
for (let c = Object.getPrototypeOf(klass); typeof c === 'function'; c = Object.getPrototypeOf(c)) {
|
|
1922
|
+
const reg = registeredClasses.get(c);
|
|
1923
|
+
if (reg !== undefined) {
|
|
1924
|
+
return { parentGTypeHandle: reg.typeHandle };
|
|
1925
|
+
}
|
|
1926
|
+
const gt = c.$gtypeName;
|
|
1927
|
+
if (typeof gt === 'string') {
|
|
1928
|
+
const dot = gt.indexOf('.');
|
|
1929
|
+
if (dot > 0) {
|
|
1930
|
+
return { parentNamespace: gt.slice(0, dot), parentType: gt.slice(dot + 1) };
|
|
1931
|
+
}
|
|
1932
|
+
// A dotless `$gtypeName` with no registeredClasses entry should never occur
|
|
1933
|
+
// (only registered types are dotless, and they are in the registry); be
|
|
1934
|
+
// explicit rather than mis-resolving the namespace split.
|
|
1935
|
+
throw new TypeError("GObject.registerClass: parent '" + gt + "' has no resolvable GType");
|
|
1936
|
+
}
|
|
1944
1937
|
}
|
|
1945
|
-
|
|
1946
|
-
return undefined;
|
|
1938
|
+
return undefined;
|
|
1947
1939
|
}
|
|
1948
1940
|
|
|
1949
1941
|
// Map a GObject.ParamSpec.* descriptor (from meta.Properties) to a native
|
|
1950
1942
|
// PropertySpec. The property name defaults to the Properties key.
|
|
1951
1943
|
function paramSpecToNative(desc, key) {
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
if (desc.gtype !== undefined) spec.gtype = desc.gtype;
|
|
1966
|
-
return spec;
|
|
1944
|
+
if (desc === null || typeof desc !== 'object') {
|
|
1945
|
+
throw new TypeError("GObject.registerClass: Properties['" + key + "'] must be a GObject.ParamSpec descriptor");
|
|
1946
|
+
}
|
|
1947
|
+
const spec = { name: typeof desc.name === 'string' && desc.name.length > 0 ? desc.name : key };
|
|
1948
|
+
spec.type = desc.type;
|
|
1949
|
+
if (typeof desc.flags === 'number') spec.flags = desc.flags;
|
|
1950
|
+
if (desc.default !== undefined) spec.default = desc.default;
|
|
1951
|
+
if (typeof desc.minimum === 'number') spec.minimum = desc.minimum;
|
|
1952
|
+
if (typeof desc.maximum === 'number') spec.maximum = desc.maximum;
|
|
1953
|
+
// object/boxed ParamSpecs carry a value GType handle (passed straight to the
|
|
1954
|
+
// engine's BuildParamSpec → g_param_spec_object / g_param_spec_boxed).
|
|
1955
|
+
if (desc.gtype !== undefined) spec.gtype = desc.gtype;
|
|
1956
|
+
return spec;
|
|
1967
1957
|
}
|
|
1968
1958
|
|
|
1969
1959
|
// Normalise a signal param/return type to the native type-name vocabulary: a
|
|
1970
1960
|
// string passes through; a ParamSpec descriptor contributes its `.type`.
|
|
1971
1961
|
function normalizeSignalType(t) {
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1962
|
+
if (typeof t === 'string') return t;
|
|
1963
|
+
if (t !== null && typeof t === 'object' && typeof t.type === 'string') return t.type;
|
|
1964
|
+
return undefined;
|
|
1975
1965
|
}
|
|
1976
1966
|
|
|
1977
1967
|
// Map a meta.Signals entry (`{ param_types?, return_type?, flags? }`, GJS keys,
|
|
1978
1968
|
// camelCase also accepted) to a native SignalSpec; the signal name is the key.
|
|
1979
1969
|
function signalSpecToNative(spec, key) {
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1970
|
+
const s = spec !== null && typeof spec === 'object' ? spec : {};
|
|
1971
|
+
const out = { name: key };
|
|
1972
|
+
const params = s.param_types !== undefined ? s.param_types : s.paramTypes;
|
|
1973
|
+
if (Array.isArray(params)) {
|
|
1974
|
+
out.paramTypes = params.map(normalizeSignalType).filter((t) => t !== undefined);
|
|
1975
|
+
}
|
|
1976
|
+
const ret = normalizeSignalType(s.return_type !== undefined ? s.return_type : s.returnType);
|
|
1977
|
+
if (ret !== undefined) out.returnType = ret;
|
|
1978
|
+
if (typeof s.flags === 'number') out.flags = s.flags;
|
|
1979
|
+
return out;
|
|
1990
1980
|
}
|
|
1991
1981
|
|
|
1992
1982
|
// Collect this class's OWN newly-declared `vfunc_<name>` methods into the native
|
|
@@ -2008,23 +1998,23 @@ function signalSpecToNative(spec, key) {
|
|
|
2008
1998
|
// the leaf and the nearest registered ancestor ARE still collected — they have no
|
|
2009
1999
|
// GType of their own, so their vfuncs only reach the vtable through the leaf.
|
|
2010
2000
|
function collectVfuncs(klass) {
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2001
|
+
const vfuncs = {};
|
|
2002
|
+
const seen = new Set();
|
|
2003
|
+
for (let p = klass.prototype; p !== null && p !== Object.prototype; p = Object.getPrototypeOf(p)) {
|
|
2004
|
+
const owner = Object.getOwnPropertyDescriptor(p, 'constructor')?.value;
|
|
2005
|
+
if (owner !== undefined && owner !== klass && registeredClasses.has(owner)) break;
|
|
2006
|
+
for (const key of Object.getOwnPropertyNames(p)) {
|
|
2007
|
+
if (!key.startsWith('vfunc_') || seen.has(key)) continue;
|
|
2008
|
+
const desc = Object.getOwnPropertyDescriptor(p, key);
|
|
2009
|
+
if (desc === undefined || typeof desc.value !== 'function') continue;
|
|
2010
|
+
seen.add(key);
|
|
2011
|
+
const userFn = desc.value;
|
|
2012
|
+
vfuncs[key.slice('vfunc_'.length)] = function (...args) {
|
|
2013
|
+
return userFn.apply(wrapInstance(this, klass.prototype), args);
|
|
2014
|
+
};
|
|
2015
|
+
}
|
|
2025
2016
|
}
|
|
2026
|
-
|
|
2027
|
-
return vfuncs;
|
|
2017
|
+
return vfuncs;
|
|
2028
2018
|
}
|
|
2029
2019
|
|
|
2030
2020
|
/**
|
|
@@ -2042,120 +2032,119 @@ function collectVfuncs(klass) {
|
|
|
2042
2032
|
* @returns {Function} the same `klass` (now registered)
|
|
2043
2033
|
*/
|
|
2044
2034
|
function registerClass(metaOrClass, maybeClass) {
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2035
|
+
let meta;
|
|
2036
|
+
let klass;
|
|
2037
|
+
if (typeof metaOrClass === 'function') {
|
|
2038
|
+
klass = metaOrClass;
|
|
2039
|
+
meta = {};
|
|
2040
|
+
} else {
|
|
2041
|
+
meta = metaOrClass !== null && typeof metaOrClass === 'object' ? metaOrClass : {};
|
|
2042
|
+
klass = maybeClass;
|
|
2043
|
+
}
|
|
2044
|
+
if (typeof klass !== 'function') {
|
|
2045
|
+
throw new TypeError('GObject.registerClass: expected a class to register');
|
|
2046
|
+
}
|
|
2047
|
+
|
|
2048
|
+
const parent = findParentGType(klass);
|
|
2049
|
+
if (parent === undefined) {
|
|
2050
|
+
throw new TypeError(
|
|
2051
|
+
'GObject.registerClass: ' +
|
|
2052
|
+
(klass.name || 'the class') +
|
|
2053
|
+
' must extend a node-gi GObject class (e.g. GObject.Object)',
|
|
2054
|
+
);
|
|
2055
|
+
}
|
|
2066
2056
|
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2057
|
+
const properties = [];
|
|
2058
|
+
if (meta.Properties !== null && typeof meta.Properties === 'object') {
|
|
2059
|
+
for (const key of Object.keys(meta.Properties)) {
|
|
2060
|
+
properties.push(paramSpecToNative(meta.Properties[key], key));
|
|
2061
|
+
}
|
|
2071
2062
|
}
|
|
2072
|
-
}
|
|
2073
2063
|
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2064
|
+
const signals = [];
|
|
2065
|
+
if (meta.Signals !== null && typeof meta.Signals === 'object') {
|
|
2066
|
+
for (const key of Object.keys(meta.Signals)) {
|
|
2067
|
+
signals.push(signalSpecToNative(meta.Signals[key], key));
|
|
2068
|
+
}
|
|
2078
2069
|
}
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
.
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
classesByGType.set(gtypeName, klass);
|
|
2158
|
-
return klass;
|
|
2070
|
+
|
|
2071
|
+
const vfuncs = collectVfuncs(klass);
|
|
2072
|
+
|
|
2073
|
+
const gtypeName = typeof meta.GTypeName === 'string' && meta.GTypeName.length > 0 ? meta.GTypeName : klass.name;
|
|
2074
|
+
if (!gtypeName) {
|
|
2075
|
+
throw new TypeError('GObject.registerClass: a GTypeName is required for an anonymous class');
|
|
2076
|
+
}
|
|
2077
|
+
|
|
2078
|
+
// Gtk.Widget composite template (GJS-shaped meta: Template / Children /
|
|
2079
|
+
// InternalChildren / CssName). Template is a Uint8Array/Buffer of inline UI-XML,
|
|
2080
|
+
// a "resource:///…" path string, or an inline UI-XML string — passed through to
|
|
2081
|
+
// the engine, which installs it in class_init (set_template[_from_resource] +
|
|
2082
|
+
// bind_template_child_full) and runs gtk_widget_init_template at construction.
|
|
2083
|
+
const options = { properties, signals, vfuncs };
|
|
2084
|
+
if (meta.Template !== undefined && meta.Template !== null) options.template = meta.Template;
|
|
2085
|
+
if (typeof meta.CssName === 'string' && meta.CssName.length > 0) options.cssName = meta.CssName;
|
|
2086
|
+
const children = Array.isArray(meta.Children) ? meta.Children.filter((c) => typeof c === 'string') : [];
|
|
2087
|
+
const internalChildren = Array.isArray(meta.InternalChildren)
|
|
2088
|
+
? meta.InternalChildren.filter((c) => typeof c === 'string')
|
|
2089
|
+
: [];
|
|
2090
|
+
if (children.length > 0) options.children = children;
|
|
2091
|
+
if (internalChildren.length > 0) options.internalChildren = internalChildren;
|
|
2092
|
+
|
|
2093
|
+
// Branch on the parent shape (findParentGType): a REGISTERED parent is subclassed
|
|
2094
|
+
// from its runtime GType handle (it has no introspection entry); an INTROSPECTED
|
|
2095
|
+
// parent is resolved by namespace+type. Construction (the leaf's registered GType
|
|
2096
|
+
// via the makeClass new.target routing) is already multi-level-correct either way.
|
|
2097
|
+
const typeHandle =
|
|
2098
|
+
parent.parentGTypeHandle !== undefined
|
|
2099
|
+
? native.registerClassFromGType(gtypeName, parent.parentGTypeHandle, options)
|
|
2100
|
+
: native.registerClass(gtypeName, parent.parentNamespace, parent.parentType, options);
|
|
2101
|
+
|
|
2102
|
+
// G3: register the GType for the GIVEN class IN PLACE (no throwaway Subclass) and
|
|
2103
|
+
// return that same class. `$gtypeName`/`$gtype` are defined as OWN properties:
|
|
2104
|
+
// `klass` inherits a getter-only `$gtype` accessor from the introspected base ctor
|
|
2105
|
+
// (via the makeClass Proxy), so a plain `klass.$gtype = …` assignment would throw
|
|
2106
|
+
// in strict mode — defineProperty installs an own data property that shadows it.
|
|
2107
|
+
Object.defineProperty(klass, '$gtypeName', {
|
|
2108
|
+
value: gtypeName,
|
|
2109
|
+
configurable: true,
|
|
2110
|
+
writable: true,
|
|
2111
|
+
});
|
|
2112
|
+
// native.registerClass returns the registered GType as a tag-distinct node-gi
|
|
2113
|
+
// GType handle (G4) — surface it as `$gtype` (GObject.type_ensure(X.$gtype),
|
|
2114
|
+
// ParamSpec.object(..., X) all read it). It is the SAME handle constructType
|
|
2115
|
+
// consumes, so registration and `$gtype` share one carrier.
|
|
2116
|
+
Object.defineProperty(klass, '$gtype', {
|
|
2117
|
+
value: typeHandle,
|
|
2118
|
+
configurable: true,
|
|
2119
|
+
enumerable: false,
|
|
2120
|
+
writable: false,
|
|
2121
|
+
});
|
|
2122
|
+
// Record the registration so the introspected base ctor (makeClass) routes
|
|
2123
|
+
// `new X(args)` (where new.target === klass) to constructType(typeHandle, …) +
|
|
2124
|
+
// the canonical wrapper. Template children move here from the old Subclass.
|
|
2125
|
+
// The names of CONSTRUCT / CONSTRUCT_ONLY properties — the ONLY ones GObject
|
|
2126
|
+
// applies at construction. The flush (flushConstructProperties) must be limited to
|
|
2127
|
+
// these: a plain READWRITE property is NOT set during construction, so running its
|
|
2128
|
+
// JS setter early (GObject never would) fires the setter's side effects against
|
|
2129
|
+
// still-uninitialised instance state (the SourceView `selectable` → `_signalHandlers`
|
|
2130
|
+
// forEach crash). Matches GJS, which only runs setters for props GObject applies at
|
|
2131
|
+
// construct. Flags are the ABI-stable G_PARAM_CONSTRUCT (1<<2) | CONSTRUCT_ONLY (1<<3).
|
|
2132
|
+
const PARAM_CONSTRUCT_MASK = 0x4 | 0x8;
|
|
2133
|
+
const constructPropertyNames = properties
|
|
2134
|
+
.filter((p) => typeof p.flags === 'number' && (p.flags & PARAM_CONSTRUCT_MASK) !== 0)
|
|
2135
|
+
.map((p) => p.name)
|
|
2136
|
+
.filter((n) => typeof n === 'string');
|
|
2137
|
+
registeredClasses.set(klass, {
|
|
2138
|
+
typeHandle,
|
|
2139
|
+
children: children.length > 0 ? children : undefined,
|
|
2140
|
+
internalChildren: internalChildren.length > 0 ? internalChildren : undefined,
|
|
2141
|
+
// CONSTRUCT-property names, for the construct-property flush (below).
|
|
2142
|
+
constructPropertyNames: constructPropertyNames.length > 0 ? constructPropertyNames : undefined,
|
|
2143
|
+
});
|
|
2144
|
+
// Reverse index for runCtorForCObject: the engine identifies a C-created instance
|
|
2145
|
+
// by its GType name (= gtypeName, what native.registerClass registered).
|
|
2146
|
+
classesByGType.set(gtypeName, klass);
|
|
2147
|
+
return klass;
|
|
2159
2148
|
}
|
|
2160
2149
|
|
|
2161
2150
|
// Merge convenience flag names UNDER an introspected enum: the introspected
|
|
@@ -2165,8 +2154,8 @@ function registerClass(metaOrClass, maybeClass) {
|
|
|
2165
2154
|
// (e.g. READWRITE). Returns the convenience table verbatim if introspection
|
|
2166
2155
|
// yielded no usable enum.
|
|
2167
2156
|
function mergeFlags(introspected, convenience) {
|
|
2168
|
-
|
|
2169
|
-
|
|
2157
|
+
if (introspected === null || typeof introspected !== 'object') return convenience;
|
|
2158
|
+
return Object.freeze({ ...convenience, ...introspected });
|
|
2170
2159
|
}
|
|
2171
2160
|
|
|
2172
2161
|
// GObject.AccumulatorType — gjs's fake enum for signal accumulators (GObject.js,
|
|
@@ -2181,32 +2170,32 @@ const AccumulatorType = Object.freeze({ NONE: 0, FIRST_WINS: 1, TRUE_HANDLED: 2
|
|
|
2181
2170
|
// native.disconnectSignal. Returns the count of handlers matched, exactly as gjs's
|
|
2182
2171
|
// g_signal_handlers_*_matched-based helpers do.
|
|
2183
2172
|
function signalHandlersBlockByFunc(instance, fn) {
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2173
|
+
const handle = unwrapArg(instance);
|
|
2174
|
+
const ids = handlerIdsForFunc(handle, fn);
|
|
2175
|
+
for (const id of ids) native.callFunction('GObject', 'signal_handler_block', [handle, id]);
|
|
2176
|
+
return ids.length;
|
|
2188
2177
|
}
|
|
2189
2178
|
function signalHandlersUnblockByFunc(instance, fn) {
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2179
|
+
const handle = unwrapArg(instance);
|
|
2180
|
+
const ids = handlerIdsForFunc(handle, fn);
|
|
2181
|
+
for (const id of ids) native.callFunction('GObject', 'signal_handler_unblock', [handle, id]);
|
|
2182
|
+
return ids.length;
|
|
2194
2183
|
}
|
|
2195
2184
|
function signalHandlersDisconnectByFunc(instance, fn) {
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2185
|
+
const handle = unwrapArg(instance);
|
|
2186
|
+
const ids = handlerIdsForFunc(handle, fn);
|
|
2187
|
+
for (const id of ids) {
|
|
2188
|
+
native.disconnectSignal(handle, id);
|
|
2189
|
+
forgetSignalHandlerId(handle, id);
|
|
2190
|
+
}
|
|
2191
|
+
return ids.length;
|
|
2203
2192
|
}
|
|
2204
2193
|
// Matches gjs's non-introspectable guard (GObject.js) — there is no func→data map.
|
|
2205
2194
|
function signalHandlersDisconnectByData() {
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2195
|
+
throw new Error(
|
|
2196
|
+
'GObject.signal_handlers_disconnect_by_data() is not introspectable. Use ' +
|
|
2197
|
+
'GObject.signal_handlers_disconnect_by_func() instead.',
|
|
2198
|
+
);
|
|
2210
2199
|
}
|
|
2211
2200
|
|
|
2212
2201
|
// GObject.signal_connect / signal_connect_after / signal_emit_by_name — gjs's
|
|
@@ -2214,13 +2203,13 @@ function signalHandlersDisconnectByData() {
|
|
|
2214
2203
|
// workaround for classes that shadow those names with their own methods). The
|
|
2215
2204
|
// instance is the L1 wrapper, whose .connect records the id in the by-func registry.
|
|
2216
2205
|
function signalConnect(object, name, handler) {
|
|
2217
|
-
|
|
2206
|
+
return object.connect(name, handler);
|
|
2218
2207
|
}
|
|
2219
2208
|
function signalConnectAfter(object, name, handler) {
|
|
2220
|
-
|
|
2209
|
+
return object.connect_after(name, handler);
|
|
2221
2210
|
}
|
|
2222
2211
|
function signalEmitByName(object, ...nameAndArgs) {
|
|
2223
|
-
|
|
2212
|
+
return object.emit(...nameAndArgs);
|
|
2224
2213
|
}
|
|
2225
2214
|
|
|
2226
2215
|
// Overlay the GJS `GObject` runtime statics on top of the introspected GObject
|
|
@@ -2234,19 +2223,19 @@ function signalEmitByName(object, ...nameAndArgs) {
|
|
|
2234
2223
|
// `GObject.Object` etc. keep resolving from introspection (with .new added in
|
|
2235
2224
|
// makeClass). Merged enums are cached so identity is stable.
|
|
2236
2225
|
const OVERLAY_NAMES = new Set([
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2226
|
+
'registerClass',
|
|
2227
|
+
'ParamSpec',
|
|
2228
|
+
'ParamFlags',
|
|
2229
|
+
'SignalFlags',
|
|
2230
|
+
'Value',
|
|
2231
|
+
'AccumulatorType',
|
|
2232
|
+
'signal_handlers_block_by_func',
|
|
2233
|
+
'signal_handlers_unblock_by_func',
|
|
2234
|
+
'signal_handlers_disconnect_by_func',
|
|
2235
|
+
'signal_handlers_disconnect_by_data',
|
|
2236
|
+
'signal_connect',
|
|
2237
|
+
'signal_connect_after',
|
|
2238
|
+
'signal_emit_by_name',
|
|
2250
2239
|
]);
|
|
2251
2240
|
|
|
2252
2241
|
// The fundamental GObject.TYPE_* constants. GJS defines these in its GObject
|
|
@@ -2263,109 +2252,109 @@ const OVERLAY_NAMES = new Set([
|
|
|
2263
2252
|
// that libgobject does not register on a plain node-gi host, so it has no correct
|
|
2264
2253
|
// value here.)
|
|
2265
2254
|
const GTYPE_CONSTANT_NAMES = {
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2255
|
+
TYPE_NONE: 'void',
|
|
2256
|
+
TYPE_INTERFACE: 'GInterface',
|
|
2257
|
+
TYPE_CHAR: 'gchar',
|
|
2258
|
+
TYPE_UCHAR: 'guchar',
|
|
2259
|
+
TYPE_BOOLEAN: 'gboolean',
|
|
2260
|
+
TYPE_INT: 'gint',
|
|
2261
|
+
TYPE_UINT: 'guint',
|
|
2262
|
+
TYPE_LONG: 'glong',
|
|
2263
|
+
TYPE_ULONG: 'gulong',
|
|
2264
|
+
TYPE_INT64: 'gint64',
|
|
2265
|
+
TYPE_UINT64: 'guint64',
|
|
2266
|
+
TYPE_ENUM: 'GEnum',
|
|
2267
|
+
TYPE_FLAGS: 'GFlags',
|
|
2268
|
+
TYPE_FLOAT: 'gfloat',
|
|
2269
|
+
TYPE_DOUBLE: 'gdouble',
|
|
2270
|
+
TYPE_STRING: 'gchararray',
|
|
2271
|
+
TYPE_POINTER: 'gpointer',
|
|
2272
|
+
TYPE_BOXED: 'GBoxed',
|
|
2273
|
+
TYPE_PARAM: 'GParam',
|
|
2274
|
+
TYPE_OBJECT: 'GObject',
|
|
2275
|
+
TYPE_VARIANT: 'GVariant',
|
|
2276
|
+
TYPE_GTYPE: 'GType',
|
|
2277
|
+
TYPE_UNICHAR: 'gint',
|
|
2289
2278
|
};
|
|
2290
2279
|
|
|
2291
2280
|
function isGObjectOverlayName(prop) {
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2281
|
+
return (
|
|
2282
|
+
typeof prop === 'string' &&
|
|
2283
|
+
(OVERLAY_NAMES.has(prop) || Object.prototype.hasOwnProperty.call(GTYPE_CONSTANT_NAMES, prop))
|
|
2284
|
+
);
|
|
2296
2285
|
}
|
|
2297
2286
|
|
|
2298
2287
|
function decorateGObjectNamespace(baseNs) {
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2288
|
+
const cache = new Map();
|
|
2289
|
+
const resolve = (prop) => {
|
|
2290
|
+
if (cache.has(prop)) return cache.get(prop);
|
|
2291
|
+
let value;
|
|
2292
|
+
if (prop === 'registerClass') value = registerClass;
|
|
2293
|
+
else if (prop === 'ParamSpec') value = ParamSpec;
|
|
2294
|
+
else if (prop === 'ParamFlags') value = mergeFlags(baseNs.ParamFlags, ParamFlags);
|
|
2295
|
+
else if (prop === 'SignalFlags') value = mergeFlags(baseNs.SignalFlags, SignalFlags);
|
|
2296
|
+
else if (prop === 'Value') value = valueClass;
|
|
2297
|
+
else if (prop === 'AccumulatorType') value = AccumulatorType;
|
|
2298
|
+
else if (prop === 'signal_handlers_block_by_func') value = signalHandlersBlockByFunc;
|
|
2299
|
+
else if (prop === 'signal_handlers_unblock_by_func') value = signalHandlersUnblockByFunc;
|
|
2300
|
+
else if (prop === 'signal_handlers_disconnect_by_func') value = signalHandlersDisconnectByFunc;
|
|
2301
|
+
else if (prop === 'signal_handlers_disconnect_by_data') value = signalHandlersDisconnectByData;
|
|
2302
|
+
else if (prop === 'signal_connect') value = signalConnect;
|
|
2303
|
+
else if (prop === 'signal_connect_after') value = signalConnectAfter;
|
|
2304
|
+
else if (prop === 'signal_emit_by_name') value = signalEmitByName;
|
|
2305
|
+
else if (Object.prototype.hasOwnProperty.call(GTYPE_CONSTANT_NAMES, prop)) {
|
|
2306
|
+
// Resolve the real GType from libgobject via the introspected type_from_name
|
|
2307
|
+
// (0 / unregistered → null, matching object.cc's GType marshalling).
|
|
2308
|
+
value = baseNs.type_from_name(GTYPE_CONSTANT_NAMES[prop]);
|
|
2309
|
+
}
|
|
2310
|
+
cache.set(prop, value);
|
|
2311
|
+
return value;
|
|
2312
|
+
};
|
|
2313
|
+
return new Proxy(baseNs, {
|
|
2314
|
+
get(t, prop) {
|
|
2315
|
+
return isGObjectOverlayName(prop) ? resolve(prop) : t[prop];
|
|
2316
|
+
},
|
|
2317
|
+
has(t, prop) {
|
|
2318
|
+
return isGObjectOverlayName(prop) || prop in t;
|
|
2319
|
+
},
|
|
2320
|
+
});
|
|
2332
2321
|
}
|
|
2333
2322
|
|
|
2334
2323
|
function createNamespace(namespace) {
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2324
|
+
const cache = new Map();
|
|
2325
|
+
return new Proxy(Object.create(null), {
|
|
2326
|
+
get(_t, prop) {
|
|
2327
|
+
if (typeof prop !== 'string' || RESERVED.has(prop)) return undefined;
|
|
2328
|
+
if (cache.has(prop)) return cache.get(prop);
|
|
2329
|
+
const info = native.findInfo(namespace, prop);
|
|
2330
|
+
let value;
|
|
2331
|
+
if (info === null) {
|
|
2332
|
+
value = undefined;
|
|
2333
|
+
} else if (info.kind === 'function') {
|
|
2334
|
+
value = (...args) => wrapReturn(native.callFunction(namespace, prop, unwrapArgs(args)));
|
|
2335
|
+
} else if (info.kind === 'object' || info.kind === 'interface') {
|
|
2336
|
+
// Interfaces are not constructible (`new` throws via newObject) but
|
|
2337
|
+
// carry static/constructor methods, e.g. Gio.File.new_for_path.
|
|
2338
|
+
value = makeClass(namespace, prop);
|
|
2339
|
+
} else if (info.kind === 'enum' || info.kind === 'flags') {
|
|
2340
|
+
value = makeEnum(namespace, prop);
|
|
2341
|
+
} else if (info.kind === 'constant') {
|
|
2342
|
+
value = native.getConstantValue(namespace, prop);
|
|
2343
|
+
} else if (info.kind === 'struct' || info.kind === 'union') {
|
|
2344
|
+
// Boxed/struct types: static/constructor methods + boxed instances
|
|
2345
|
+
// (GLib.MainLoop, …). Field access lands with the broader structs drop.
|
|
2346
|
+
value = makeStruct(namespace, prop);
|
|
2347
|
+
} else {
|
|
2348
|
+
// interface / callback: surfaced in a later drop.
|
|
2349
|
+
value = undefined;
|
|
2350
|
+
}
|
|
2351
|
+
cache.set(prop, value);
|
|
2352
|
+
return value;
|
|
2353
|
+
},
|
|
2354
|
+
has(_t, prop) {
|
|
2355
|
+
return typeof prop === 'string' && native.findInfo(namespace, prop) !== null;
|
|
2356
|
+
},
|
|
2357
|
+
});
|
|
2369
2358
|
}
|
|
2370
2359
|
|
|
2371
2360
|
const namespaceCache = new Map();
|
|
@@ -2383,66 +2372,66 @@ let loopAttached = false;
|
|
|
2383
2372
|
* @returns {Record<string, unknown>}
|
|
2384
2373
|
*/
|
|
2385
2374
|
export function requireGi(namespace, version) {
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2375
|
+
native.requireNamespace(namespace, version);
|
|
2376
|
+
// Attach the libuv↔GLib integration once. On Node this arms BOTH directions:
|
|
2377
|
+
// the uv-in-GLib bridge (a blocking GLib.MainLoop.run / Gio.Application.run
|
|
2378
|
+
// keeps Node's event loop alive) AND the uv-driven auto-pump (pending GLib
|
|
2379
|
+
// sources — Gio async completions, GLib timeouts/idles, DBus — dispatch from
|
|
2380
|
+
// Node's own loop, so async gi:// code works with NO explicit mainloop). This
|
|
2381
|
+
// is Node-only: Bun/Deno have no usable libuv (Deno exports no libuv symbols;
|
|
2382
|
+
// Bun panics on uv_backend_fd). There, GLib async is co-pumped from the runtime
|
|
2383
|
+
// loop via the portable GLib-iteration pump instead (see startMainContextPump /
|
|
2384
|
+
// runAsync).
|
|
2385
|
+
if (!loopAttached) {
|
|
2386
|
+
if (native.isNodeRuntime) {
|
|
2387
|
+
native.startMainLoop();
|
|
2388
|
+
// Bootstrap kick for the auto-pump: with ONLY unref'd handles active,
|
|
2389
|
+
// uv_run exits without running the pump's prepare/check phase at all, so
|
|
2390
|
+
// an otherwise-empty loop would never arm GLib's timer/fd wake-ups (a
|
|
2391
|
+
// pending top-level await on a GLib timeout would exit-13 unsettled).
|
|
2392
|
+
// 'beforeExit' fires exactly when the loop runs empty: drain what is
|
|
2393
|
+
// ready and re-arm — if GLib still has scheduled work, the armed (ref'd)
|
|
2394
|
+
// uv timer revives the loop; otherwise the process exits normally.
|
|
2395
|
+
runtimeProcess.on('beforeExit', () => native.pumpKick());
|
|
2396
|
+
} else if (native.RUNTIME !== 'gjs') {
|
|
2397
|
+
// Bun/Deno: no libuv to hook, so the portable timer pump IS the auto-pump.
|
|
2398
|
+
// Armed HERE, not left to the caller, so that `bun bundle.mjs` /
|
|
2399
|
+
// `deno run bundle.mjs` behaves like `node bundle.mjs` and `gjs -m`:
|
|
2400
|
+
// GLib sources dispatch with no explicit loop anywhere, and the pump's
|
|
2401
|
+
// keep-alive accounting decides when the process may exit. Leaving it
|
|
2402
|
+
// opt-in is what made the same bundle exit 0 MID-suite on bun/deno while
|
|
2403
|
+
// node ran it to completion.
|
|
2404
|
+
startMainContextPump(); // permanent +1 — the auto-pump is never disposed
|
|
2405
|
+
// Tag that hold as the AUTO pump's: pumpBeat dispatches for it only while
|
|
2406
|
+
// a JS-armed GI callback is outstanding, so a finished program produces
|
|
2407
|
+
// no teardown churn (the deno #47 window) — explicit callers keep
|
|
2408
|
+
// unconditional dispatch.
|
|
2409
|
+
pumpAutoRefs++;
|
|
2410
|
+
// The portable analogue of the Node pump's beforeExit kick. The runtime's
|
|
2411
|
+
// loop just ran empty, which is exactly the moment an unref'd pump would
|
|
2412
|
+
// let the process die with GLib work still outstanding: run one beat —
|
|
2413
|
+
// if the program still owns GLib work it drains + re-refs (reviving the
|
|
2414
|
+
// loop); if not, the beat stays silent and the process exits.
|
|
2415
|
+
runtimeProcess.on('beforeExit', pumpBeat);
|
|
2416
|
+
}
|
|
2417
|
+
loopAttached = true;
|
|
2418
|
+
}
|
|
2419
|
+
const key = version ? `${namespace}@${version}` : namespace;
|
|
2420
|
+
let ns = namespaceCache.get(key);
|
|
2421
|
+
if (ns === undefined) {
|
|
2422
|
+
ns = createNamespace(namespace);
|
|
2423
|
+
// The GObject namespace also carries the GJS runtime statics (registerClass +
|
|
2424
|
+
// ParamSpec/ParamFlags/SignalFlags) layered over its introspected members.
|
|
2425
|
+
if (namespace === 'GObject') ns = decorateGObjectNamespace(ns);
|
|
2426
|
+
// The GLib namespace carries the GJS-shaped GLib.Variant ergonomics
|
|
2427
|
+
// (new GLib.Variant(sig, value) + deepUnpack/unpack/recursiveUnpack) and the
|
|
2428
|
+
// GLib.Error class.
|
|
2429
|
+
else if (namespace === 'GLib') ns = decorateGLibNamespace(ns);
|
|
2430
|
+
// The Gio namespace carries Gio._promisify (async → Promise).
|
|
2431
|
+
else if (namespace === 'Gio') ns = decorateGioNamespace(ns);
|
|
2432
|
+
namespaceCache.set(key, ns);
|
|
2427
2433
|
}
|
|
2428
|
-
|
|
2429
|
-
}
|
|
2430
|
-
const key = version ? `${namespace}@${version}` : namespace;
|
|
2431
|
-
let ns = namespaceCache.get(key);
|
|
2432
|
-
if (ns === undefined) {
|
|
2433
|
-
ns = createNamespace(namespace);
|
|
2434
|
-
// The GObject namespace also carries the GJS runtime statics (registerClass +
|
|
2435
|
-
// ParamSpec/ParamFlags/SignalFlags) layered over its introspected members.
|
|
2436
|
-
if (namespace === 'GObject') ns = decorateGObjectNamespace(ns);
|
|
2437
|
-
// The GLib namespace carries the GJS-shaped GLib.Variant ergonomics
|
|
2438
|
-
// (new GLib.Variant(sig, value) + deepUnpack/unpack/recursiveUnpack) and the
|
|
2439
|
-
// GLib.Error class.
|
|
2440
|
-
else if (namespace === 'GLib') ns = decorateGLibNamespace(ns);
|
|
2441
|
-
// The Gio namespace carries Gio._promisify (async → Promise).
|
|
2442
|
-
else if (namespace === 'Gio') ns = decorateGioNamespace(ns);
|
|
2443
|
-
namespaceCache.set(key, ns);
|
|
2444
|
-
}
|
|
2445
|
-
return ns;
|
|
2434
|
+
return ns;
|
|
2446
2435
|
}
|
|
2447
2436
|
|
|
2448
2437
|
/**
|
|
@@ -2452,12 +2441,12 @@ export function requireGi(namespace, version) {
|
|
|
2452
2441
|
* @returns {unknown}
|
|
2453
2442
|
*/
|
|
2454
2443
|
export function unwrap(value) {
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2444
|
+
// NOT unwrapArg: the public unwrap only extracts handles — it must never
|
|
2445
|
+
// replace a function with the engine-facing callback shim.
|
|
2446
|
+
if (value !== null && typeof value === 'object' && value[HANDLE] !== undefined) {
|
|
2447
|
+
return value[HANDLE];
|
|
2448
|
+
}
|
|
2449
|
+
return value;
|
|
2461
2450
|
}
|
|
2462
2451
|
|
|
2463
2452
|
export default requireGi;
|