@gtkx/runtime 2.0.0-beta.1 → 2.0.0-beta.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.
Files changed (55) hide show
  1. package/dist/internal.d.ts +16 -0
  2. package/dist/internal.d.ts.map +1 -1
  3. package/dist/internal.js +12 -0
  4. package/dist/internal.js.map +1 -1
  5. package/dist/listeners.d.ts +3 -13
  6. package/dist/listeners.d.ts.map +1 -1
  7. package/dist/listeners.js +12 -12
  8. package/dist/listeners.js.map +1 -1
  9. package/dist/mixin.d.ts +5 -3
  10. package/dist/mixin.d.ts.map +1 -1
  11. package/dist/mixin.js +36 -9
  12. package/dist/mixin.js.map +1 -1
  13. package/dist/object.d.ts +11 -0
  14. package/dist/object.d.ts.map +1 -1
  15. package/dist/object.js +18 -25
  16. package/dist/object.js.map +1 -1
  17. package/dist/properties.d.ts +4 -2
  18. package/dist/properties.d.ts.map +1 -1
  19. package/dist/properties.js +69 -16
  20. package/dist/properties.js.map +1 -1
  21. package/dist/property-brand.d.ts +5 -0
  22. package/dist/property-brand.d.ts.map +1 -0
  23. package/dist/property-brand.js +5 -0
  24. package/dist/property-brand.js.map +1 -0
  25. package/dist/register-class.d.ts +102 -20
  26. package/dist/register-class.d.ts.map +1 -1
  27. package/dist/register-class.js +19 -37
  28. package/dist/register-class.js.map +1 -1
  29. package/dist/registry.d.ts +1 -1
  30. package/dist/registry.d.ts.map +1 -1
  31. package/dist/registry.js +4 -4
  32. package/dist/registry.js.map +1 -1
  33. package/dist/signal-brand.d.ts +12 -0
  34. package/dist/signal-brand.d.ts.map +1 -0
  35. package/dist/signal-brand.js +6 -0
  36. package/dist/signal-brand.js.map +1 -0
  37. package/dist/signal.d.ts +39 -2
  38. package/dist/signal.d.ts.map +1 -1
  39. package/dist/signal.js +75 -5
  40. package/dist/signal.js.map +1 -1
  41. package/dist/type.d.ts.map +1 -1
  42. package/dist/type.js +1 -1
  43. package/dist/type.js.map +1 -1
  44. package/package.json +4 -4
  45. package/src/internal.ts +41 -0
  46. package/src/listeners.ts +21 -26
  47. package/src/mixin.ts +55 -9
  48. package/src/object.ts +55 -5
  49. package/src/properties.ts +94 -17
  50. package/src/property-brand.ts +5 -0
  51. package/src/register-class.ts +296 -95
  52. package/src/registry.ts +4 -4
  53. package/src/signal-brand.ts +29 -0
  54. package/src/signal.ts +177 -6
  55. package/src/type.ts +1 -1
package/src/signal.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import type { Descriptor, ExternalObject, Handle } from "@gtkx/native";
2
- import { toCamelIdentifier, upperFirst } from "@gtkx/utils";
2
+ import { type AnyClass, toCamelIdentifier, upperFirst } from "@gtkx/utils";
3
+ import type { ResolvedSignalEmitMap, ResolvedSignalMap } from "./signal-brand.js";
3
4
  import { type Arg, isCallerAllocatedArg, isInoutArg, isOutputArg } from "./arg.js";
4
5
  import { bind } from "./bind.js";
5
6
  import { wrapCallback } from "./callback.js";
@@ -19,7 +20,7 @@ import {
19
20
  voidT,
20
21
  } from "./descriptors.js";
21
22
  import { LIB, VALUE_SIZE, VALUE_T } from "./library.js";
22
- import { getHandle } from "./registry.js";
23
+ import { getClassType, getHandle, getInstanceType } from "./registry.js";
23
24
  import { packTupleResult } from "./tuple.js";
24
25
  import { TYPE_INVALID, type TypedClass, typeInterfaces, typeParent } from "./type.js";
25
26
  import {
@@ -37,6 +38,55 @@ import {
37
38
  /** Function invoked when a connected GObject signal is emitted. */
38
39
  type SignalHandler = (...args: unknown[]) => unknown;
39
40
 
41
+ const isSignalHandler = (value: unknown): value is SignalHandler => typeof value === "function";
42
+
43
+ type DeclaredSignalMap<T> = T extends { __signals__?: infer TSignals } ? NonNullable<TSignals> : never;
44
+ type SignalMap<T> = ResolvedSignalMap<T, DeclaredSignalMap<T>>;
45
+ type SignalName<T> = Extract<keyof SignalMap<T>, string>;
46
+ type DeclaredSignalEmitMap<T> = T extends { __signalEmit__?: infer TSignals }
47
+ ? NonNullable<TSignals>
48
+ : T extends { __signals__?: infer TSignals }
49
+ ? {
50
+ [K in keyof NonNullable<TSignals>]: NonNullable<TSignals>[K] extends (
51
+ ...args: infer TArgs
52
+ ) => infer TResult
53
+ ? { args: TArgs; result: TResult }
54
+ : never;
55
+ }
56
+ : never;
57
+ type SignalEmitMap<T> = ResolvedSignalEmitMap<T, DeclaredSignalEmitMap<T>>;
58
+ type SignalEmitName<T> = Extract<keyof SignalEmitMap<T>, string>;
59
+ type SignalEmitArguments<T, K extends SignalEmitName<T>> = SignalEmitMap<T>[K] extends {
60
+ args: infer TArgs extends unknown[];
61
+ }
62
+ ? TArgs
63
+ : never;
64
+ type SignalEmitResult<T, K extends SignalEmitName<T>> = SignalEmitMap<T>[K] extends {
65
+ result: infer TResult;
66
+ }
67
+ ? TResult
68
+ : never;
69
+
70
+ type SignalConnector = (
71
+ instance: object,
72
+ signal: string,
73
+ handler: SignalHandler,
74
+ isAfter?: boolean,
75
+ ) => number;
76
+ type SignalEmitter = (instance: object, signal: string, args: unknown[]) => unknown;
77
+ type SignalDispatch = {
78
+ connect: SignalConnector;
79
+ emit: SignalEmitter;
80
+ };
81
+ type SignalDispatchSpec = {
82
+ connect: SignalConnector;
83
+ emit: SignalEmitter;
84
+ };
85
+ type PendingSignalDispatch = {
86
+ ownerType: bigint;
87
+ spec: SignalDispatchSpec;
88
+ };
89
+
40
90
  type DeclaredSignalTypes = {
41
91
  paramTypes: bigint[];
42
92
  returnType?: bigint;
@@ -70,6 +120,8 @@ type EmitArg = Arg & {
70
120
  };
71
121
 
72
122
  const connectionTable: WeakMap<object, Map<string, Set<number>>> = new WeakMap();
123
+ const signalDispatchTable: Map<number, SignalDispatch> = new Map();
124
+ const pendingSignalDispatches: Map<string, PendingSignalDispatch[]> = new Map();
73
125
  const gQuarkFromString = bind(LIB, "g_quark_from_string", [stringT("borrowed")], uint32T);
74
126
  const gSignalLookup = bind(LIB, "g_signal_lookup", [stringT("borrowed"), biguint64T], uint32T);
75
127
  const gSignalName = bind(LIB, "g_signal_name", [uint32T], stringT("borrowed"));
@@ -114,6 +166,15 @@ const getSignalBaseName = (signal: string): string => {
114
166
  return detailIndex === -1 ? signal : signal.slice(0, detailIndex);
115
167
  };
116
168
 
169
+ const canonicalSignalName = (signal: string): string => getSignalBaseName(signal).replaceAll("_", "-");
170
+
171
+ const canonicalDetailedSignalName = (signal: string): string => {
172
+ const detailIndex = signal.indexOf("::");
173
+ const base = canonicalSignalName(signal);
174
+
175
+ return detailIndex === -1 ? base : `${base}${signal.slice(detailIndex)}`;
176
+ };
177
+
117
178
  function getSignalDetailQuark(signal: string): number {
118
179
  const detailIndex = signal.indexOf("::");
119
180
 
@@ -180,7 +241,7 @@ function hasSignalListener(instance: object, signals?: string[]): boolean {
180
241
  }
181
242
 
182
243
  const names =
183
- signals === undefined ? bySignal.keys().toArray() : signals.map((signal) => getSignalBaseName(signal));
244
+ signals === undefined ? bySignal.keys().toArray() : signals.map((signal) => canonicalSignalName(signal));
184
245
 
185
246
  return names.some((name) => {
186
247
  const handlerIds = bySignal.get(name);
@@ -242,7 +303,7 @@ function connectSignal(instance: object, signal: string, spec: SignalConnectSpec
242
303
  const key = `${String(type)}\0${getSignalBaseName(signal)}`;
243
304
  const closure = newCCallbackClosure(key, callback, wrapped);
244
305
  const handlerId = gSignalConnectClosure(getHandle(instance), signal, closure, isAfter) as number;
245
- trackConnection(instance, getSignalBaseName(signal), handlerId);
306
+ trackConnection(instance, canonicalSignalName(signal), handlerId);
246
307
 
247
308
  return handlerId;
248
309
  }
@@ -254,7 +315,7 @@ function overrideSignalClassClosure(type: bigint, signalId: number, handler: Sig
254
315
  function connectClosureSignal(instance: object, signal: string, handler: SignalHandler, isAfter: boolean): number {
255
316
  const closure = toClosure((...args: unknown[]) => Reflect.apply(handler, null, args.slice(1)));
256
317
  const handlerId = gSignalConnectClosure(getHandle(instance), signal, closure, isAfter) as number;
257
- trackConnection(instance, getSignalBaseName(signal), handlerId);
318
+ trackConnection(instance, canonicalSignalName(signal), handlerId);
258
319
 
259
320
  return handlerId;
260
321
  }
@@ -350,19 +411,129 @@ function emitSignal(instance: object, signal: string, args: EmitArg[], returns?:
350
411
  return packTupleResult(readEmitOutputs(reads), fromValue(returnValue), true);
351
412
  }
352
413
 
414
+ function installSignalDispatch(
415
+ klass: AnyClass,
416
+ names: readonly string[],
417
+ spec: SignalDispatchSpec,
418
+ ): void {
419
+ const ownerType = getClassType(klass);
420
+
421
+ for (const rawName of names) {
422
+ const name = canonicalSignalName(rawName);
423
+ pendingSignalDispatches.getOrInsertComputed(name, () => []).push({ ownerType, spec });
424
+ }
425
+ }
426
+
427
+ const resolvePendingSignalDispatch = (signalId: number, name: string): SignalDispatch | undefined => {
428
+ const pending = pendingSignalDispatches.get(name) ?? [];
429
+
430
+ for (let index = pending.length - 1; index >= 0; index -= 1) {
431
+ const entry = pending[index];
432
+
433
+ if (
434
+ entry !== undefined &&
435
+ entry.ownerType !== TYPE_INVALID &&
436
+ signalIdFor(entry.ownerType, name) === signalId
437
+ ) {
438
+ signalDispatchTable.set(signalId, entry.spec);
439
+
440
+ return entry.spec;
441
+ }
442
+ }
443
+
444
+ return undefined;
445
+ };
446
+
447
+ const signalDispatchFor = (instance: object, signal: string): SignalDispatch | undefined => {
448
+ const name = canonicalSignalName(signal);
449
+ const instanceType = getInstanceType(instance);
450
+
451
+ if (instanceType === TYPE_INVALID) {
452
+ return undefined;
453
+ }
454
+
455
+ const signalId = signalIdFor(instanceType, name);
456
+
457
+ if (signalId === 0) {
458
+ return undefined;
459
+ }
460
+
461
+ return signalDispatchTable.get(signalId) ?? resolvePendingSignalDispatch(signalId, name);
462
+ };
463
+
464
+ const connectDispatcherFor = (instance: object, signal: string): SignalConnector => {
465
+ const dispatch = signalDispatchFor(instance, signal);
466
+
467
+ if (dispatch === undefined) {
468
+ throw new TypeError("connectSignal: unknown signal");
469
+ }
470
+
471
+ return dispatch.connect;
472
+ };
473
+
474
+ const emitDispatcherFor = (instance: object, signal: string): SignalEmitter => {
475
+ const dispatch = signalDispatchFor(instance, signal);
476
+
477
+ if (dispatch === undefined) {
478
+ throw new TypeError("emitSignal: unknown signal");
479
+ }
480
+
481
+ return dispatch.emit;
482
+ };
483
+
484
+ function signalConnect<T extends object, K extends SignalName<NoInfer<T>>>(
485
+ instance: T,
486
+ signal: K,
487
+ handler: SignalMap<NoInfer<T>>[K],
488
+ isAfter?: boolean,
489
+ ): number {
490
+ return connectSignalByName(instance, signal, handler, isAfter);
491
+ }
492
+
493
+ function connectSignalByName(
494
+ instance: object,
495
+ signal: string,
496
+ handler: unknown,
497
+ isAfter?: boolean,
498
+ ): number {
499
+ if (!isSignalHandler(handler)) {
500
+ throw new TypeError("connectSignal: handler must be a function");
501
+ }
502
+
503
+ return connectDispatcherFor(instance, signal)(instance, signal, handler, isAfter);
504
+ }
505
+
506
+ function emitSignalByName(instance: object, signal: string, args: unknown[]): unknown {
507
+ return emitDispatcherFor(instance, signal)(instance, signal, args);
508
+ }
509
+
510
+ function signalEmit<T extends object, K extends SignalEmitName<NoInfer<T>>>(
511
+ instance: T,
512
+ signal: K,
513
+ ...args: SignalEmitArguments<NoInfer<T>, K>
514
+ ): SignalEmitResult<NoInfer<T>, K> {
515
+ return emitSignalByName(instance, signal, args) as SignalEmitResult<NoInfer<T>, K>;
516
+ }
517
+
353
518
  export {
519
+ canonicalDetailedSignalName,
520
+ canonicalSignalName,
354
521
  signalForHandlerName,
355
522
  getSignalBaseName,
356
523
  signalIdFor,
357
524
  connectClosureSignal,
358
525
  connectSignal,
526
+ connectSignalByName,
359
527
  type DeclaredSignalTypes,
360
528
  disconnectSignal,
361
529
  emitDeclaredSignal,
362
530
  emitSignal,
531
+ emitSignalByName,
363
532
  hasSignalListener,
364
533
  isSignalHandlerConnected,
534
+ installSignalDispatch,
365
535
  overrideSignalClassClosure,
366
- untrackConnection,
536
+ signalConnect,
537
+ signalEmit,
367
538
  type SignalHandler,
368
539
  };
package/src/type.ts CHANGED
@@ -86,7 +86,7 @@ const TYPE_VARIANT: bigint = typeFromName("GVariant");
86
86
  /** GType tag for a Unicode character stored as `guint`. */
87
87
  const TYPE_UNICHAR: bigint = typeFromName("guint");
88
88
  const getErrorType: () => bigint = lazyType("GError");
89
- const getStrvType: () => bigint = lazyType("GStrv");
89
+ const getStrvType = (): bigint => resolveType(LIB, "g_strv_get_type");
90
90
 
91
91
  const PLAIN_DESCRIPTOR_TYPES: Partial<Record<Descriptor["kind"], bigint>> = {
92
92
  boolean: TYPE_BOOLEAN,