@gtkx/runtime 1.0.0-rc.3 → 1.0.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 +8 -9
- package/dist/application-class.d.ts +32 -0
- package/dist/application-class.d.ts.map +1 -0
- package/dist/application-class.js +68 -0
- package/dist/application-class.js.map +1 -0
- package/dist/arg.d.ts +9 -3
- package/dist/arg.d.ts.map +1 -1
- package/dist/arg.js +4 -3
- package/dist/arg.js.map +1 -1
- package/dist/bind.d.ts +11 -0
- package/dist/bind.d.ts.map +1 -1
- package/dist/bind.js +11 -0
- package/dist/bind.js.map +1 -1
- package/dist/callback.d.ts +2 -1
- package/dist/callback.d.ts.map +1 -1
- package/dist/callback.js +119 -21
- package/dist/callback.js.map +1 -1
- package/dist/closure.d.ts +26 -0
- package/dist/closure.d.ts.map +1 -0
- package/dist/closure.js +99 -0
- package/dist/closure.js.map +1 -0
- package/dist/descriptors.d.ts +105 -6
- package/dist/descriptors.d.ts.map +1 -1
- package/dist/descriptors.js +56 -12
- package/dist/descriptors.js.map +1 -1
- package/dist/error.d.ts +7 -0
- package/dist/error.d.ts.map +1 -1
- package/dist/error.js +1 -1
- package/dist/error.js.map +1 -1
- package/dist/fn.d.ts +24 -3
- package/dist/fn.d.ts.map +1 -1
- package/dist/fn.js +70 -33
- package/dist/fn.js.map +1 -1
- package/dist/index.d.ts +29 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +26 -4
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +1 -0
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +1 -0
- package/dist/internal.js.map +1 -1
- package/dist/library.d.ts +2 -1
- package/dist/library.d.ts.map +1 -1
- package/dist/library.js +2 -1
- package/dist/library.js.map +1 -1
- package/dist/lifecycle.d.ts +52 -10
- package/dist/lifecycle.d.ts.map +1 -1
- package/dist/lifecycle.js +62 -18
- package/dist/lifecycle.js.map +1 -1
- package/dist/listeners.d.ts +6 -0
- package/dist/listeners.d.ts.map +1 -1
- package/dist/listeners.js +3 -1
- package/dist/listeners.js.map +1 -1
- package/dist/mixin.d.ts +16 -2
- package/dist/mixin.d.ts.map +1 -1
- package/dist/mixin.js +18 -6
- package/dist/mixin.js.map +1 -1
- package/dist/native-value.d.ts +8 -0
- package/dist/native-value.d.ts.map +1 -1
- package/dist/native-value.js +12 -3
- package/dist/native-value.js.map +1 -1
- package/dist/object.d.ts +34 -6
- package/dist/object.d.ts.map +1 -1
- package/dist/object.js +89 -25
- package/dist/object.js.map +1 -1
- package/dist/param-spec.d.ts +11 -0
- package/dist/param-spec.d.ts.map +1 -0
- package/dist/param-spec.js +149 -0
- package/dist/param-spec.js.map +1 -0
- package/dist/promisify.d.ts +3 -5
- package/dist/promisify.d.ts.map +1 -1
- package/dist/promisify.js +4 -3
- package/dist/promisify.js.map +1 -1
- package/dist/properties.d.ts +39 -7
- package/dist/properties.d.ts.map +1 -1
- package/dist/properties.js +305 -42
- package/dist/properties.js.map +1 -1
- package/dist/register-class.d.ts +119 -14
- package/dist/register-class.d.ts.map +1 -1
- package/dist/register-class.js +109 -80
- package/dist/register-class.js.map +1 -1
- package/dist/registry.d.ts +81 -17
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +72 -26
- package/dist/registry.js.map +1 -1
- package/dist/signal.d.ts +22 -3
- package/dist/signal.d.ts.map +1 -1
- package/dist/signal.js +64 -8
- package/dist/signal.js.map +1 -1
- package/dist/t.d.ts +44 -0
- package/dist/t.d.ts.map +1 -1
- package/dist/t.js.map +1 -1
- package/dist/type.d.ts +2 -2
- package/dist/type.d.ts.map +1 -1
- package/dist/type.js +2 -0
- package/dist/type.js.map +1 -1
- package/dist/value.d.ts +10 -10
- package/dist/value.d.ts.map +1 -1
- package/dist/value.js +44 -22
- package/dist/value.js.map +1 -1
- package/dist/vfunc-call.d.ts +37 -0
- package/dist/vfunc-call.d.ts.map +1 -0
- package/dist/vfunc-call.js +168 -0
- package/dist/vfunc-call.js.map +1 -0
- package/dist/vfunc-seeds.d.ts +12 -0
- package/dist/vfunc-seeds.d.ts.map +1 -0
- package/dist/vfunc-seeds.js +19 -0
- package/dist/vfunc-seeds.js.map +1 -0
- package/dist/vfunc.d.ts +8 -0
- package/dist/vfunc.d.ts.map +1 -0
- package/dist/vfunc.js +23 -0
- package/dist/vfunc.js.map +1 -0
- package/package.json +4 -4
- package/src/application-class.ts +116 -0
- package/src/arg.ts +11 -5
- package/src/bind.ts +11 -0
- package/src/callback.ts +163 -23
- package/src/closure.ts +136 -0
- package/src/descriptors.ts +137 -41
- package/src/error.ts +8 -1
- package/src/fn.ts +104 -39
- package/src/index.ts +36 -7
- package/src/internal.ts +1 -0
- package/src/library.ts +2 -1
- package/src/lifecycle.ts +99 -22
- package/src/listeners.ts +9 -1
- package/src/mixin.ts +27 -8
- package/src/native-value.ts +13 -3
- package/src/object.ts +128 -34
- package/src/param-spec.ts +235 -0
- package/src/promisify.ts +8 -8
- package/src/properties.ts +459 -45
- package/src/register-class.ts +329 -114
- package/src/registry.ts +150 -38
- package/src/signal.ts +101 -17
- package/src/t.ts +44 -0
- package/src/type.ts +3 -2
- package/src/value.ts +57 -19
- package/src/vfunc-call.ts +243 -0
- package/src/vfunc-seeds.ts +36 -0
- package/src/vfunc.ts +31 -0
package/src/registry.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { AnyClass } from "@gtkx/utils";
|
|
2
1
|
import {
|
|
3
2
|
type ExternalObject,
|
|
4
3
|
getType,
|
|
@@ -7,6 +6,7 @@ import {
|
|
|
7
6
|
type RegisterClassVfunc as NativeRegisterClassVfunc,
|
|
8
7
|
setWrapper,
|
|
9
8
|
} from "@gtkx/native";
|
|
9
|
+
import { type AnyClass, walkClassChain } from "@gtkx/utils";
|
|
10
10
|
import type { Mixin, MixinReceiver } from "./mixin.js";
|
|
11
11
|
import { TYPE_INVALID, type TypedClass, typeInterfaces, typeIsA, typeName, typeParent } from "./type.js";
|
|
12
12
|
|
|
@@ -17,38 +17,104 @@ import { TYPE_INVALID, type TypedClass, typeInterfaces, typeIsA, typeName, typeP
|
|
|
17
17
|
type StaticBase<C, K extends PropertyKey = "new"> = Omit<C, K> &
|
|
18
18
|
(C extends new (...args: infer A) => infer R ? new (...args: A) => R : never);
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
/** One overridable vtable slot: where it sits in the vtable struct and how it is marshalled. */
|
|
21
|
+
type VfuncDescriptor = {
|
|
22
|
+
/** GIR name of the type struct holding the slot, without its namespace, such as `WidgetClass`. */
|
|
22
23
|
className: string;
|
|
24
|
+
/** Name of the slot's field in that struct. */
|
|
23
25
|
vfuncName: string;
|
|
26
|
+
/** Byte offset of the slot within the struct. */
|
|
24
27
|
byteOffset: number;
|
|
25
|
-
|
|
28
|
+
/**
|
|
29
|
+
* Byte size of an interface's vtable struct, used to bounds-check `VfuncDescriptor.byteOffset`.
|
|
30
|
+
* A class struct is bounds-checked against the size `g_type_query` reports for it, so a slot in
|
|
31
|
+
* one carries no size of its own.
|
|
32
|
+
*/
|
|
33
|
+
vtableSize?: number;
|
|
34
|
+
/** Descriptor for each argument the slot receives, starting with the instance. */
|
|
26
35
|
argDescriptors: NativeRegisterClassVfunc["argDescriptors"];
|
|
36
|
+
/** Descriptor for the value the slot returns. */
|
|
27
37
|
returnDescriptor: NativeRegisterClassVfunc["returnDescriptor"];
|
|
38
|
+
/**
|
|
39
|
+
* The slot takes a trailing `GError**` that `VfuncDescriptor.argDescriptors` leaves out, the
|
|
40
|
+
* way GIR leaves it out of a callable's parameters. A call through the slot has to append it
|
|
41
|
+
* or it passes one argument fewer than the implementation reads.
|
|
42
|
+
*/
|
|
43
|
+
canThrow?: boolean;
|
|
28
44
|
};
|
|
29
45
|
|
|
30
|
-
|
|
46
|
+
/**
|
|
47
|
+
* The vtable slots a wrapper class or interface exposes, keyed by the JavaScript method name that
|
|
48
|
+
* overrides each one.
|
|
49
|
+
*/
|
|
50
|
+
type VfuncRegistry = Record<string, VfuncDescriptor>;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* How one property an interface declares reaches the vtable, given as the interface's own accessor
|
|
54
|
+
* members. A direction introspection does not route through a vtable slot is left out, and the
|
|
55
|
+
* property owns that direction's state itself.
|
|
56
|
+
*/
|
|
57
|
+
type InterfaceProperty = {
|
|
58
|
+
/** Member reading the slot the property's value comes from, such as `getEnabled`. */
|
|
59
|
+
getter?: string;
|
|
60
|
+
/** Member writing the slot the property's value goes to, such as `setActionName`. */
|
|
61
|
+
setter?: string;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* What an interface's vtable struct looks like, for the classes that adopt the interface.
|
|
66
|
+
* `g_type_query` reports no size for an interface, so each slot's generated metadata carries the
|
|
67
|
+
* struct's byte size to bounds-check the slot's offset; an interface introspection describes no
|
|
68
|
+
* vtable for simply contributes no slots.
|
|
69
|
+
*/
|
|
70
|
+
type InterfaceLayout = {
|
|
71
|
+
/** The slots the struct declares, keyed by the JavaScript method name that fills each one. */
|
|
72
|
+
vfuncs?: VfuncRegistry;
|
|
73
|
+
/**
|
|
74
|
+
* The properties a vtable slot backs, keyed by canonical property name, so a class adopting the
|
|
75
|
+
* interface answers `g_object_get` and `g_object_set` with what the slot holds.
|
|
76
|
+
*/
|
|
77
|
+
properties?: Record<string, InterfaceProperty>;
|
|
78
|
+
};
|
|
31
79
|
|
|
32
80
|
const classRegistry: Map<bigint, AnyClass> = new Map();
|
|
33
81
|
const interfaceMixinRegistry: Map<bigint, Mixin> = new Map();
|
|
34
82
|
const composedClassRegistry: Map<bigint, AnyClass> = new Map();
|
|
35
83
|
const handleMap: WeakMap<object, ExternalObject<Handle>> = new WeakMap();
|
|
36
84
|
const vfuncRegistry: WeakMap<object, VfuncRegistry> = new WeakMap();
|
|
37
|
-
const
|
|
85
|
+
const interfaceLayoutRegistry: Map<bigint, InterfaceLayout> = new Map();
|
|
86
|
+
const wrapperClasses: WeakSet<AnyClass> = new WeakSet();
|
|
87
|
+
const derivedClasses: WeakSet<AnyClass> = new WeakSet();
|
|
38
88
|
|
|
39
89
|
function setClassType(cls: AnyClass, type: bigint): void {
|
|
40
90
|
(cls.prototype as { [K in keyof TypedClass]: TypedClass[K] }).__type__ = type;
|
|
41
91
|
}
|
|
42
92
|
|
|
43
|
-
|
|
44
|
-
|
|
93
|
+
/**
|
|
94
|
+
* Returns the GType a class was registered under, or the invalid type when it carries none. The tag is
|
|
95
|
+
* read off the class's own prototype, so a subclass that never went through `registerClass` reports the
|
|
96
|
+
* invalid type rather than inheriting the one its parent was registered with.
|
|
97
|
+
*/
|
|
98
|
+
function getClassType(cls: AnyClass | undefined): bigint {
|
|
99
|
+
const proto: object | undefined = cls?.prototype;
|
|
100
|
+
|
|
101
|
+
if (proto === undefined || !Object.hasOwn(proto, "__type__")) {
|
|
102
|
+
return TYPE_INVALID;
|
|
103
|
+
}
|
|
45
104
|
|
|
46
|
-
return
|
|
105
|
+
return (proto as TypedClass).__type__;
|
|
47
106
|
}
|
|
48
107
|
|
|
49
|
-
/**
|
|
108
|
+
/**
|
|
109
|
+
* Returns the GType the given instance's handle carries, or the invalid type when it has no handle.
|
|
110
|
+
* This is the object's own type rather than the type of the class it was wrapped as, and the two differ
|
|
111
|
+
* for an object GTK created itself, such as the row widget inside a `Gtk.ListView`, which GTKX wraps as
|
|
112
|
+
* the nearest registered ancestor.
|
|
113
|
+
*/
|
|
50
114
|
function getInstanceType(instance: object): bigint {
|
|
51
|
-
|
|
115
|
+
const handle = handleMap.get(instance);
|
|
116
|
+
|
|
117
|
+
return handle === undefined ? TYPE_INVALID : getType(handle);
|
|
52
118
|
}
|
|
53
119
|
|
|
54
120
|
function registerClassType(cls: AnyClass, type: bigint): void {
|
|
@@ -65,25 +131,53 @@ function registerClassType(cls: AnyClass, type: bigint): void {
|
|
|
65
131
|
* installing a registry of virtual functions.
|
|
66
132
|
* @param cls Wrapper class to associate with the type.
|
|
67
133
|
* @param type GType the class wraps.
|
|
68
|
-
* @param vfuncs
|
|
134
|
+
* @param vfuncs Vtable slots the class exposes, so `registerClass` can bind the ones a subclass
|
|
135
|
+
* overrides.
|
|
69
136
|
*/
|
|
70
137
|
function registerWrapperClass(cls: AnyClass, type: bigint, vfuncs?: VfuncRegistry): void {
|
|
71
138
|
registerClassType(cls, type);
|
|
72
139
|
|
|
140
|
+
if (type !== TYPE_INVALID) {
|
|
141
|
+
wrapperClasses.add(cls);
|
|
142
|
+
}
|
|
143
|
+
|
|
73
144
|
if (vfuncs) {
|
|
74
145
|
registerVfuncRegistry(cls, vfuncs);
|
|
75
146
|
}
|
|
76
147
|
}
|
|
77
148
|
|
|
149
|
+
function markDerivedClass(cls: AnyClass): void {
|
|
150
|
+
derivedClasses.add(cls);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function resolveAncestorType(ancestor: AnyClass): bigint | undefined {
|
|
154
|
+
if (derivedClasses.has(ancestor) || !wrapperClasses.has(ancestor)) {
|
|
155
|
+
return undefined;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return getClassType(ancestor);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function resolveWrapperType(instance: object): bigint {
|
|
162
|
+
const cls = instance.constructor as AnyClass | undefined;
|
|
163
|
+
|
|
164
|
+
if (cls === undefined) {
|
|
165
|
+
return TYPE_INVALID;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return walkClassChain(cls, (ancestor) => resolveAncestorType(ancestor)) ?? TYPE_INVALID;
|
|
169
|
+
}
|
|
170
|
+
|
|
78
171
|
/**
|
|
79
172
|
* Registers a GInterface, associating its GType with a mixin used to compose the
|
|
80
|
-
* interface onto wrapper classes and
|
|
173
|
+
* interface onto wrapper classes and, when introspection describes its vtable, that layout.
|
|
81
174
|
* @param cls Class carrying the interface's GType tag.
|
|
82
175
|
* @param type GType of the interface.
|
|
83
176
|
* @param mixin Mixin that applies the interface to a wrapper class.
|
|
84
|
-
* @param
|
|
177
|
+
* @param layout The interface's vtable struct, so `registerClass` can bind the slots an
|
|
178
|
+
* implementing class overrides and take over the ones it leaves alone.
|
|
85
179
|
*/
|
|
86
|
-
function registerInterface(cls: AnyClass, type: bigint, mixin: Mixin,
|
|
180
|
+
function registerInterface(cls: AnyClass, type: bigint, mixin: Mixin, layout?: InterfaceLayout): void {
|
|
87
181
|
if (type === TYPE_INVALID) {
|
|
88
182
|
return;
|
|
89
183
|
}
|
|
@@ -91,16 +185,17 @@ function registerInterface(cls: AnyClass, type: bigint, mixin: Mixin, vfuncs?: V
|
|
|
91
185
|
setClassType(cls, type);
|
|
92
186
|
interfaceMixinRegistry.set(type, mixin);
|
|
93
187
|
|
|
94
|
-
if (
|
|
95
|
-
|
|
188
|
+
if (layout) {
|
|
189
|
+
interfaceLayoutRegistry.set(type, layout);
|
|
96
190
|
}
|
|
97
191
|
}
|
|
98
192
|
|
|
99
193
|
/**
|
|
100
194
|
* Wraps a native handle in a JS wrapper instance. With no class, resolves and
|
|
101
|
-
* reuses the wrapper for the handle's runtime GType (composing interface mixins)
|
|
102
|
-
*
|
|
103
|
-
*
|
|
195
|
+
* reuses the wrapper for the handle's runtime GType (composing interface mixins),
|
|
196
|
+
* and hands back an instance that already carries a handle unchanged; with an
|
|
197
|
+
* explicit class, creates a bare instance backed by the handle. Returns null for
|
|
198
|
+
* a null or undefined handle.
|
|
104
199
|
* @param handle Native handle to wrap.
|
|
105
200
|
* @param cls Wrapper class to instantiate, or omitted to resolve it from the runtime type.
|
|
106
201
|
*/
|
|
@@ -160,12 +255,16 @@ function resolveWrapperClass(type: bigint): AnyClass | null {
|
|
|
160
255
|
return null;
|
|
161
256
|
}
|
|
162
257
|
|
|
258
|
+
function getInterfaceMixin(type: bigint): Mixin | undefined {
|
|
259
|
+
return interfaceMixinRegistry.get(type);
|
|
260
|
+
}
|
|
261
|
+
|
|
163
262
|
function applyInterfaceMixin(cls: AnyClass, type: bigint, baseType: bigint, applied: Set<bigint>): AnyClass {
|
|
164
263
|
if (applied.has(type) || typeIsA(baseType, type)) {
|
|
165
264
|
return cls;
|
|
166
265
|
}
|
|
167
266
|
|
|
168
|
-
const mixin =
|
|
267
|
+
const mixin = getInterfaceMixin(type);
|
|
169
268
|
|
|
170
269
|
if (mixin === undefined) {
|
|
171
270
|
return cls;
|
|
@@ -214,12 +313,25 @@ function resolveComposedClass(runtimeType: bigint): AnyClass | null {
|
|
|
214
313
|
}
|
|
215
314
|
|
|
216
315
|
setClassType(composed, runtimeType);
|
|
316
|
+
wrapperClasses.add(composed);
|
|
217
317
|
composedClassRegistry.set(runtimeType, composed);
|
|
218
318
|
|
|
219
319
|
return composed;
|
|
220
320
|
}
|
|
221
321
|
|
|
322
|
+
function wrapObject(value: unknown): object | null {
|
|
323
|
+
if (value == null) {
|
|
324
|
+
return null;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
return getOrCreateWrapper(value as ExternalObject<Handle>);
|
|
328
|
+
}
|
|
329
|
+
|
|
222
330
|
function getOrCreateWrapper(handle: ExternalObject<Handle>): object {
|
|
331
|
+
if (handleMap.has(handle)) {
|
|
332
|
+
return handle;
|
|
333
|
+
}
|
|
334
|
+
|
|
223
335
|
const existing = getWrapper(handle);
|
|
224
336
|
|
|
225
337
|
if (existing) {
|
|
@@ -244,23 +356,21 @@ function getOrCreateWrapper(handle: ExternalObject<Handle>): object {
|
|
|
244
356
|
return instance;
|
|
245
357
|
}
|
|
246
358
|
|
|
359
|
+
function instanceClassName(instance: object): string {
|
|
360
|
+
return (instance as { constructor?: { name?: string } }).constructor?.name ?? "object";
|
|
361
|
+
}
|
|
362
|
+
|
|
247
363
|
/** Returns the native handle bound to a wrapper instance, throwing if none is set. */
|
|
248
364
|
function getHandle(instance: object): ExternalObject<Handle> {
|
|
249
365
|
const handle = handleMap.get(instance);
|
|
250
366
|
|
|
251
367
|
if (handle === undefined) {
|
|
252
|
-
|
|
253
|
-
throw new Error(`No native handle associated with ${name}`);
|
|
368
|
+
throw new Error(`No native handle associated with ${instanceClassName(instance)}`);
|
|
254
369
|
}
|
|
255
370
|
|
|
256
371
|
return handle;
|
|
257
372
|
}
|
|
258
373
|
|
|
259
|
-
/** Returns the native handle bound to an instance, or undefined when there is none or the instance is null. */
|
|
260
|
-
function tryGetHandle(instance: object | null | undefined): ExternalObject<Handle> | undefined {
|
|
261
|
-
return instance == null ? undefined : handleMap.get(instance);
|
|
262
|
-
}
|
|
263
|
-
|
|
264
374
|
/** Associates a native handle with a wrapper instance. */
|
|
265
375
|
function setHandle(instance: object, handle: ExternalObject<Handle>): void {
|
|
266
376
|
handleMap.set(instance, handle);
|
|
@@ -279,21 +389,18 @@ function getVfuncRegistry(cls: object): VfuncRegistry | undefined {
|
|
|
279
389
|
return vfuncRegistry.get(cls);
|
|
280
390
|
}
|
|
281
391
|
|
|
282
|
-
function
|
|
283
|
-
|
|
284
|
-
return;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
interfaceVfuncRegistry.set(type, registry);
|
|
392
|
+
function getInterfaceVfuncRegistry(type: bigint): VfuncRegistry | undefined {
|
|
393
|
+
return interfaceLayoutRegistry.get(type)?.vfuncs;
|
|
288
394
|
}
|
|
289
395
|
|
|
290
|
-
function
|
|
291
|
-
return
|
|
396
|
+
function getInterfaceProperties(type: bigint): Record<string, InterfaceProperty> | undefined {
|
|
397
|
+
return interfaceLayoutRegistry.get(type)?.properties;
|
|
292
398
|
}
|
|
293
399
|
|
|
294
400
|
export {
|
|
295
401
|
getClassType,
|
|
296
402
|
getInstanceType,
|
|
403
|
+
markDerivedClass,
|
|
297
404
|
registerClassType,
|
|
298
405
|
registerWrapperClass,
|
|
299
406
|
registerInterface,
|
|
@@ -301,11 +408,16 @@ export {
|
|
|
301
408
|
getWrapperClass,
|
|
302
409
|
resolveWrapperClass,
|
|
303
410
|
getHandle,
|
|
304
|
-
tryGetHandle,
|
|
305
411
|
setHandle,
|
|
306
412
|
getVfuncRegistry,
|
|
413
|
+
getInterfaceMixin,
|
|
414
|
+
getInterfaceProperties,
|
|
307
415
|
getInterfaceVfuncRegistry,
|
|
416
|
+
instanceClassName,
|
|
417
|
+
registerWrapper,
|
|
418
|
+
resolveWrapperType,
|
|
419
|
+
wrapObject,
|
|
420
|
+
type InterfaceProperty,
|
|
308
421
|
type StaticBase,
|
|
309
422
|
type VfuncDescriptor,
|
|
310
|
-
type VfuncRegistry,
|
|
311
423
|
};
|
package/src/signal.ts
CHANGED
|
@@ -30,15 +30,30 @@ import {
|
|
|
30
30
|
/** Function invoked when a connected GObject signal is emitted. */
|
|
31
31
|
type SignalHandler = (...args: unknown[]) => unknown;
|
|
32
32
|
|
|
33
|
+
/** The marshalling and handler that make up a single signal connection. */
|
|
33
34
|
type SignalConnectSpec = {
|
|
35
|
+
/**
|
|
36
|
+
* Marshalling for the emission, whose `argDescriptors` lead with the emitter and include the
|
|
37
|
+
* closure's user data slot.
|
|
38
|
+
*/
|
|
34
39
|
callback: CallbackDescriptor;
|
|
40
|
+
/** Called on each emission with the signal's own arguments, without the leading emitter. */
|
|
35
41
|
handler: SignalHandler;
|
|
36
|
-
after
|
|
42
|
+
/** When true, run the handler after the class's default handler instead of before it. */
|
|
43
|
+
isAfter: boolean;
|
|
37
44
|
};
|
|
38
45
|
|
|
39
|
-
|
|
46
|
+
/** One argument of a signal emission: how to marshal it, plus the value to marshal. */
|
|
47
|
+
type EmitArg = Arg & {
|
|
48
|
+
/**
|
|
49
|
+
* The value to pass for an input or inout argument, or the caller-allocated storage to fill for
|
|
50
|
+
* a caller-allocated output argument; omitted for a plain output argument.
|
|
51
|
+
*/
|
|
52
|
+
value?: unknown;
|
|
53
|
+
};
|
|
40
54
|
|
|
41
55
|
const connectCache = createBindCache();
|
|
56
|
+
const connectionTable: WeakMap<object, Map<string, Set<number>>> = new WeakMap();
|
|
42
57
|
const gQuarkFromString = bind(LIB, "g_quark_from_string", [stringT("borrowed")], uint32T);
|
|
43
58
|
const gSignalLookup = bind(LIB, "g_signal_lookup", [stringT("borrowed"), biguint64T], uint32T);
|
|
44
59
|
|
|
@@ -49,13 +64,6 @@ const gSignalEmitv = bind(
|
|
|
49
64
|
voidT,
|
|
50
65
|
);
|
|
51
66
|
|
|
52
|
-
const gSignalHandlersBlockMatched = bind(
|
|
53
|
-
LIB,
|
|
54
|
-
"g_signal_handlers_block_matched",
|
|
55
|
-
[objectT("borrowed"), uint32T, uint32T, uint32T, objectT("borrowed"), objectT("borrowed"), objectT("borrowed")],
|
|
56
|
-
uint32T,
|
|
57
|
-
);
|
|
58
|
-
|
|
59
67
|
const gSignalHandlerIsConnected = bind(
|
|
60
68
|
LIB,
|
|
61
69
|
"g_signal_handler_is_connected",
|
|
@@ -63,6 +71,8 @@ const gSignalHandlerIsConnected = bind(
|
|
|
63
71
|
booleanT,
|
|
64
72
|
);
|
|
65
73
|
|
|
74
|
+
const gSignalHandlerDisconnect = bind(LIB, "g_signal_handler_disconnect", [objectT("borrowed"), uint64T], voidT);
|
|
75
|
+
|
|
66
76
|
/** Returns the signal name without its detail suffix (the part after `::`). */
|
|
67
77
|
const getSignalBaseName = (signal: string): string => {
|
|
68
78
|
const detailIndex = signal.indexOf("::");
|
|
@@ -83,6 +93,81 @@ function getSignalDetailQuark(signal: string): number {
|
|
|
83
93
|
const isSignalHandlerConnected = (instance: object, handlerId: number): boolean =>
|
|
84
94
|
gSignalHandlerIsConnected(getHandle(instance), handlerId) as boolean;
|
|
85
95
|
|
|
96
|
+
const trackConnection = (instance: object, signal: string, handlerId: number): void => {
|
|
97
|
+
let bySignal = connectionTable.get(instance);
|
|
98
|
+
|
|
99
|
+
if (!bySignal) {
|
|
100
|
+
bySignal = new Map();
|
|
101
|
+
connectionTable.set(instance, bySignal);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
let handlerIds = bySignal.get(signal);
|
|
105
|
+
|
|
106
|
+
if (!handlerIds) {
|
|
107
|
+
handlerIds = new Set();
|
|
108
|
+
bySignal.set(signal, handlerIds);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
handlerIds.add(handlerId);
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const untrackConnection = (instance: object, handlerId: number): void => {
|
|
115
|
+
const bySignal = connectionTable.get(instance);
|
|
116
|
+
|
|
117
|
+
if (bySignal === undefined) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
for (const [name, handlerIds] of bySignal) {
|
|
122
|
+
handlerIds.delete(handlerId);
|
|
123
|
+
|
|
124
|
+
if (handlerIds.size === 0) {
|
|
125
|
+
bySignal.delete(name);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Disconnects the handler an instance connected under the given id, and forgets the connection.
|
|
132
|
+
* @param instance Emitter the handler was connected to.
|
|
133
|
+
* @param handlerId Id {@link connectSignal} returned for the handler.
|
|
134
|
+
*/
|
|
135
|
+
const disconnectSignal = (instance: object, handlerId: number): void => {
|
|
136
|
+
untrackConnection(instance, handlerId);
|
|
137
|
+
gSignalHandlerDisconnect(getHandle(instance), handlerId);
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
const hasLiveConnection = (instance: object, handlerIds: Set<number>): boolean => {
|
|
141
|
+
let isLive = false;
|
|
142
|
+
|
|
143
|
+
for (const handlerId of handlerIds) {
|
|
144
|
+
if (isSignalHandlerConnected(instance, handlerId)) {
|
|
145
|
+
isLive = true;
|
|
146
|
+
} else {
|
|
147
|
+
handlerIds.delete(handlerId);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return isLive;
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
function hasSignalListener(instance: object, signals?: string[]): boolean {
|
|
155
|
+
const bySignal = connectionTable.get(instance);
|
|
156
|
+
|
|
157
|
+
if (!bySignal) {
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const names =
|
|
162
|
+
signals === undefined ? bySignal.keys().toArray() : signals.map((signal) => getSignalBaseName(signal));
|
|
163
|
+
|
|
164
|
+
return names.some((name) => {
|
|
165
|
+
const handlerIds = bySignal.get(name);
|
|
166
|
+
|
|
167
|
+
return handlerIds !== undefined && hasLiveConnection(instance, handlerIds);
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
86
171
|
const getSignalId = (instance: object, signal: string): number => {
|
|
87
172
|
const type: bigint = (instance as TypedClass).__type__;
|
|
88
173
|
|
|
@@ -108,17 +193,14 @@ function connectBind(type: bigint, signal: string, callback: CallbackDescriptor)
|
|
|
108
193
|
* @param spec Callback descriptor, handler function, and whether to run after the default handler.
|
|
109
194
|
*/
|
|
110
195
|
function connectSignal(instance: object, signal: string, spec: SignalConnectSpec): number {
|
|
111
|
-
const { callback, handler,
|
|
196
|
+
const { callback, handler, isAfter } = spec;
|
|
112
197
|
const wrapped = wrapCallback(handler, callback, "emitter");
|
|
113
198
|
const type: bigint = (instance as TypedClass).__type__;
|
|
114
199
|
const connect = connectBind(type, signal, callback);
|
|
200
|
+
const handlerId = connect(getHandle(instance), signal, wrapped, isAfter ? 1 : 0) as number;
|
|
201
|
+
trackConnection(instance, getSignalBaseName(signal), handlerId);
|
|
115
202
|
|
|
116
|
-
return
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
function blockMatchedSignalHandlers(instance: object, signal: string): void {
|
|
120
|
-
const signalId = getSignalId(instance, signal);
|
|
121
|
-
gSignalHandlersBlockMatched(getHandle(instance), 1, signalId, 0, undefined, undefined, undefined);
|
|
203
|
+
return handlerId;
|
|
122
204
|
}
|
|
123
205
|
|
|
124
206
|
const createEmitValue = (arg: EmitArg): { value: ExternalObject<Handle>; read?: () => unknown } => {
|
|
@@ -185,8 +267,10 @@ function emitSignal(instance: object, signal: string, args: EmitArg[], returnDes
|
|
|
185
267
|
export {
|
|
186
268
|
getSignalBaseName,
|
|
187
269
|
connectSignal,
|
|
188
|
-
|
|
270
|
+
disconnectSignal,
|
|
189
271
|
emitSignal,
|
|
272
|
+
hasSignalListener,
|
|
190
273
|
isSignalHandlerConnected,
|
|
274
|
+
untrackConnection,
|
|
191
275
|
type SignalHandler,
|
|
192
276
|
};
|
package/src/t.ts
CHANGED
|
@@ -2,43 +2,87 @@ import { bind } from "./bind.js";
|
|
|
2
2
|
import * as helpers from "./descriptors.js";
|
|
3
3
|
import { fn } from "./fn.js";
|
|
4
4
|
|
|
5
|
+
/** The descriptor factories and function binders exposed as {@link t}. */
|
|
5
6
|
type T = {
|
|
7
|
+
/** Binds a symbol in a shared library to a callable that marshals its arguments and return value. */
|
|
6
8
|
bind: typeof bind;
|
|
9
|
+
/** Descriptor for a `gint8`, marshalled as a number. */
|
|
7
10
|
int8: typeof helpers.int8T;
|
|
11
|
+
/** Descriptor for a `guint8`, marshalled as a number. */
|
|
8
12
|
uint8: typeof helpers.uint8T;
|
|
13
|
+
/** Descriptor for a `gint16`, marshalled as a number. */
|
|
9
14
|
int16: typeof helpers.int16T;
|
|
15
|
+
/** Descriptor for a `guint16`, marshalled as a number. */
|
|
10
16
|
uint16: typeof helpers.uint16T;
|
|
17
|
+
/** Descriptor for a `gint32`, marshalled as a number. */
|
|
11
18
|
int32: typeof helpers.int32T;
|
|
19
|
+
/** Descriptor for a `guint32`, marshalled as a number. */
|
|
12
20
|
uint32: typeof helpers.uint32T;
|
|
21
|
+
/** Descriptor for a `gint64`, marshalled as a number and rejected outside the 2^53 safe range. */
|
|
13
22
|
int64: typeof helpers.int64T;
|
|
23
|
+
/** Descriptor for a `guint64`, marshalled as a number and rejected outside the 2^53 safe range. */
|
|
14
24
|
uint64: typeof helpers.uint64T;
|
|
25
|
+
/** Descriptor for a `gint64`, marshalled as a bigint so the full 64-bit range survives. */
|
|
15
26
|
bigint64: typeof helpers.bigint64T;
|
|
27
|
+
/** Descriptor for a `guint64`, marshalled as a bigint so the full 64-bit range survives. */
|
|
16
28
|
biguint64: typeof helpers.biguint64T;
|
|
29
|
+
/** Descriptor for a `GType`, marshalled as a bigint and recognized as a GType by GValue conversion. */
|
|
17
30
|
gtype: typeof helpers.gtypeT;
|
|
31
|
+
/** Descriptor for a `gfloat`. */
|
|
18
32
|
float32: typeof helpers.float32T;
|
|
33
|
+
/** Descriptor for a `gdouble`. */
|
|
19
34
|
float64: typeof helpers.float64T;
|
|
35
|
+
/** Descriptor for a `gboolean`, marshalled as a JavaScript boolean. */
|
|
20
36
|
boolean: typeof helpers.booleanT;
|
|
37
|
+
/** Descriptor for the absence of a value, used as the return descriptor of a `void` function. */
|
|
21
38
|
void: typeof helpers.voidT;
|
|
39
|
+
/** Descriptor for a `gunichar`, marshalled as a single-character string or a codepoint number. */
|
|
22
40
|
unichar: typeof helpers.unicharT;
|
|
41
|
+
/** Descriptor for an opaque `gpointer` argument, taken from a typed array's memory or a numeric address. */
|
|
23
42
|
buffer: typeof helpers.bufferT;
|
|
43
|
+
/**
|
|
44
|
+
* Builds a descriptor for a C string, whose optional length sizes the caller-allocated buffer
|
|
45
|
+
* used when the string is passed by reference.
|
|
46
|
+
*/
|
|
24
47
|
string: typeof helpers.stringT;
|
|
48
|
+
/** Builds a descriptor for a `GObject`, wrapped in the class registered for its runtime GType. */
|
|
25
49
|
object: typeof helpers.objectT;
|
|
50
|
+
/** Builds a descriptor for a `GBoxed` value of the named type. */
|
|
26
51
|
boxed: typeof helpers.boxedT;
|
|
52
|
+
/** Builds a descriptor for a plain C struct. */
|
|
27
53
|
struct: typeof helpers.structT;
|
|
54
|
+
/** Builds a descriptor for a fundamental type whose lifetime is managed by named ref and unref functions. */
|
|
28
55
|
fundamental: typeof helpers.fundamentalT;
|
|
56
|
+
/** Wraps a descriptor in a pointer to it, for an output or inout argument. */
|
|
29
57
|
ref: typeof helpers.refT;
|
|
58
|
+
/** Builds a descriptor for a `GHashTable`, marshalled as an array of key/value pairs. */
|
|
30
59
|
hashTable: typeof helpers.hashTableT;
|
|
60
|
+
/** Builds a descriptor for an enumeration, resolving its GType from the named `get_type` function. */
|
|
31
61
|
enum: typeof helpers.enumT;
|
|
62
|
+
/** Builds a descriptor for a flags type, resolving its GType from the named `get_type` function. */
|
|
32
63
|
flags: typeof helpers.flagsT;
|
|
64
|
+
/** Builds a descriptor for an array of items in one of the supported container layouts. */
|
|
33
65
|
array: typeof helpers.arrayT;
|
|
66
|
+
/** Builds a descriptor for a `GList` of items. */
|
|
34
67
|
list: typeof helpers.listT;
|
|
68
|
+
/** Builds a descriptor for a `GSList` of items. */
|
|
35
69
|
slist: typeof helpers.slistT;
|
|
70
|
+
/** Builds a descriptor for a `GPtrArray` of items. */
|
|
36
71
|
ptrArray: typeof helpers.ptrArrayT;
|
|
72
|
+
/** Builds a descriptor for a `GArray` of items, optionally with an explicit element size. */
|
|
37
73
|
gArray: typeof helpers.gArrayT;
|
|
74
|
+
/** Builds a descriptor for a `GByteArray`. */
|
|
38
75
|
byteArray: typeof helpers.byteArrayT;
|
|
76
|
+
/** Builds a descriptor for a C array whose length is carried by another argument. */
|
|
39
77
|
sizedArray: typeof helpers.sizedArrayT;
|
|
78
|
+
/** Builds a descriptor for a C array of a fixed length. */
|
|
40
79
|
fixedArray: typeof helpers.fixedArrayT;
|
|
80
|
+
/** Builds a descriptor for a function pointer, marshalling a JavaScript function into a native closure. */
|
|
41
81
|
callback: typeof helpers.callbackT;
|
|
82
|
+
/**
|
|
83
|
+
* Binds a native function, wiring up argument directions, `GError` checking, and packing output
|
|
84
|
+
* arguments into the result.
|
|
85
|
+
*/
|
|
42
86
|
fn: typeof fn;
|
|
43
87
|
};
|
|
44
88
|
|
package/src/type.ts
CHANGED
|
@@ -15,11 +15,11 @@ import { LIB } from "./library.js";
|
|
|
15
15
|
|
|
16
16
|
/** Object tagged with its GLib type through a `__type__` GType field. */
|
|
17
17
|
type TypedClass = {
|
|
18
|
+
/** GType the object's class is registered under. */
|
|
18
19
|
__type__: bigint;
|
|
19
20
|
};
|
|
20
21
|
|
|
21
22
|
type ResolvableKind = "enum" | "flags" | "boxed" | "fundamental" | "array";
|
|
22
|
-
/** Descriptor kinds whose GType is resolved from library metadata rather than a fixed fundamental type. */
|
|
23
23
|
type ResolvableDescriptor = Extract<Descriptor, { kind: ResolvableKind }>;
|
|
24
24
|
|
|
25
25
|
const resolvedTypeCache: Map<string, bigint> = new Map();
|
|
@@ -120,6 +120,7 @@ const isTypedClass = (value: unknown): value is TypedClass =>
|
|
|
120
120
|
typeof value === "object" && value !== null && "__type__" in value && typeof value.__type__ === "bigint";
|
|
121
121
|
|
|
122
122
|
/** Returns whether `type` is `ancestorType` or descends from it. */
|
|
123
|
+
/* eslint-disable-next-line unicorn/consistent-boolean-name -- mirrors g_type_is_a from the C API */
|
|
123
124
|
function typeIsA(type: bigint, ancestorType: bigint): boolean {
|
|
124
125
|
return gTypeIsA(type, ancestorType) as boolean;
|
|
125
126
|
}
|
|
@@ -151,6 +152,7 @@ function typeName(type: bigint): string | null {
|
|
|
151
152
|
}
|
|
152
153
|
|
|
153
154
|
/** Returns whether `value` is a typed wrapper whose GType is or descends from `gtype`. */
|
|
155
|
+
/* eslint-disable-next-line unicorn/consistent-boolean-name -- mirrors g_type_is_a from the C API */
|
|
154
156
|
function valueIsA(value: unknown, gtype: bigint): boolean {
|
|
155
157
|
return isTypedClass(value) && typeIsA(value.__type__, gtype);
|
|
156
158
|
}
|
|
@@ -285,6 +287,5 @@ export {
|
|
|
285
287
|
resolveBoxedType,
|
|
286
288
|
resolveFundamentalType,
|
|
287
289
|
resolveDescriptorType,
|
|
288
|
-
type ResolvableDescriptor,
|
|
289
290
|
type TypedClass,
|
|
290
291
|
};
|