@gtkx/runtime 1.0.0 → 1.1.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 +1 -1
- package/dist/application-class.d.ts +5 -0
- package/dist/application-class.d.ts.map +1 -1
- package/dist/application-class.js +11 -1
- package/dist/application-class.js.map +1 -1
- package/dist/callback.d.ts +2 -2
- package/dist/callback.d.ts.map +1 -1
- package/dist/callback.js +51 -54
- package/dist/callback.js.map +1 -1
- package/dist/default-application.d.ts +4 -0
- package/dist/default-application.d.ts.map +1 -0
- package/dist/default-application.js +17 -0
- package/dist/default-application.js.map +1 -0
- package/dist/descriptors.d.ts +25 -2
- package/dist/descriptors.d.ts.map +1 -1
- package/dist/descriptors.js +31 -9
- package/dist/descriptors.js.map +1 -1
- package/dist/exit-hook.js +13 -0
- package/dist/exit-hook.js.map +1 -1
- package/dist/fn.d.ts +5 -0
- package/dist/fn.d.ts.map +1 -1
- package/dist/fn.js +11 -13
- package/dist/fn.js.map +1 -1
- package/dist/folded-lengths.d.ts +16 -0
- package/dist/folded-lengths.d.ts.map +1 -0
- package/dist/folded-lengths.js +38 -0
- package/dist/folded-lengths.js.map +1 -0
- 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 +16 -1
- package/dist/lifecycle.d.ts.map +1 -1
- package/dist/lifecycle.js +44 -14
- package/dist/lifecycle.js.map +1 -1
- package/dist/listeners.d.ts.map +1 -1
- package/dist/listeners.js +16 -4
- package/dist/listeners.js.map +1 -1
- package/dist/native-value.js +2 -2
- package/dist/native-value.js.map +1 -1
- package/dist/object.d.ts +7 -2
- package/dist/object.d.ts.map +1 -1
- package/dist/object.js +7 -2
- package/dist/object.js.map +1 -1
- package/dist/param-spec.d.ts.map +1 -1
- package/dist/param-spec.js +18 -20
- package/dist/param-spec.js.map +1 -1
- package/dist/properties.d.ts +2 -1
- package/dist/properties.d.ts.map +1 -1
- package/dist/properties.js +13 -9
- package/dist/properties.js.map +1 -1
- package/dist/register-class.d.ts +30 -0
- package/dist/register-class.d.ts.map +1 -1
- package/dist/register-class.js +21 -7
- package/dist/register-class.js.map +1 -1
- package/dist/registry.d.ts +12 -3
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +24 -15
- package/dist/registry.js.map +1 -1
- package/dist/signal.d.ts +2 -2
- package/dist/signal.d.ts.map +1 -1
- package/dist/signal.js +22 -18
- package/dist/signal.js.map +1 -1
- package/dist/t.d.ts +2 -0
- package/dist/t.d.ts.map +1 -1
- package/dist/t.js +1 -0
- package/dist/t.js.map +1 -1
- package/dist/tuple.d.ts +6 -3
- package/dist/tuple.d.ts.map +1 -1
- package/dist/tuple.js +9 -7
- package/dist/tuple.js.map +1 -1
- package/dist/value.d.ts +3 -1
- package/dist/value.d.ts.map +1 -1
- package/dist/value.js +64 -18
- package/dist/value.js.map +1 -1
- package/dist/vfunc-call.d.ts.map +1 -1
- package/dist/vfunc-call.js +5 -1
- package/dist/vfunc-call.js.map +1 -1
- package/dist/vfunc.d.ts.map +1 -1
- package/dist/vfunc.js +9 -3
- package/dist/vfunc.js.map +1 -1
- package/package.json +14 -4
- package/src/application-class.ts +12 -2
- package/src/callback.ts +70 -66
- package/src/default-application.ts +21 -0
- package/src/descriptors.ts +69 -14
- package/src/exit-hook.ts +18 -0
- package/src/fn.ts +23 -13
- package/src/folded-lengths.ts +64 -0
- package/src/internal.ts +1 -0
- package/src/library.ts +2 -1
- package/src/lifecycle.ts +60 -14
- package/src/listeners.ts +22 -5
- package/src/native-value.ts +2 -2
- package/src/object.ts +7 -2
- package/src/param-spec.ts +21 -22
- package/src/properties.ts +23 -9
- package/src/register-class.ts +63 -8
- package/src/registry.ts +40 -18
- package/src/signal.ts +32 -28
- package/src/t.ts +3 -0
- package/src/tuple.ts +18 -12
- package/src/value.ts +94 -19
- package/src/vfunc-call.ts +5 -1
- package/src/vfunc.ts +10 -3
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { bind } from "./bind.js";
|
|
2
|
+
import { objectT, voidT } from "./descriptors.js";
|
|
3
|
+
import { GIO_LIB } from "./library.js";
|
|
4
|
+
import { getHandle } from "./registry.js";
|
|
5
|
+
|
|
6
|
+
const readDefaultApplication = bind(GIO_LIB, "g_application_get_default", [], objectT("borrowed"));
|
|
7
|
+
const writeDefaultApplication = bind(GIO_LIB, "g_application_set_default", [objectT("borrowed")], voidT);
|
|
8
|
+
|
|
9
|
+
const claimDefaultApplication = (application: object): void => {
|
|
10
|
+
writeDefaultApplication(getHandle(application));
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const releaseDefaultApplication = (application: object): void => {
|
|
14
|
+
if (readDefaultApplication() !== application) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
writeDefaultApplication(null);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export { claimDefaultApplication, releaseDefaultApplication };
|
package/src/descriptors.ts
CHANGED
|
@@ -89,7 +89,10 @@ type BoxedOptions = {
|
|
|
89
89
|
size?: number;
|
|
90
90
|
};
|
|
91
91
|
|
|
92
|
-
/**
|
|
92
|
+
/**
|
|
93
|
+
* What the bindings do with a callback's return value, how the callee takes its closure, and how
|
|
94
|
+
* long that closure has to stay alive.
|
|
95
|
+
*/
|
|
93
96
|
type CallbackOptions = {
|
|
94
97
|
/** The callee also takes a destroy notify, which frees the closure once it is done with it. */
|
|
95
98
|
hasDestroy?: boolean;
|
|
@@ -107,12 +110,22 @@ type CallbackOptions = {
|
|
|
107
110
|
type ArrayOptions = {
|
|
108
111
|
/** Stride in bytes between elements stored inline in the array. */
|
|
109
112
|
elementSize?: number | undefined;
|
|
113
|
+
/** Position of the argument whose buffer a cursor array points into. */
|
|
114
|
+
baseParamIndex?: number | undefined;
|
|
110
115
|
/** Position of the argument carrying the element count, for a length-bounded array. */
|
|
111
116
|
sizeParamIndex?: number | undefined;
|
|
112
117
|
/** Element count of a fixed-length array. */
|
|
113
118
|
fixedSize?: number | undefined;
|
|
114
119
|
};
|
|
115
120
|
|
|
121
|
+
/** Where a cursor array's base buffer and total length come from. */
|
|
122
|
+
type CursorBounds = {
|
|
123
|
+
/** Position of the argument holding the buffer the cursor points into. */
|
|
124
|
+
baseParamIndex: number;
|
|
125
|
+
/** Position of the argument carrying that buffer's element count. */
|
|
126
|
+
sizeParamIndex: number;
|
|
127
|
+
};
|
|
128
|
+
|
|
116
129
|
/** How a ref-counted fundamental value is named, wrapped and stored. */
|
|
117
130
|
type FundamentalOptions = {
|
|
118
131
|
/** Whether a decoded value is owned by the caller; defaults to `"borrowed"`. */
|
|
@@ -125,6 +138,12 @@ type FundamentalOptions = {
|
|
|
125
138
|
isInline?: boolean;
|
|
126
139
|
};
|
|
127
140
|
|
|
141
|
+
type FundamentalLifecycle = {
|
|
142
|
+
sharedLibrary: string;
|
|
143
|
+
refFnName: string;
|
|
144
|
+
unrefFnName: string;
|
|
145
|
+
};
|
|
146
|
+
|
|
128
147
|
/** How a plain C struct is stored and wrapped. */
|
|
129
148
|
type StructOptions = {
|
|
130
149
|
/** The caller owns the storage the callee fills, so a decoded value is borrowed instead of copied. */
|
|
@@ -171,6 +190,7 @@ const voidT: VoidDescriptor = { kind: "void" };
|
|
|
171
190
|
const unicharT: UnicharDescriptor = { kind: "unichar" };
|
|
172
191
|
/** Descriptor for an opaque `gpointer`, taken from a typed array's memory or a numeric address. */
|
|
173
192
|
const bufferT: BufferDescriptor = { kind: "buffer" };
|
|
193
|
+
const fundamentalLifecycles: Map<string, FundamentalLifecycle> = new Map();
|
|
174
194
|
|
|
175
195
|
/**
|
|
176
196
|
* Builds a descriptor for a C string, whose optional length sizes the caller-allocated buffer
|
|
@@ -280,6 +300,15 @@ const structT = (ownership: Ownership = "borrowed", options: StructOptions = {})
|
|
|
280
300
|
return result;
|
|
281
301
|
};
|
|
282
302
|
|
|
303
|
+
const recordFundamentalLifecycle = (typeName: string, lifecycle: FundamentalLifecycle): void => {
|
|
304
|
+
if (!fundamentalLifecycles.has(typeName)) {
|
|
305
|
+
fundamentalLifecycles.set(typeName, lifecycle);
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
const fundamentalLifecycleFor = (typeName: string): FundamentalLifecycle | undefined =>
|
|
310
|
+
fundamentalLifecycles.get(typeName);
|
|
311
|
+
|
|
283
312
|
/** Builds a descriptor for a fundamental type whose lifetime is managed by named ref and unref functions. */
|
|
284
313
|
const fundamentalT = (
|
|
285
314
|
sharedLibrary: string,
|
|
@@ -292,6 +321,7 @@ const fundamentalT = (
|
|
|
292
321
|
|
|
293
322
|
if (options.typeName !== undefined) {
|
|
294
323
|
result.typeName = options.typeName;
|
|
324
|
+
recordFundamentalLifecycle(options.typeName, { sharedLibrary, refFnName, unrefFnName });
|
|
295
325
|
}
|
|
296
326
|
|
|
297
327
|
if (options.wrapperClass !== undefined) {
|
|
@@ -318,6 +348,10 @@ const arrayT = (
|
|
|
318
348
|
result.elementSize = options.elementSize;
|
|
319
349
|
}
|
|
320
350
|
|
|
351
|
+
if (options?.baseParamIndex !== undefined) {
|
|
352
|
+
result.baseParamIndex = options.baseParamIndex;
|
|
353
|
+
}
|
|
354
|
+
|
|
321
355
|
if (options?.sizeParamIndex !== undefined) {
|
|
322
356
|
result.sizeParamIndex = options.sizeParamIndex;
|
|
323
357
|
}
|
|
@@ -361,6 +395,17 @@ const sizedArrayT = (
|
|
|
361
395
|
elementSize?: number,
|
|
362
396
|
): ArrayDescriptor => arrayT(itemDescriptor, "sized", ownership, { sizeParamIndex, elementSize });
|
|
363
397
|
|
|
398
|
+
/**
|
|
399
|
+
* Builds a descriptor for an out pointer into the buffer another argument supplied, decoded as the
|
|
400
|
+
* elements from where it points to the end of that buffer.
|
|
401
|
+
*/
|
|
402
|
+
const cursorArrayT = (
|
|
403
|
+
itemDescriptor: Descriptor,
|
|
404
|
+
bounds: CursorBounds,
|
|
405
|
+
ownership: Ownership = "borrowed",
|
|
406
|
+
elementSize?: number,
|
|
407
|
+
): ArrayDescriptor => arrayT(itemDescriptor, "cursor", ownership, { ...bounds, elementSize });
|
|
408
|
+
|
|
364
409
|
/** Builds a descriptor for a C array of a fixed length. */
|
|
365
410
|
const fixedArrayT = (
|
|
366
411
|
itemDescriptor: Descriptor,
|
|
@@ -369,33 +414,41 @@ const fixedArrayT = (
|
|
|
369
414
|
elementSize?: number,
|
|
370
415
|
): ArrayDescriptor => arrayT(itemDescriptor, "fixed", ownership, { fixedSize, elementSize });
|
|
371
416
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
argDescriptors: Descriptor[],
|
|
375
|
-
returnDescriptor: Descriptor,
|
|
376
|
-
options?: CallbackOptions,
|
|
377
|
-
): CallbackDescriptor => {
|
|
378
|
-
const result: CallbackDescriptor = { kind: "callback", argDescriptors, returnDescriptor };
|
|
379
|
-
|
|
380
|
-
if (options?.hasDestroy !== undefined) {
|
|
417
|
+
const applyClosureOptions = (result: CallbackDescriptor, options: CallbackOptions): void => {
|
|
418
|
+
if (options.hasDestroy !== undefined) {
|
|
381
419
|
result.hasDestroy = options.hasDestroy;
|
|
382
420
|
}
|
|
383
421
|
|
|
384
|
-
if (options
|
|
422
|
+
if (options.destroyKind !== undefined) {
|
|
385
423
|
result.destroyKind = options.destroyKind;
|
|
386
424
|
}
|
|
387
425
|
|
|
388
|
-
if (options
|
|
426
|
+
if (options.hasUserData !== undefined) {
|
|
389
427
|
result.hasUserData = options.hasUserData;
|
|
390
428
|
}
|
|
391
429
|
|
|
392
|
-
if (options
|
|
430
|
+
if (options.userDataIndex !== undefined) {
|
|
393
431
|
result.userDataIndex = options.userDataIndex;
|
|
394
432
|
}
|
|
395
433
|
|
|
396
|
-
if (options
|
|
434
|
+
if (options.scope !== undefined) {
|
|
397
435
|
result.scope = options.scope;
|
|
398
436
|
}
|
|
437
|
+
};
|
|
438
|
+
|
|
439
|
+
/** Builds a descriptor for a function pointer, marshalling a JavaScript function into a native closure. */
|
|
440
|
+
const callbackT = (
|
|
441
|
+
argDescriptors: Descriptor[],
|
|
442
|
+
returnDescriptor: Descriptor,
|
|
443
|
+
options?: CallbackOptions,
|
|
444
|
+
): CallbackDescriptor => {
|
|
445
|
+
const result: CallbackDescriptor = { kind: "callback", argDescriptors, returnDescriptor };
|
|
446
|
+
|
|
447
|
+
if (options === undefined) {
|
|
448
|
+
return result;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
applyClosureOptions(result, options);
|
|
399
452
|
|
|
400
453
|
return result;
|
|
401
454
|
};
|
|
@@ -426,6 +479,7 @@ export {
|
|
|
426
479
|
flagsT,
|
|
427
480
|
boxedT,
|
|
428
481
|
structT,
|
|
482
|
+
fundamentalLifecycleFor,
|
|
429
483
|
fundamentalT,
|
|
430
484
|
arrayT,
|
|
431
485
|
listT,
|
|
@@ -435,6 +489,7 @@ export {
|
|
|
435
489
|
byteArrayT,
|
|
436
490
|
sizedArrayT,
|
|
437
491
|
fixedArrayT,
|
|
492
|
+
cursorArrayT,
|
|
438
493
|
callbackT,
|
|
439
494
|
type BoxedDescriptor,
|
|
440
495
|
type StructDescriptor,
|
package/src/exit-hook.ts
CHANGED
|
@@ -1,3 +1,21 @@
|
|
|
1
1
|
import { quit } from "./lifecycle.js";
|
|
2
2
|
|
|
3
|
+
const TERMINATION_SIGNALS: NodeJS.Signals[] = ["SIGHUP", "SIGINT", "SIGTERM"];
|
|
4
|
+
|
|
5
|
+
const isSoleHandler = (signal: NodeJS.Signals): boolean => process.listenerCount(signal) === 1;
|
|
6
|
+
|
|
7
|
+
const quitOnSignal = (signal: NodeJS.Signals): void => {
|
|
8
|
+
if (!isSoleHandler(signal)) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
quit();
|
|
13
|
+
process.removeAllListeners(signal);
|
|
14
|
+
process.kill(process.pid, signal);
|
|
15
|
+
};
|
|
16
|
+
|
|
3
17
|
process.on("exit", quit);
|
|
18
|
+
|
|
19
|
+
for (const signal of TERMINATION_SIGNALS) {
|
|
20
|
+
process.on(signal, quitOnSignal);
|
|
21
|
+
}
|
package/src/fn.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { checkError } from "./error.js";
|
|
|
8
8
|
import { LIB } from "./library.js";
|
|
9
9
|
import { fromNative } from "./native-value.js";
|
|
10
10
|
import { getHandle } from "./registry.js";
|
|
11
|
-
import { packTupleResult } from "./tuple.js";
|
|
11
|
+
import { hasSurfacedPrimary, packTupleResult } from "./tuple.js";
|
|
12
12
|
|
|
13
13
|
/** The signature a native function is bound against. */
|
|
14
14
|
type FnSpec = {
|
|
@@ -16,6 +16,11 @@ type FnSpec = {
|
|
|
16
16
|
args: Arg[];
|
|
17
17
|
/** Descriptor for the C return value, packed first when output arguments are also returned. */
|
|
18
18
|
returns: Descriptor;
|
|
19
|
+
/**
|
|
20
|
+
* The callee returns a value the bindings do not surface, so the call is made against the
|
|
21
|
+
* declared return type and the value is dropped instead of being packed into the result.
|
|
22
|
+
*/
|
|
23
|
+
isReturnSkipped?: boolean;
|
|
19
24
|
/** The function takes a trailing `GError**`, whose contents are thrown as an error on return. */
|
|
20
25
|
canThrow?: boolean;
|
|
21
26
|
/**
|
|
@@ -135,15 +140,19 @@ const resizeInputs = (inputs: unknown[], argCount: number): unknown[] => {
|
|
|
135
140
|
const directCallable = (
|
|
136
141
|
descriptor: ExternalObject<CallDescriptor>,
|
|
137
142
|
returnDescriptor: Descriptor,
|
|
143
|
+
hasPrimary: boolean,
|
|
138
144
|
argCount: number,
|
|
139
145
|
): ((...inputs: unknown[]) => unknown) => {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
146
|
+
const marshal = (inputs: unknown[]): unknown =>
|
|
147
|
+
fromNative(returnDescriptor, call(descriptor, resizeInputs(inputs, argCount)));
|
|
148
|
+
|
|
149
|
+
if (hasPrimary) {
|
|
150
|
+
return (...inputs) => marshal(inputs);
|
|
144
151
|
}
|
|
145
152
|
|
|
146
|
-
return (...inputs) =>
|
|
153
|
+
return (...inputs) => {
|
|
154
|
+
marshal(inputs);
|
|
155
|
+
};
|
|
147
156
|
};
|
|
148
157
|
|
|
149
158
|
function fromNativeCallable(
|
|
@@ -152,20 +161,21 @@ function fromNativeCallable(
|
|
|
152
161
|
getRefSeeds?: () => RefSeeds | undefined,
|
|
153
162
|
): (...inputs: unknown[]) => unknown {
|
|
154
163
|
const { args, returns: returnDescriptor, canThrow = false } = spec;
|
|
155
|
-
const hasPrimary = returnDescriptor.
|
|
164
|
+
const hasPrimary = hasSurfacedPrimary(returnDescriptor, spec.isReturnSkipped);
|
|
156
165
|
const plans = buildArgSpecs(args);
|
|
157
166
|
const outPlans = plans.filter((plan) => plan.isOutParam);
|
|
158
167
|
const arePassThrough = plans.every((plan, index) => isPassThroughPlan(plan, index));
|
|
159
168
|
|
|
160
169
|
if (!canThrow && arePassThrough) {
|
|
161
|
-
return directCallable(descriptor, returnDescriptor, plans.length);
|
|
170
|
+
return directCallable(descriptor, returnDescriptor, hasPrimary, plans.length);
|
|
162
171
|
}
|
|
163
172
|
|
|
164
|
-
const shape = (inputs: unknown[], nativeValues: unknown[], nativeResult: unknown): unknown =>
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
173
|
+
const shape = (inputs: unknown[], nativeValues: unknown[], nativeResult: unknown): unknown =>
|
|
174
|
+
packTupleResult(
|
|
175
|
+
readOutParams(outPlans, inputs, nativeValues),
|
|
176
|
+
fromNative(returnDescriptor, nativeResult),
|
|
177
|
+
hasPrimary,
|
|
178
|
+
);
|
|
169
179
|
|
|
170
180
|
if (canThrow) {
|
|
171
181
|
return (...inputs) => {
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { Descriptor } from "@gtkx/native";
|
|
2
|
+
|
|
3
|
+
type LengthSource = { kind: "return" } | { kind: "outArg"; argIndex: number };
|
|
4
|
+
type LengthSources = Map<number, LengthSource[]>;
|
|
5
|
+
|
|
6
|
+
type FoldedLengthSpec = {
|
|
7
|
+
argDescriptors: Descriptor[];
|
|
8
|
+
returnDescriptor: Descriptor;
|
|
9
|
+
userDataIndex?: number | undefined;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const sizedArrayLengthIndex = (descriptor: Descriptor): number | undefined =>
|
|
13
|
+
descriptor.kind === "array" && descriptor.arrayKind === "sized" ? descriptor.sizeParamIndex : undefined;
|
|
14
|
+
|
|
15
|
+
const outArgLengthIndex = (descriptor: Descriptor | undefined): number | undefined =>
|
|
16
|
+
descriptor?.kind === "ref" ? sizedArrayLengthIndex(descriptor.innerDescriptor) : undefined;
|
|
17
|
+
|
|
18
|
+
const effectiveArgIndex = (argIndex: number | undefined, userDataIndex: number | undefined): number | undefined => {
|
|
19
|
+
if (argIndex === undefined || userDataIndex === undefined || argIndex < userDataIndex) {
|
|
20
|
+
return argIndex;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return argIndex === userDataIndex ? undefined : argIndex - 1;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const addLengthSource = (sources: LengthSources, index: number | undefined, source: LengthSource): void => {
|
|
27
|
+
if (index === undefined) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const existing = sources.get(index);
|
|
32
|
+
|
|
33
|
+
if (existing === undefined) {
|
|
34
|
+
sources.set(index, [source]);
|
|
35
|
+
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
existing.push(source);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const addOutArgLengthSources = (sources: LengthSources, spec: FoldedLengthSpec): void => {
|
|
43
|
+
const { userDataIndex } = spec;
|
|
44
|
+
|
|
45
|
+
for (const [declaredIndex, descriptor] of spec.argDescriptors.entries()) {
|
|
46
|
+
const argIndex = effectiveArgIndex(declaredIndex, userDataIndex);
|
|
47
|
+
|
|
48
|
+
if (argIndex !== undefined) {
|
|
49
|
+
const lengthIndex = effectiveArgIndex(outArgLengthIndex(descriptor), userDataIndex);
|
|
50
|
+
addLengthSource(sources, lengthIndex, { kind: "outArg", argIndex });
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const foldedLengthSources = (spec: FoldedLengthSpec): LengthSources => {
|
|
56
|
+
const sources: LengthSources = new Map();
|
|
57
|
+
const returnLengthIndex = effectiveArgIndex(sizedArrayLengthIndex(spec.returnDescriptor), spec.userDataIndex);
|
|
58
|
+
addLengthSource(sources, returnLengthIndex, { kind: "return" });
|
|
59
|
+
addOutArgLengthSources(sources, spec);
|
|
60
|
+
|
|
61
|
+
return sources;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export { foldedLengthSources, type LengthSource, type LengthSources };
|
package/src/internal.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { checkError, createErrorDomain } from "./error.js";
|
|
2
|
+
export { type ApplicationInstance, getApplicationInstance } from "./lifecycle.js";
|
|
2
3
|
export { registerClassType, resolveWrapperClass, wrapHandle } from "./registry.js";
|
|
3
4
|
export { hasSignalListener } from "./signal.js";
|
|
4
5
|
export { resolveType } from "./type.js";
|
package/src/library.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type BoxedDescriptor, boxedT, type FundamentalDescriptor, fundamentalT } from "./descriptors.js";
|
|
2
2
|
|
|
3
3
|
const LIB = "libgobject-2.0.so.0,libglib-2.0.so.0";
|
|
4
|
+
const GIO_LIB = "libgio-2.0.so.0";
|
|
4
5
|
const VALUE_SIZE = 24;
|
|
5
6
|
const CLOSURE_SIZE = 32;
|
|
6
7
|
const VALUE_T: BoxedDescriptor = boxedT("GValue", { sharedLibrary: LIB, getTypeFnName: "g_value_get_type" });
|
|
@@ -15,4 +16,4 @@ const VARIANT_T: FundamentalDescriptor = fundamentalT(LIB, "g_variant_ref", "g_v
|
|
|
15
16
|
typeName: "GVariant",
|
|
16
17
|
});
|
|
17
18
|
|
|
18
|
-
export { CLOSURE_SIZE, LIB, VALUE_SIZE, VALUE_T, PARAM_T, VARIANT_T };
|
|
19
|
+
export { CLOSURE_SIZE, GIO_LIB, LIB, VALUE_SIZE, VALUE_T, PARAM_T, VARIANT_T };
|
package/src/lifecycle.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { keepAlive, quit as nativeQuit } from "@gtkx/native";
|
|
2
2
|
import { isDerivedApplication, type LocalCommandLineApplication, shutDownThroughRun } from "./application-class.js";
|
|
3
|
+
import { claimDefaultApplication, releaseDefaultApplication } from "./default-application.js";
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* The GIO and GTK application surface {@link runApplication} and {@link quitApplication} drive, so any
|
|
@@ -30,6 +31,8 @@ type ApplicationLike = {
|
|
|
30
31
|
emit(signal: "shutdown"): unknown;
|
|
31
32
|
};
|
|
32
33
|
|
|
34
|
+
type ApplicationInstance = "primary" | "remote" | "shutDown" | "unregistered";
|
|
35
|
+
|
|
33
36
|
/** What {@link runApplication} reports about the process it just started. */
|
|
34
37
|
type RunApplicationResult = {
|
|
35
38
|
/** Whether this process owns the application ID and may build a user interface. */
|
|
@@ -40,6 +43,7 @@ type RunApplicationResult = {
|
|
|
40
43
|
|
|
41
44
|
const shutdownCallbacks: (() => void)[] = [];
|
|
42
45
|
const startedApplications: WeakSet<object> = new WeakSet();
|
|
46
|
+
const registeredApplications: WeakSet<object> = new WeakSet();
|
|
43
47
|
const shutDownApplications: WeakSet<object> = new WeakSet();
|
|
44
48
|
/**
|
|
45
49
|
* Runs every registered exit callback and shuts down the native runtime. Safe to
|
|
@@ -101,6 +105,14 @@ const restartApplication = (application: ApplicationLike): number => {
|
|
|
101
105
|
return 0;
|
|
102
106
|
};
|
|
103
107
|
|
|
108
|
+
const getApplicationInstance = (application: ApplicationLike): ApplicationInstance => {
|
|
109
|
+
if (!application.getIsRegistered()) {
|
|
110
|
+
return registeredApplications.has(application) ? "shutDown" : "unregistered";
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return application.getIsRemote?.() === true ? "remote" : "primary";
|
|
114
|
+
};
|
|
115
|
+
|
|
104
116
|
/**
|
|
105
117
|
* Hands `argv` to GLib's own command line handling, which parses the application's options, prints
|
|
106
118
|
* `--help`, runs `handle-local-options`, registers the application, and either activates it or
|
|
@@ -119,6 +131,13 @@ const restartApplication = (application: ApplicationLike): number => {
|
|
|
119
131
|
* user interface may be built here, because a remote application has no `GtkApplicationImpl` and
|
|
120
132
|
* attaching a window to it crashes.
|
|
121
133
|
*
|
|
134
|
+
* The application also claims the process-wide default `Gio.Application.getDefault()` returns, before
|
|
135
|
+
* its command line is parsed, so an `activate` handler and anything the host reads afterwards see the
|
|
136
|
+
* application being started rather than an earlier one. That holds whether the start ends registered,
|
|
137
|
+
* remote or unregistered: a refused command line still has to be reportable through the default, which
|
|
138
|
+
* is how `gtkx dev` learns which application its entry mounted. {@link quitApplication} gives the
|
|
139
|
+
* default back up, since GLib drops it only at finalize.
|
|
140
|
+
*
|
|
122
141
|
* @param application The application to start.
|
|
123
142
|
* @param argv The command line, whose first entry names the program as `--help` should print it.
|
|
124
143
|
* @returns Whether this process may build a user interface, and the status to exit with.
|
|
@@ -132,11 +151,19 @@ const runApplication = (application: ApplicationLike, argv: string[]): RunApplic
|
|
|
132
151
|
);
|
|
133
152
|
}
|
|
134
153
|
|
|
154
|
+
claimDefaultApplication(application);
|
|
155
|
+
|
|
135
156
|
const exitStatus = startedApplications.has(application)
|
|
136
157
|
? restartApplication(application)
|
|
137
158
|
: startApplication(application, argv);
|
|
138
159
|
|
|
139
|
-
const
|
|
160
|
+
const instance = getApplicationInstance(application);
|
|
161
|
+
|
|
162
|
+
if (instance !== "unregistered") {
|
|
163
|
+
registeredApplications.add(application);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const isPrimary = instance === "primary";
|
|
140
167
|
|
|
141
168
|
if (isPrimary) {
|
|
142
169
|
keepAlive(true);
|
|
@@ -145,18 +172,7 @@ const runApplication = (application: ApplicationLike, argv: string[]): RunApplic
|
|
|
145
172
|
return { isPrimary, exitStatus };
|
|
146
173
|
};
|
|
147
174
|
|
|
148
|
-
|
|
149
|
-
* Detaches every window from the application and runs GLib's own shutdown, which emits `shutdown`,
|
|
150
|
-
* destroys the application implementation and releases the D-Bus registration. Does nothing for an
|
|
151
|
-
* application that is not registered, so a repeated call is a no-op.
|
|
152
|
-
*
|
|
153
|
-
* GLib's own shutdown is reachable once per application: reaching it marks the application as
|
|
154
|
-
* quitting, which GLib never undoes. An application that has already quit falls back to emitting
|
|
155
|
-
* `shutdown`, which releases the runtime and leaves the registration for GLib to drop at finalize.
|
|
156
|
-
*
|
|
157
|
-
* @param application The application to shut down.
|
|
158
|
-
*/
|
|
159
|
-
const quitApplication = (application: ApplicationLike): void => {
|
|
175
|
+
const tearDownApplication = (application: ApplicationLike): void => {
|
|
160
176
|
if (!application.getIsRegistered() || shutDownApplications.has(application)) {
|
|
161
177
|
return;
|
|
162
178
|
}
|
|
@@ -175,4 +191,34 @@ const quitApplication = (application: ApplicationLike): void => {
|
|
|
175
191
|
}
|
|
176
192
|
};
|
|
177
193
|
|
|
178
|
-
|
|
194
|
+
/**
|
|
195
|
+
* Detaches every window from the application and runs GLib's own shutdown, which emits `shutdown`,
|
|
196
|
+
* destroys the application implementation and releases the D-Bus registration. Does nothing for an
|
|
197
|
+
* application that is not registered, so a repeated call is a no-op.
|
|
198
|
+
*
|
|
199
|
+
* GLib's own shutdown is reachable once per application: reaching it marks the application as
|
|
200
|
+
* quitting, which GLib never undoes. An application that has already quit falls back to emitting
|
|
201
|
+
* `shutdown`, which releases the runtime and leaves the registration for GLib to drop at finalize.
|
|
202
|
+
*
|
|
203
|
+
* The process-wide default {@link runApplication} claimed is given up whichever of those paths the
|
|
204
|
+
* application took, including the ones that have no teardown left to do, so
|
|
205
|
+
* `Gio.Application.getDefault()` never hands back an application that has been torn down or that
|
|
206
|
+
* never registered in the first place. GLib clears that default only at finalize, which a
|
|
207
|
+
* garbage-collected wrapper reaches arbitrarily late or never.
|
|
208
|
+
*
|
|
209
|
+
* @param application The application to shut down.
|
|
210
|
+
*/
|
|
211
|
+
const quitApplication = (application: ApplicationLike): void => {
|
|
212
|
+
tearDownApplication(application);
|
|
213
|
+
releaseDefaultApplication(application);
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
export {
|
|
217
|
+
getApplicationInstance,
|
|
218
|
+
onExit,
|
|
219
|
+
quit,
|
|
220
|
+
runApplication,
|
|
221
|
+
quitApplication,
|
|
222
|
+
type ApplicationInstance,
|
|
223
|
+
type RunApplicationResult,
|
|
224
|
+
};
|
package/src/listeners.ts
CHANGED
|
@@ -11,10 +11,10 @@ type SignalConnectable = {
|
|
|
11
11
|
disconnect(handlerId: number): void;
|
|
12
12
|
};
|
|
13
13
|
|
|
14
|
-
const listenerTable: WeakMap<object, Map<string, Map<SignalHandler, number>>> = new WeakMap();
|
|
14
|
+
const listenerTable: WeakMap<object, Map<string, Map<SignalHandler, number[]>>> = new WeakMap();
|
|
15
15
|
|
|
16
16
|
const findListenerHandlerId = (instance: object, signal: string, handler: SignalHandler): number | undefined =>
|
|
17
|
-
listenerTable.get(instance)?.get(signal)?.get(handler);
|
|
17
|
+
listenerTable.get(instance)?.get(signal)?.get(handler)?.at(-1);
|
|
18
18
|
|
|
19
19
|
const trackListener = (instance: object, signal: string, handler: SignalHandler, handlerId: number): void => {
|
|
20
20
|
let bySignal = listenerTable.get(instance);
|
|
@@ -31,7 +31,24 @@ const trackListener = (instance: object, signal: string, handler: SignalHandler,
|
|
|
31
31
|
bySignal.set(signal, byHandler);
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
byHandler.
|
|
34
|
+
let handlerIds = byHandler.get(handler);
|
|
35
|
+
|
|
36
|
+
if (!handlerIds) {
|
|
37
|
+
handlerIds = [];
|
|
38
|
+
byHandler.set(handler, handlerIds);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
handlerIds.push(handlerId);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const removeTrackedHandlerId = (handlerIds: number[], handlerId: number): number => {
|
|
45
|
+
const index = handlerIds.lastIndexOf(handlerId);
|
|
46
|
+
|
|
47
|
+
if (index !== -1) {
|
|
48
|
+
handlerIds.splice(index, 1);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return handlerIds.length;
|
|
35
52
|
};
|
|
36
53
|
|
|
37
54
|
const untrackHandlerId = (instance: object, signal: string, handlerId: number): void => {
|
|
@@ -42,8 +59,8 @@ const untrackHandlerId = (instance: object, signal: string, handlerId: number):
|
|
|
42
59
|
return;
|
|
43
60
|
}
|
|
44
61
|
|
|
45
|
-
for (const [handler,
|
|
46
|
-
if (
|
|
62
|
+
for (const [handler, handlerIds] of byHandler) {
|
|
63
|
+
if (removeTrackedHandlerId(handlerIds, handlerId) === 0) {
|
|
47
64
|
byHandler.delete(handler);
|
|
48
65
|
}
|
|
49
66
|
}
|
package/src/native-value.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Descriptor, ExternalObject, Handle } from "@gtkx/native";
|
|
2
2
|
import type { ArrayDescriptor, FundamentalDescriptor, HashTableDescriptor, StructDescriptor } from "./descriptors.js";
|
|
3
|
-
import { getHandle, getWrapperClass, wrapHandle, wrapObject } from "./registry.js";
|
|
3
|
+
import { getHandle, getWrapperClass, wrapCallScopedObject, wrapHandle, wrapObject } from "./registry.js";
|
|
4
4
|
import { resolveDescriptorType } from "./type.js";
|
|
5
5
|
|
|
6
6
|
type MarshalledKind = "object" | "struct" | "boxed" | "fundamental" | "array" | "hashtable";
|
|
@@ -76,7 +76,7 @@ function fromNative(descriptor: Descriptor, value: unknown): unknown {
|
|
|
76
76
|
|
|
77
77
|
switch (descriptor.kind) {
|
|
78
78
|
case "object": {
|
|
79
|
-
return wrapObject(value);
|
|
79
|
+
return descriptor.isCallScoped === true ? wrapCallScopedObject(value) : wrapObject(value);
|
|
80
80
|
}
|
|
81
81
|
case "struct": {
|
|
82
82
|
return wrapHandle(value as ExternalObject<Handle> | null, (descriptor as StructDescriptor).wrapperClass);
|
package/src/object.ts
CHANGED
|
@@ -121,7 +121,9 @@ function constructPropertyForEntry(
|
|
|
121
121
|
* that name, dashed or camelCased, and is skipped when the type installs none.
|
|
122
122
|
* A value that ParamSpec would refuse throws before GObject sees it: a `TypeError`
|
|
123
123
|
* for a read-only property and for a value of a type the property cannot hold, and
|
|
124
|
-
* a `RangeError` for a value the ParamSpec rejects.
|
|
124
|
+
* a `RangeError` for a value the ParamSpec rejects. A value marshalled through a
|
|
125
|
+
* declared descriptor is converted rather than checked, so a `null` handed to a
|
|
126
|
+
* numeric one of those lands 0 rather than being refused.
|
|
125
127
|
* Properties whose value is `undefined` are skipped. A type registered with
|
|
126
128
|
* `registerClass` binds the wrapper before its `constructed` slot runs, so an
|
|
127
129
|
* override of that slot already sees a usable instance.
|
|
@@ -168,7 +170,10 @@ function getObjectProperty(obj: object, propertyName: string, descriptor: Descri
|
|
|
168
170
|
|
|
169
171
|
/**
|
|
170
172
|
* Writes a JavaScript value to a GObject property, converting it to native form
|
|
171
|
-
* using the descriptor.
|
|
173
|
+
* using the descriptor. The descriptor converts what it is given rather than checking it against
|
|
174
|
+
* the property's `GObject.ParamSpec`, so `null` and `undefined` written to a numeric or enum
|
|
175
|
+
* property land 0, where the same write to a property installed through `registerClass` is
|
|
176
|
+
* refused with a `TypeError`.
|
|
172
177
|
*
|
|
173
178
|
* @param obj The object to write to.
|
|
174
179
|
* @param propertyName The property name.
|