@gtkx/runtime 1.0.0-rc.1 → 1.0.0-rc.2
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/dist/arg.d.ts +6 -5
- package/dist/arg.d.ts.map +1 -1
- package/dist/arg.js +5 -4
- package/dist/arg.js.map +1 -1
- package/dist/bind.d.ts +3 -2
- package/dist/bind.d.ts.map +1 -1
- package/dist/bind.js +5 -3
- package/dist/bind.js.map +1 -1
- package/dist/callback.d.ts +3 -3
- package/dist/callback.d.ts.map +1 -1
- package/dist/callback.js +51 -33
- package/dist/callback.js.map +1 -1
- package/dist/descriptors.d.ts +67 -64
- package/dist/descriptors.d.ts.map +1 -1
- package/dist/descriptors.js +92 -60
- package/dist/descriptors.js.map +1 -1
- package/dist/error.d.ts +5 -6
- package/dist/error.d.ts.map +1 -1
- package/dist/error.js +10 -6
- package/dist/error.js.map +1 -1
- package/dist/exit-hook.d.ts +2 -0
- package/dist/exit-hook.d.ts.map +1 -0
- package/dist/exit-hook.js +3 -0
- package/dist/exit-hook.js.map +1 -0
- package/dist/fn.d.ts +2 -2
- package/dist/fn.d.ts.map +1 -1
- package/dist/fn.js +26 -15
- package/dist/fn.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/library.d.ts +6 -5
- package/dist/library.d.ts.map +1 -1
- package/dist/library.js +6 -5
- package/dist/library.js.map +1 -1
- package/dist/lifecycle.d.ts +10 -9
- package/dist/lifecycle.d.ts.map +1 -1
- package/dist/lifecycle.js +38 -23
- package/dist/lifecycle.js.map +1 -1
- package/dist/listeners.d.ts +8 -8
- package/dist/listeners.d.ts.map +1 -1
- package/dist/listeners.js +28 -15
- package/dist/listeners.js.map +1 -1
- package/dist/mixin.d.ts +5 -11
- package/dist/mixin.d.ts.map +1 -1
- package/dist/mixin.js +24 -14
- package/dist/mixin.js.map +1 -1
- package/dist/native-value.d.ts +3 -2
- package/dist/native-value.d.ts.map +1 -1
- package/dist/native-value.js +75 -42
- package/dist/native-value.js.map +1 -1
- package/dist/object.d.ts +4 -3
- package/dist/object.d.ts.map +1 -1
- package/dist/object.js +8 -5
- package/dist/object.js.map +1 -1
- package/dist/promisify.d.ts +9 -1
- package/dist/promisify.d.ts.map +1 -1
- package/dist/promisify.js +18 -15
- package/dist/promisify.js.map +1 -1
- package/dist/properties.d.ts +14 -0
- package/dist/properties.d.ts.map +1 -0
- package/dist/properties.js +79 -0
- package/dist/properties.js.map +1 -0
- package/dist/register-class.d.ts +10 -2
- package/dist/register-class.d.ts.map +1 -1
- package/dist/register-class.js +83 -29
- package/dist/register-class.js.map +1 -1
- package/dist/registry.d.ts +23 -21
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +54 -35
- package/dist/registry.js.map +1 -1
- package/dist/signal.d.ts +7 -6
- package/dist/signal.d.ts.map +1 -1
- package/dist/signal.js +19 -12
- package/dist/signal.js.map +1 -1
- package/dist/t.d.ts +2 -2
- package/dist/t.d.ts.map +1 -1
- package/dist/t.js +2 -1
- package/dist/t.js.map +1 -1
- package/dist/tuple.d.ts +3 -2
- package/dist/tuple.d.ts.map +1 -1
- package/dist/tuple.js +7 -4
- package/dist/tuple.js.map +1 -1
- package/dist/type.d.ts +48 -41
- package/dist/type.d.ts.map +1 -1
- package/dist/type.js +107 -87
- package/dist/type.js.map +1 -1
- package/dist/value.d.ts +19 -10
- package/dist/value.d.ts.map +1 -1
- package/dist/value.js +164 -99
- package/dist/value.js.map +1 -1
- package/package.json +4 -6
- package/src/arg.ts +7 -5
- package/src/bind.ts +13 -4
- package/src/callback.ts +97 -35
- package/src/descriptors.ts +227 -85
- package/src/error.ts +18 -9
- package/src/exit-hook.ts +3 -0
- package/src/fn.ts +53 -22
- package/src/index.ts +4 -2
- package/src/library.ts +7 -5
- package/src/lifecycle.ts +54 -26
- package/src/listeners.ts +46 -18
- package/src/mixin.ts +39 -14
- package/src/native-value.ts +103 -46
- package/src/object.ts +19 -7
- package/src/promisify.ts +42 -15
- package/src/properties.ts +115 -0
- package/src/register-class.ts +157 -30
- package/src/registry.ts +144 -54
- package/src/signal.ts +53 -13
- package/src/t.ts +3 -1
- package/src/tuple.ts +16 -4
- package/src/type.ts +183 -93
- package/src/value.ts +231 -108
package/src/fn.ts
CHANGED
|
@@ -24,61 +24,87 @@ type ArgSpec = {
|
|
|
24
24
|
isOutParam: boolean;
|
|
25
25
|
};
|
|
26
26
|
|
|
27
|
-
const buildNativeArgTypes = (args: Arg[],
|
|
27
|
+
const buildNativeArgTypes = (args: Arg[], canThrow: boolean): Descriptor[] => {
|
|
28
28
|
const nativeArgTypes = args.map((argSpec) =>
|
|
29
29
|
argSpec.direction !== undefined && argSpec.callerAllocated !== true ? refT(argSpec.type) : argSpec.type,
|
|
30
30
|
);
|
|
31
|
-
|
|
31
|
+
|
|
32
|
+
if (canThrow) {
|
|
32
33
|
nativeArgTypes.push(
|
|
33
34
|
refT(boxedT("GError", { ownership: "full", sharedLibrary: LIB, getTypeFnName: "g_error_get_type" })),
|
|
34
35
|
);
|
|
36
|
+
}
|
|
37
|
+
|
|
35
38
|
return nativeArgTypes;
|
|
36
39
|
};
|
|
37
40
|
|
|
38
41
|
const buildArgSpecs = (args: Arg[]): ArgSpec[] => {
|
|
39
42
|
let inputCursor = 0;
|
|
43
|
+
|
|
40
44
|
return args.map((arg) => {
|
|
41
45
|
const isRef = isRefArg(arg);
|
|
42
|
-
const
|
|
46
|
+
const isConsumesInput = !isRef || isInoutArg(arg);
|
|
43
47
|
const isOutParam = isOutputArg(arg) && arg.consumed !== true;
|
|
48
|
+
|
|
44
49
|
return {
|
|
45
50
|
arg,
|
|
46
51
|
isRef,
|
|
47
52
|
isCallerAllocated: isCallerAllocatedArg(arg),
|
|
48
|
-
consumesInput,
|
|
49
|
-
inputIndex:
|
|
53
|
+
consumesInput: isConsumesInput,
|
|
54
|
+
inputIndex: isConsumesInput ? inputCursor++ : -1,
|
|
50
55
|
isOutParam,
|
|
51
56
|
};
|
|
52
57
|
});
|
|
53
58
|
};
|
|
54
59
|
|
|
60
|
+
const resolveCallerAllocated = (inputs: unknown[], inputIndex: number): unknown => {
|
|
61
|
+
const wrapper = inputs[inputIndex];
|
|
62
|
+
|
|
63
|
+
return wrapper == null ? wrapper : getHandle(wrapper);
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const buildRefValue = (isInputConsumed: boolean, inputs: unknown[], inputIndex: number): Ref => ({
|
|
67
|
+
value: isInputConsumed ? inputs[inputIndex] : null,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const buildNativeValue = (spec: ArgSpec, inputs: unknown[]): unknown => {
|
|
71
|
+
const { arg, isRef, isCallerAllocated, consumesInput, inputIndex } = spec;
|
|
72
|
+
|
|
73
|
+
if (isCallerAllocated) {
|
|
74
|
+
return resolveCallerAllocated(inputs, inputIndex);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (isRef) {
|
|
78
|
+
return buildRefValue(consumesInput, inputs, inputIndex);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (arg.type.kind === "callback") {
|
|
82
|
+
return wrapCallbackValue(arg.type, inputs[inputIndex]);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return inputs[inputIndex];
|
|
86
|
+
};
|
|
87
|
+
|
|
55
88
|
const buildNativeValues = (plans: ArgSpec[], inputs: unknown[]): unknown[] =>
|
|
56
|
-
plans.map((
|
|
57
|
-
if (isCallerAllocated) {
|
|
58
|
-
const wrapper = inputs[inputIndex];
|
|
59
|
-
return wrapper == null ? wrapper : getHandle(wrapper as object);
|
|
60
|
-
}
|
|
61
|
-
if (isRef) {
|
|
62
|
-
return { value: consumesInput ? inputs[inputIndex] : null };
|
|
63
|
-
}
|
|
64
|
-
if (arg.type.kind === "callback") {
|
|
65
|
-
return wrapCallbackValue(arg.type, inputs[inputIndex]);
|
|
66
|
-
}
|
|
67
|
-
return inputs[inputIndex];
|
|
68
|
-
});
|
|
89
|
+
plans.map((plan) => buildNativeValue(plan, inputs));
|
|
69
90
|
|
|
70
91
|
const readOutParams = (plans: ArgSpec[], inputs: unknown[], nativeValues: unknown[]): unknown[] => {
|
|
71
92
|
const outParams: unknown[] = [];
|
|
72
|
-
|
|
73
|
-
|
|
93
|
+
|
|
94
|
+
for (const [index, { arg, isCallerAllocated, inputIndex, isOutParam }] of plans.entries()) {
|
|
95
|
+
if (!isOutParam) {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
|
|
74
99
|
outParams.push(
|
|
75
100
|
isCallerAllocated ? inputs[inputIndex] : fromNative(arg.type, (nativeValues[index] as Ref).value),
|
|
76
101
|
);
|
|
77
|
-
}
|
|
102
|
+
}
|
|
103
|
+
|
|
78
104
|
return outParams;
|
|
79
105
|
};
|
|
80
106
|
|
|
81
|
-
|
|
107
|
+
function fn(sharedLibrary: string, symbol: string, spec: FnSpec): (...inputs: unknown[]) => unknown {
|
|
82
108
|
const { args, returns: returnDescriptor, throws = false } = spec;
|
|
83
109
|
const nativeArgTypes = buildNativeArgTypes(args, throws);
|
|
84
110
|
const nativeFn = bind(sharedLibrary, symbol, nativeArgTypes, returnDescriptor);
|
|
@@ -87,6 +113,7 @@ export function fn(sharedLibrary: string, symbol: string, spec: FnSpec): (...inp
|
|
|
87
113
|
|
|
88
114
|
const shape = (inputs: unknown[], nativeValues: unknown[], nativeResult: unknown): unknown => {
|
|
89
115
|
const primary = hasPrimary ? fromNative(returnDescriptor, nativeResult) : undefined;
|
|
116
|
+
|
|
90
117
|
return packTupleResult(readOutParams(plans, inputs, nativeValues), primary, hasPrimary);
|
|
91
118
|
};
|
|
92
119
|
|
|
@@ -97,12 +124,16 @@ export function fn(sharedLibrary: string, symbol: string, spec: FnSpec): (...inp
|
|
|
97
124
|
nativeValues.push(errorRef);
|
|
98
125
|
const nativeResult = nativeFn(...nativeValues);
|
|
99
126
|
checkError(errorRef);
|
|
127
|
+
|
|
100
128
|
return shape(inputs, nativeValues, nativeResult);
|
|
101
129
|
};
|
|
102
130
|
}
|
|
103
131
|
|
|
104
132
|
return (...inputs) => {
|
|
105
133
|
const nativeValues = buildNativeValues(plans, inputs);
|
|
134
|
+
|
|
106
135
|
return shape(inputs, nativeValues, nativeFn(...nativeValues));
|
|
107
136
|
};
|
|
108
137
|
}
|
|
138
|
+
|
|
139
|
+
export { fn };
|
package/src/index.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
import "./exit-hook.js";
|
|
2
|
+
|
|
2
3
|
export { createErrorDomain, type ErrorDomain } from "./error.js";
|
|
3
4
|
export { type ApplicationLike, onExit, quit, quitApplication, runApplication } from "./lifecycle.js";
|
|
4
5
|
export { offSignal, onceSignal, onSignal } from "./listeners.js";
|
|
5
6
|
export { installMixins, type Mixin } from "./mixin.js";
|
|
6
7
|
export { fromNative } from "./native-value.js";
|
|
7
8
|
export { getObjectProperty, newObjectWithProperties, setObjectProperty } from "./object.js";
|
|
8
|
-
export { promisify } from "./promisify.js";
|
|
9
|
+
export { type FinishResult, promisify } from "./promisify.js";
|
|
9
10
|
export { registerClass } from "./register-class.js";
|
|
10
11
|
export {
|
|
11
12
|
getHandle,
|
|
@@ -55,3 +56,4 @@ export {
|
|
|
55
56
|
valueIsA,
|
|
56
57
|
} from "./type.js";
|
|
57
58
|
export { getBoxedValue, setBoxedValue } from "./value.js";
|
|
59
|
+
export { alloc, type ExternalObject, type Handle, read, setWrapper, write } from "@gtkx/native";
|
package/src/library.ts
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import { type BoxedDescriptor, boxedT, type FundamentalDescriptor, fundamentalT } from "./descriptors.js";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
const LIB = "libgobject-2.0.so.0,libglib-2.0.so.0";
|
|
4
|
+
const VALUE_SIZE = 24;
|
|
5
|
+
const VALUE_T: BoxedDescriptor = boxedT("GValue", { sharedLibrary: LIB, getTypeFnName: "g_value_get_type" });
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
const PARAM_T: FundamentalDescriptor = fundamentalT(LIB, "g_param_spec_ref", "g_param_spec_unref", {
|
|
8
8
|
ownership: "borrowed",
|
|
9
9
|
typeName: "GParam",
|
|
10
10
|
});
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
const VARIANT_T: FundamentalDescriptor = fundamentalT(LIB, "g_variant_ref", "g_variant_unref", {
|
|
13
13
|
ownership: "borrowed",
|
|
14
14
|
typeName: "GVariant",
|
|
15
15
|
});
|
|
16
|
+
|
|
17
|
+
export { LIB, VALUE_SIZE, VALUE_T, PARAM_T, VARIANT_T };
|
package/src/lifecycle.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { blockMatchedSignalHandlers } from "./signal.js";
|
|
|
5
5
|
* Minimal structural interface of a GTK4/GIO application needed to register, run,
|
|
6
6
|
* and shut it down, plus the signals its lifecycle helpers connect to.
|
|
7
7
|
*/
|
|
8
|
-
|
|
8
|
+
type ApplicationLike = {
|
|
9
9
|
getIsRegistered(): boolean;
|
|
10
10
|
register(cancellable: null): boolean;
|
|
11
11
|
activate(): void;
|
|
@@ -17,7 +17,29 @@ export type ApplicationLike = {
|
|
|
17
17
|
};
|
|
18
18
|
|
|
19
19
|
const shutdownCallbacks: (() => void)[] = [];
|
|
20
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Runs every registered exit callback and shuts down the native runtime. Safe to
|
|
22
|
+
* call more than once; only the first call takes effect.
|
|
23
|
+
*/
|
|
24
|
+
const quit: () => void = createQuit();
|
|
25
|
+
|
|
26
|
+
function createQuit(): () => void {
|
|
27
|
+
let hasQuit = false;
|
|
28
|
+
|
|
29
|
+
return () => {
|
|
30
|
+
if (hasQuit) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
hasQuit = true;
|
|
35
|
+
|
|
36
|
+
for (const callback of shutdownCallbacks) {
|
|
37
|
+
callback();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
nativeQuit();
|
|
41
|
+
};
|
|
42
|
+
}
|
|
21
43
|
|
|
22
44
|
/**
|
|
23
45
|
* Registers a callback to run once when the process quits, before the native
|
|
@@ -25,36 +47,29 @@ let hasQuit = false;
|
|
|
25
47
|
*
|
|
26
48
|
* @param callback Invoked during shutdown.
|
|
27
49
|
*/
|
|
28
|
-
|
|
50
|
+
const onExit = (callback: () => void): void => {
|
|
29
51
|
shutdownCallbacks.push(callback);
|
|
30
52
|
};
|
|
31
53
|
|
|
32
|
-
/**
|
|
33
|
-
* Runs every registered exit callback and shuts down the native runtime. Safe to
|
|
34
|
-
* call more than once; only the first call takes effect.
|
|
35
|
-
*/
|
|
36
|
-
export const quit = (): void => {
|
|
37
|
-
if (hasQuit) return;
|
|
38
|
-
hasQuit = true;
|
|
39
|
-
|
|
40
|
-
for (const callback of shutdownCallbacks) callback();
|
|
41
|
-
|
|
42
|
-
nativeQuit();
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
process.on("exit", quit);
|
|
46
|
-
|
|
47
54
|
/**
|
|
48
55
|
* Registers the application if needed and activates it, keeping the runtime alive
|
|
49
56
|
* while it is active and releasing it on shutdown.
|
|
50
57
|
*
|
|
51
58
|
* @param application The application to register and activate.
|
|
52
59
|
*/
|
|
53
|
-
|
|
54
|
-
application.on("activate", () =>
|
|
55
|
-
|
|
60
|
+
const runApplication = (application: ApplicationLike): void => {
|
|
61
|
+
application.on("activate", () => {
|
|
62
|
+
keepAlive(true);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
application.on("shutdown", () => {
|
|
66
|
+
keepAlive(false);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
if (!application.getIsRegistered()) {
|
|
70
|
+
application.register(null);
|
|
71
|
+
}
|
|
56
72
|
|
|
57
|
-
if (!application.getIsRegistered()) application.register(null);
|
|
58
73
|
application.activate();
|
|
59
74
|
};
|
|
60
75
|
|
|
@@ -64,10 +79,23 @@ export const runApplication = (application: ApplicationLike): void => {
|
|
|
64
79
|
*
|
|
65
80
|
* @param application The application to shut down.
|
|
66
81
|
*/
|
|
67
|
-
|
|
68
|
-
if (!application.getIsRegistered())
|
|
69
|
-
|
|
70
|
-
|
|
82
|
+
const quitApplication = (application: ApplicationLike): void => {
|
|
83
|
+
if (!application.getIsRegistered()) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const windows = application.getWindows?.() ?? [];
|
|
88
|
+
|
|
89
|
+
for (const window of windows) {
|
|
90
|
+
application.removeWindow?.(window);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
application.on("shutdown", () => {
|
|
94
|
+
application.quit();
|
|
95
|
+
});
|
|
96
|
+
|
|
71
97
|
blockMatchedSignalHandlers(application, "activate");
|
|
72
98
|
application.run([]);
|
|
73
99
|
};
|
|
100
|
+
|
|
101
|
+
export { onExit, quit, runApplication, quitApplication, type ApplicationLike };
|
package/src/listeners.ts
CHANGED
|
@@ -1,34 +1,50 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { isSignalHandlerConnected, type SignalHandler } from "./signal.js";
|
|
2
2
|
|
|
3
3
|
type SignalConnectable = {
|
|
4
|
-
connect(signal: string, handler: SignalHandler,
|
|
4
|
+
connect(signal: string, handler: SignalHandler, isAfter?: boolean): number;
|
|
5
5
|
disconnect(handlerId: number): void;
|
|
6
6
|
};
|
|
7
7
|
|
|
8
|
-
const listenerTable
|
|
8
|
+
const listenerTable: WeakMap<object, Map<string, Map<SignalHandler, number>>> = new WeakMap();
|
|
9
9
|
|
|
10
10
|
const findListenerHandlerId = (instance: object, signal: string, handler: SignalHandler): number | undefined =>
|
|
11
11
|
listenerTable.get(instance)?.get(signal)?.get(handler);
|
|
12
12
|
|
|
13
13
|
const trackListener = (instance: object, signal: string, handler: SignalHandler, handlerId: number): void => {
|
|
14
14
|
let bySignal = listenerTable.get(instance);
|
|
15
|
+
|
|
15
16
|
if (!bySignal) {
|
|
16
17
|
bySignal = new Map();
|
|
17
18
|
listenerTable.set(instance, bySignal);
|
|
18
19
|
}
|
|
20
|
+
|
|
19
21
|
let byHandler = bySignal.get(signal);
|
|
22
|
+
|
|
20
23
|
if (!byHandler) {
|
|
21
24
|
byHandler = new Map();
|
|
22
25
|
bySignal.set(signal, byHandler);
|
|
23
26
|
}
|
|
27
|
+
|
|
24
28
|
byHandler.set(handler, handlerId);
|
|
25
29
|
};
|
|
26
30
|
|
|
27
|
-
const
|
|
31
|
+
const untrackHandlerId = (instance: object, signal: string, handlerId: number): void => {
|
|
28
32
|
const bySignal = listenerTable.get(instance);
|
|
29
33
|
const byHandler = bySignal?.get(signal);
|
|
30
|
-
|
|
31
|
-
if (byHandler
|
|
34
|
+
|
|
35
|
+
if (byHandler === undefined) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
for (const [handler, id] of byHandler) {
|
|
40
|
+
if (id === handlerId) {
|
|
41
|
+
byHandler.delete(handler);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (byHandler.size === 0) {
|
|
46
|
+
bySignal?.delete(signal);
|
|
47
|
+
}
|
|
32
48
|
};
|
|
33
49
|
|
|
34
50
|
/**
|
|
@@ -38,10 +54,10 @@ const untrackListener = (instance: object, signal: string, handler: SignalHandle
|
|
|
38
54
|
* @param instance The object emitting the signal.
|
|
39
55
|
* @param signal The signal name to connect to.
|
|
40
56
|
* @param handler The callback invoked on each emission.
|
|
41
|
-
* @param
|
|
57
|
+
* @param isAfter When true, run the handler after the default handler.
|
|
42
58
|
*/
|
|
43
|
-
|
|
44
|
-
const handlerId = instance.connect(signal, handler,
|
|
59
|
+
function onSignal(instance: SignalConnectable, signal: string, handler: SignalHandler, isAfter?: boolean): void {
|
|
60
|
+
const handlerId = instance.connect(signal, handler, isAfter);
|
|
45
61
|
trackListener(instance, signal, handler, handlerId);
|
|
46
62
|
}
|
|
47
63
|
|
|
@@ -52,17 +68,19 @@ export function onSignal(instance: SignalConnectable, signal: string, handler: S
|
|
|
52
68
|
* @param instance The object emitting the signal.
|
|
53
69
|
* @param signal The signal name to connect to.
|
|
54
70
|
* @param handler The callback invoked on the first emission.
|
|
55
|
-
* @param
|
|
71
|
+
* @param isAfter When true, run the handler after the default handler.
|
|
56
72
|
*/
|
|
57
|
-
|
|
73
|
+
function onceSignal(instance: SignalConnectable, signal: string, handler: SignalHandler, isAfter?: boolean): void {
|
|
58
74
|
let handlerId = 0;
|
|
75
|
+
|
|
59
76
|
const wrapped: SignalHandler = (...args) => {
|
|
60
|
-
|
|
61
|
-
untrackListener(instance, signal, handler);
|
|
77
|
+
untrackHandlerId(instance, signal, handlerId);
|
|
62
78
|
instance.disconnect(handlerId);
|
|
79
|
+
|
|
63
80
|
return handler(...args);
|
|
64
81
|
};
|
|
65
|
-
|
|
82
|
+
|
|
83
|
+
handlerId = instance.connect(signal, wrapped, isAfter);
|
|
66
84
|
trackListener(instance, signal, wrapped, handlerId);
|
|
67
85
|
trackListener(instance, signal, handler, handlerId);
|
|
68
86
|
}
|
|
@@ -75,10 +93,20 @@ export function onceSignal(instance: SignalConnectable, signal: string, handler:
|
|
|
75
93
|
* @param signal The signal name the handler was connected to.
|
|
76
94
|
* @param handler The handler to disconnect.
|
|
77
95
|
*/
|
|
78
|
-
|
|
96
|
+
function offSignal(instance: SignalConnectable, signal: string, handler: SignalHandler): void {
|
|
79
97
|
const handlerId = findListenerHandlerId(instance, signal, handler);
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
98
|
+
|
|
99
|
+
if (handlerId === undefined) {
|
|
100
|
+
return;
|
|
83
101
|
}
|
|
102
|
+
|
|
103
|
+
untrackHandlerId(instance, signal, handlerId);
|
|
104
|
+
|
|
105
|
+
if (!isSignalHandlerConnected(instance, handlerId)) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
instance.disconnect(handlerId);
|
|
84
110
|
}
|
|
111
|
+
|
|
112
|
+
export { onSignal, onceSignal, offSignal };
|
package/src/mixin.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { AnyClass } from "@gtkx/utils";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
connect(signal: string, handler: (...args: unknown[]) => unknown,
|
|
3
|
+
type MixinReceiver = {
|
|
4
|
+
connect(signal: string, handler: (...args: unknown[]) => unknown, isAfter?: boolean): number;
|
|
5
5
|
emit(signal: string, ...args: unknown[]): unknown;
|
|
6
6
|
};
|
|
7
7
|
|
|
@@ -9,17 +9,38 @@ export type MixinReceiver = {
|
|
|
9
9
|
* A factory that, given a base class, returns a subclass adding extra members to
|
|
10
10
|
* be merged onto a target prototype.
|
|
11
11
|
*/
|
|
12
|
-
|
|
12
|
+
type Mixin = (base: AnyClass<MixinReceiver>) => AnyClass;
|
|
13
13
|
|
|
14
|
-
function
|
|
14
|
+
function isDefinedInClassChain(prototype: object, key: string): boolean {
|
|
15
15
|
let current: object | null = prototype;
|
|
16
|
+
|
|
16
17
|
while (current !== null && current !== Object.prototype) {
|
|
17
|
-
if (Object.hasOwn(current, key))
|
|
18
|
-
|
|
18
|
+
if (Object.hasOwn(current, key)) {
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
current = Reflect.getPrototypeOf(current);
|
|
19
23
|
}
|
|
24
|
+
|
|
20
25
|
return false;
|
|
21
26
|
}
|
|
22
27
|
|
|
28
|
+
function copyLayerMember(target: AnyClass, layer: object, key: string): void {
|
|
29
|
+
if (key === "constructor") {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (isDefinedInClassChain(target.prototype, key)) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const descriptor = Object.getOwnPropertyDescriptor(layer, key);
|
|
38
|
+
|
|
39
|
+
if (descriptor !== undefined) {
|
|
40
|
+
Object.defineProperty(target.prototype, key, descriptor);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
23
44
|
/**
|
|
24
45
|
* Copies each mixin's prototype members onto the target class prototype, skipping
|
|
25
46
|
* any member already defined anywhere in the target's class chain.
|
|
@@ -27,22 +48,26 @@ function definedInClassChain(prototype: object, key: string): boolean {
|
|
|
27
48
|
* @param target The class whose prototype receives the mixin members.
|
|
28
49
|
* @param mixins The mixins to apply, in order.
|
|
29
50
|
*/
|
|
30
|
-
|
|
51
|
+
function copyLayerMembers(target: AnyClass, layer: object): void {
|
|
52
|
+
for (const key of Object.getOwnPropertyNames(layer)) {
|
|
53
|
+
copyLayerMember(target, layer, key);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function installMixins(target: AnyClass, mixins: Mixin[]): void {
|
|
31
58
|
const empty: AnyClass<MixinReceiver> = class {
|
|
32
59
|
connect(): number {
|
|
33
60
|
return 0;
|
|
34
61
|
}
|
|
62
|
+
|
|
35
63
|
emit(): unknown {
|
|
36
64
|
return undefined;
|
|
37
65
|
}
|
|
38
66
|
};
|
|
67
|
+
|
|
39
68
|
for (const mixin of mixins) {
|
|
40
|
-
|
|
41
|
-
for (const key of Object.getOwnPropertyNames(layer)) {
|
|
42
|
-
if (key === "constructor") continue;
|
|
43
|
-
if (definedInClassChain(target.prototype, key)) continue;
|
|
44
|
-
const descriptor = Object.getOwnPropertyDescriptor(layer, key);
|
|
45
|
-
if (descriptor !== undefined) Object.defineProperty(target.prototype, key, descriptor);
|
|
46
|
-
}
|
|
69
|
+
copyLayerMembers(target, mixin(empty).prototype);
|
|
47
70
|
}
|
|
48
71
|
}
|
|
72
|
+
|
|
73
|
+
export { installMixins, type MixinReceiver, type Mixin };
|
package/src/native-value.ts
CHANGED
|
@@ -1,12 +1,65 @@
|
|
|
1
1
|
import type { Descriptor, ExternalObject, Handle } from "@gtkx/native";
|
|
2
|
-
import type { ArrayDescriptor, FundamentalDescriptor, StructDescriptor } from "./descriptors.js";
|
|
2
|
+
import type { ArrayDescriptor, FundamentalDescriptor, HashTableDescriptor, StructDescriptor } from "./descriptors.js";
|
|
3
3
|
import { getWrapperClass, tryGetHandle, wrapHandle } from "./registry.js";
|
|
4
4
|
import { resolveDescriptorType } from "./type.js";
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
type MarshalledKind = "object" | "struct" | "boxed" | "fundamental" | "array" | "hashtable";
|
|
7
|
+
type MarshalledDescriptor = Extract<Descriptor, { kind: MarshalledKind }>;
|
|
8
|
+
|
|
9
|
+
const MARSHALLED_KINDS: Set<Descriptor["kind"]> = new Set<MarshalledKind>([
|
|
10
|
+
"object",
|
|
11
|
+
"struct",
|
|
12
|
+
"boxed",
|
|
13
|
+
"fundamental",
|
|
14
|
+
"array",
|
|
15
|
+
"hashtable",
|
|
16
|
+
]);
|
|
17
|
+
|
|
18
|
+
function isMarshalledDescriptor(descriptor: Descriptor): descriptor is MarshalledDescriptor {
|
|
19
|
+
return MARSHALLED_KINDS.has(descriptor.kind);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function mapCollection(
|
|
23
|
+
descriptor: ArrayDescriptor,
|
|
24
|
+
value: unknown,
|
|
25
|
+
convert: (itemDescriptor: Descriptor, item: unknown) => unknown,
|
|
26
|
+
): unknown[] {
|
|
27
|
+
return (value as unknown[]).map((item) => convert(descriptor.itemDescriptor, item));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function collectionFromNative(descriptor: ArrayDescriptor, value: unknown): unknown {
|
|
31
|
+
return value === null ? null : mapCollection(descriptor, value, fromNative);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function boxedFromNative(descriptor: Descriptor, value: unknown): unknown {
|
|
35
|
+
return value == null
|
|
36
|
+
? null
|
|
37
|
+
: wrapHandle(value as ExternalObject<Handle>, getWrapperClass(resolveDescriptorType(descriptor)));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function fundamentalFromNative(descriptor: FundamentalDescriptor, value: unknown): unknown {
|
|
41
|
+
return value == null
|
|
42
|
+
? null
|
|
43
|
+
: wrapHandle(
|
|
44
|
+
value as ExternalObject<Handle>,
|
|
45
|
+
descriptor.wrapperClass ?? getWrapperClass(resolveDescriptorType(descriptor)),
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function hashTableFromNative(descriptor: HashTableDescriptor, value: unknown): unknown {
|
|
50
|
+
if (value === null) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const entries = value as [unknown, unknown][];
|
|
55
|
+
|
|
56
|
+
return new Map(
|
|
57
|
+
entries.map(([key, val]): [unknown, unknown] => [
|
|
58
|
+
fromNative(descriptor.keyDescriptor, key),
|
|
59
|
+
fromNative(descriptor.valueDescriptor, val),
|
|
60
|
+
]),
|
|
61
|
+
);
|
|
62
|
+
}
|
|
10
63
|
|
|
11
64
|
/**
|
|
12
65
|
* Converts a raw value returned from native code into its JavaScript form,
|
|
@@ -16,63 +69,67 @@ const collectionFromNative = (descriptor: ArrayDescriptor, value: unknown): unkn
|
|
|
16
69
|
* @param descriptor Describes the native type of the value.
|
|
17
70
|
* @param value The raw native value to convert.
|
|
18
71
|
*/
|
|
19
|
-
|
|
72
|
+
function fromNative(descriptor: Descriptor, value: unknown): unknown {
|
|
73
|
+
if (!isMarshalledDescriptor(descriptor)) {
|
|
74
|
+
return value;
|
|
75
|
+
}
|
|
76
|
+
|
|
20
77
|
switch (descriptor.kind) {
|
|
21
|
-
case "object":
|
|
22
|
-
return wrapHandle(value as ExternalObject<Handle> | null
|
|
23
|
-
|
|
78
|
+
case "object": {
|
|
79
|
+
return wrapHandle(value as ExternalObject<Handle> | null);
|
|
80
|
+
}
|
|
81
|
+
case "struct": {
|
|
24
82
|
return wrapHandle(value as ExternalObject<Handle> | null, (descriptor as StructDescriptor).wrapperClass);
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
case "fundamental":
|
|
30
|
-
return value
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
value as ExternalObject<Handle>,
|
|
34
|
-
(descriptor as FundamentalDescriptor).wrapperClass ??
|
|
35
|
-
getWrapperClass(resolveDescriptorType(descriptor)),
|
|
36
|
-
);
|
|
37
|
-
case "array":
|
|
83
|
+
}
|
|
84
|
+
case "boxed": {
|
|
85
|
+
return boxedFromNative(descriptor, value);
|
|
86
|
+
}
|
|
87
|
+
case "fundamental": {
|
|
88
|
+
return fundamentalFromNative(descriptor, value);
|
|
89
|
+
}
|
|
90
|
+
case "array": {
|
|
38
91
|
return collectionFromNative(descriptor, value);
|
|
92
|
+
}
|
|
39
93
|
case "hashtable": {
|
|
40
|
-
|
|
41
|
-
const entries = value as [unknown, unknown][];
|
|
42
|
-
return new Map(
|
|
43
|
-
entries.map(([key, val]): [unknown, unknown] => [
|
|
44
|
-
fromNative(descriptor.keyDescriptor, key),
|
|
45
|
-
fromNative(descriptor.valueDescriptor, val),
|
|
46
|
-
]),
|
|
47
|
-
);
|
|
94
|
+
return hashTableFromNative(descriptor, value);
|
|
48
95
|
}
|
|
49
|
-
default:
|
|
50
|
-
return value;
|
|
51
96
|
}
|
|
52
97
|
}
|
|
53
98
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
99
|
+
function collectionToNative(descriptor: ArrayDescriptor, value: unknown): unknown {
|
|
100
|
+
return value == null ? null : mapCollection(descriptor, value, toNative);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function hashTableToNative(descriptor: HashTableDescriptor, value: unknown): unknown {
|
|
104
|
+
if (value == null) {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return [...(value as Map<unknown, unknown>)].map(([key, val]): [unknown, unknown] => [
|
|
109
|
+
toNative(descriptor.keyDescriptor, key),
|
|
110
|
+
toNative(descriptor.valueDescriptor, val),
|
|
111
|
+
]);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function toNative(descriptor: Descriptor, value: unknown): unknown {
|
|
115
|
+
if (!isMarshalledDescriptor(descriptor)) {
|
|
116
|
+
return value;
|
|
117
|
+
}
|
|
58
118
|
|
|
59
|
-
export function toNative(descriptor: Descriptor, value: unknown): unknown {
|
|
60
119
|
switch (descriptor.kind) {
|
|
61
120
|
case "object":
|
|
62
121
|
case "struct":
|
|
63
122
|
case "boxed":
|
|
64
|
-
case "fundamental":
|
|
123
|
+
case "fundamental": {
|
|
65
124
|
return tryGetHandle(value as object | null | undefined) ?? null;
|
|
66
|
-
|
|
125
|
+
}
|
|
126
|
+
case "array": {
|
|
67
127
|
return collectionToNative(descriptor, value);
|
|
128
|
+
}
|
|
68
129
|
case "hashtable": {
|
|
69
|
-
|
|
70
|
-
return [...(value as Map<unknown, unknown>)].map(([key, val]): [unknown, unknown] => [
|
|
71
|
-
toNative(descriptor.keyDescriptor, key),
|
|
72
|
-
toNative(descriptor.valueDescriptor, val),
|
|
73
|
-
]);
|
|
130
|
+
return hashTableToNative(descriptor, value);
|
|
74
131
|
}
|
|
75
|
-
default:
|
|
76
|
-
return value;
|
|
77
132
|
}
|
|
78
133
|
}
|
|
134
|
+
|
|
135
|
+
export { fromNative, toNative };
|