@gtkx/runtime 1.3.0 → 1.5.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/dist/t.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"t.js","sourceRoot":"","sources":["../src/t.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,KAAK,OAAO,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAwF7B;;;;GAIG;AACH,MAAM,CAAC,GAAM;IACT,IAAI;IACJ,IAAI,EAAE,OAAO,CAAC,KAAK;IACnB,KAAK,EAAE,OAAO,CAAC,MAAM;IACrB,KAAK,EAAE,OAAO,CAAC,MAAM;IACrB,MAAM,EAAE,OAAO,CAAC,OAAO;IACvB,KAAK,EAAE,OAAO,CAAC,MAAM;IACrB,MAAM,EAAE,OAAO,CAAC,OAAO;IACvB,KAAK,EAAE,OAAO,CAAC,MAAM;IACrB,MAAM,EAAE,OAAO,CAAC,OAAO;IACvB,QAAQ,EAAE,OAAO,CAAC,SAAS;IAC3B,SAAS,EAAE,OAAO,CAAC,UAAU;IAC7B,KAAK,EAAE,OAAO,CAAC,MAAM;IACrB,OAAO,EAAE,OAAO,CAAC,QAAQ;IACzB,OAAO,EAAE,OAAO,CAAC,QAAQ;IACzB,OAAO,EAAE,OAAO,CAAC,QAAQ;IACzB,IAAI,EAAE,OAAO,CAAC,KAAK;IACnB,OAAO,EAAE,OAAO,CAAC,QAAQ;IACzB,MAAM,EAAE,OAAO,CAAC,OAAO;IACvB,MAAM,EAAE,OAAO,CAAC,OAAO;IACvB,MAAM,EAAE,OAAO,CAAC,OAAO;IACvB,KAAK,EAAE,OAAO,CAAC,MAAM;IACrB,MAAM,EAAE,OAAO,CAAC,OAAO;IACvB,WAAW,EAAE,OAAO,CAAC,YAAY;IACjC,GAAG,EAAE,OAAO,CAAC,IAAI;IACjB,SAAS,EAAE,OAAO,CAAC,UAAU;IAC7B,IAAI,EAAE,OAAO,CAAC,KAAK;IACnB,KAAK,EAAE,OAAO,CAAC,MAAM;IACrB,KAAK,EAAE,OAAO,CAAC,MAAM;IACrB,IAAI,EAAE,OAAO,CAAC,KAAK;IACnB,KAAK,EAAE,OAAO,CAAC,MAAM;IACrB,QAAQ,EAAE,OAAO,CAAC,SAAS;IAC3B,MAAM,EAAE,OAAO,CAAC,OAAO;IACvB,SAAS,EAAE,OAAO,CAAC,UAAU;IAC7B,UAAU,EAAE,OAAO,CAAC,WAAW;IAC/B,UAAU,EAAE,OAAO,CAAC,WAAW;IAC/B,WAAW,EAAE,OAAO,CAAC,YAAY;IACjC,QAAQ,EAAE,OAAO,CAAC,SAAS;IAC3B,EAAE;CACL,CAAC;AAEF,OAAO,EAAE,CAAC,EAAE,CAAC","sourcesContent":["import { bind } from \"./bind.js\";\nimport * as helpers from \"./descriptors.js\";\nimport { fn } from \"./fn.js\";\n\n/** The descriptor factories and function binders exposed as {@link t}. */\ntype T = {\n /** Binds a symbol in a shared library to a callable that marshals its arguments and return value. */\n bind: typeof bind;\n /** Descriptor for a `gint8`, marshalled as a number. */\n int8: typeof helpers.int8T;\n /** Descriptor for a `guint8`, marshalled as a number. */\n uint8: typeof helpers.uint8T;\n /** Descriptor for a `gint16`, marshalled as a number. */\n int16: typeof helpers.int16T;\n /** Descriptor for a `guint16`, marshalled as a number. */\n uint16: typeof helpers.uint16T;\n /** Descriptor for a `gint32`, marshalled as a number. */\n int32: typeof helpers.int32T;\n /** Descriptor for a `guint32`, marshalled as a number. */\n uint32: typeof helpers.uint32T;\n /** Descriptor for a `gint64`, marshalled as a number and rejected outside the 2^53 safe range. */\n int64: typeof helpers.int64T;\n /** Descriptor for a `guint64`, marshalled as a number and rejected outside the 2^53 safe range. */\n uint64: typeof helpers.uint64T;\n /** Descriptor for a `gint64`, marshalled as a bigint so the full 64-bit range survives. */\n bigint64: typeof helpers.bigint64T;\n /** Descriptor for a `guint64`, marshalled as a bigint so the full 64-bit range survives. */\n biguint64: typeof helpers.biguint64T;\n /** Descriptor for a `GType`, marshalled as a bigint and recognized as a GType by GValue conversion. */\n gtype: typeof helpers.gtypeT;\n /** Descriptor for a `gfloat`. */\n float32: typeof helpers.float32T;\n /** Descriptor for a `gdouble`. */\n float64: typeof helpers.float64T;\n /** Descriptor for a `gboolean`, marshalled as a JavaScript boolean. */\n boolean: typeof helpers.booleanT;\n /** Descriptor for the absence of a value, used as the return descriptor of a `void` function. */\n void: typeof helpers.voidT;\n /** Descriptor for a `gunichar`, marshalled as a single-character string or a codepoint number. */\n unichar: typeof helpers.unicharT;\n /** Descriptor for an opaque `gpointer` argument, taken from a typed array's memory or a numeric address. */\n buffer: typeof helpers.bufferT;\n /**\n * Builds a descriptor for a C string, whose optional length sizes the caller-allocated buffer\n * used when the string is passed by reference.\n */\n string: typeof helpers.stringT;\n /** Builds a descriptor for a `GObject`, wrapped in the class registered for its runtime GType. */\n object: typeof helpers.objectT;\n /** Builds a descriptor for a `GBoxed` value of the named type. */\n boxed: typeof helpers.boxedT;\n /** Builds a descriptor for a plain C struct. */\n struct: typeof helpers.structT;\n /** Builds a descriptor for a fundamental type whose lifetime is managed by named ref and unref functions. */\n fundamental: typeof helpers.fundamentalT;\n /** Wraps a descriptor in a pointer to it, for an output or inout argument. */\n ref: typeof helpers.refT;\n /** Builds a descriptor for a `GHashTable`, marshalled as an array of key/value pairs. */\n hashTable: typeof helpers.hashTableT;\n /** Builds a descriptor for an enumeration, resolving its GType from the named `get_type` function. */\n enum: typeof helpers.enumT;\n /** Builds a descriptor for a flags type, resolving its GType from the named `get_type` function. */\n flags: typeof helpers.flagsT;\n /** Builds a descriptor for an array of items in one of the supported container layouts. */\n array: typeof helpers.arrayT;\n /** Builds a descriptor for a `GList` of items. */\n list: typeof helpers.listT;\n /** Builds a descriptor for a `GSList` of items. */\n slist: typeof helpers.slistT;\n /** Builds a descriptor for a `GPtrArray` of items. */\n ptrArray: typeof helpers.ptrArrayT;\n /** Builds a descriptor for a `GArray` of items. */\n gArray: typeof helpers.gArrayT;\n /** Builds a descriptor for a `GByteArray`. */\n byteArray: typeof helpers.byteArrayT;\n /** Builds a descriptor for a C array whose length is carried by another argument. */\n sizedArray: typeof helpers.sizedArrayT;\n /** Builds a descriptor for a C array of a fixed length. */\n fixedArray: typeof helpers.fixedArrayT;\n /** Builds a descriptor for an out pointer into the buffer another argument supplied. */\n cursorArray: typeof helpers.cursorArrayT;\n /** Builds a descriptor for a function pointer, marshalling a JavaScript function into a native closure. */\n callback: typeof helpers.callbackT;\n /**\n * Binds a native function, wiring up argument directions, `GError` checking, and packing output\n * arguments into the result.\n */\n fn: typeof fn;\n};\n\n/**\n * Type descriptor builder: a collection of factory helpers that describe C types for\n * FFI marshalling, plus `bind` to bind a native function and `fn` to describe a\n * function signature.\n */\nconst t: T = {\n bind,\n int8: helpers.int8T,\n uint8: helpers.uint8T,\n int16: helpers.int16T,\n uint16: helpers.uint16T,\n int32: helpers.int32T,\n uint32: helpers.uint32T,\n int64: helpers.int64T,\n uint64: helpers.uint64T,\n bigint64: helpers.bigint64T,\n biguint64: helpers.biguint64T,\n gtype: helpers.gtypeT,\n float32: helpers.float32T,\n float64: helpers.float64T,\n boolean: helpers.booleanT,\n void: helpers.voidT,\n unichar: helpers.unicharT,\n buffer: helpers.bufferT,\n string: helpers.stringT,\n object: helpers.objectT,\n boxed: helpers.boxedT,\n struct: helpers.structT,\n fundamental: helpers.fundamentalT,\n ref: helpers.refT,\n hashTable: helpers.hashTableT,\n enum: helpers.enumT,\n flags: helpers.flagsT,\n array: helpers.arrayT,\n list: helpers.listT,\n slist: helpers.slistT,\n ptrArray: helpers.ptrArrayT,\n gArray: helpers.gArrayT,\n byteArray: helpers.byteArrayT,\n sizedArray: helpers.sizedArrayT,\n fixedArray: helpers.fixedArrayT,\n cursorArray: helpers.cursorArrayT,\n callback: helpers.callbackT,\n fn,\n};\n\nexport { t };\n"]}
1
+ {"version":3,"file":"t.js","sourceRoot":"","sources":["../src/t.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,KAAK,OAAO,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAkG7B;;;;GAIG;AACH,MAAM,CAAC,GAAM;IACT,IAAI;IACJ,IAAI,EAAE,OAAO,CAAC,KAAK;IACnB,KAAK,EAAE,OAAO,CAAC,MAAM;IACrB,KAAK,EAAE,OAAO,CAAC,MAAM;IACrB,MAAM,EAAE,OAAO,CAAC,OAAO;IACvB,KAAK,EAAE,OAAO,CAAC,MAAM;IACrB,MAAM,EAAE,OAAO,CAAC,OAAO;IACvB,KAAK,EAAE,OAAO,CAAC,MAAM;IACrB,MAAM,EAAE,OAAO,CAAC,OAAO;IACvB,QAAQ,EAAE,OAAO,CAAC,SAAS;IAC3B,SAAS,EAAE,OAAO,CAAC,UAAU;IAC7B,KAAK,EAAE,OAAO,CAAC,MAAM;IACrB,OAAO,EAAE,OAAO,CAAC,QAAQ;IACzB,OAAO,EAAE,OAAO,CAAC,QAAQ;IACzB,OAAO,EAAE,OAAO,CAAC,QAAQ;IACzB,IAAI,EAAE,OAAO,CAAC,KAAK;IACnB,OAAO,EAAE,OAAO,CAAC,QAAQ;IACzB,MAAM,EAAE,OAAO,CAAC,OAAO;IACvB,MAAM,EAAE,OAAO,CAAC,OAAO;IACvB,MAAM,EAAE,OAAO,CAAC,OAAO;IACvB,KAAK,EAAE,OAAO,CAAC,MAAM;IACrB,MAAM,EAAE,OAAO,CAAC,OAAO;IACvB,WAAW,EAAE,OAAO,CAAC,YAAY;IACjC,GAAG,EAAE,OAAO,CAAC,IAAI;IACjB,SAAS,EAAE,OAAO,CAAC,UAAU;IAC7B,IAAI,EAAE,OAAO,CAAC,KAAK;IACnB,KAAK,EAAE,OAAO,CAAC,MAAM;IACrB,KAAK,EAAE,OAAO,CAAC,MAAM;IACrB,IAAI,EAAE,OAAO,CAAC,KAAK;IACnB,KAAK,EAAE,OAAO,CAAC,MAAM;IACrB,QAAQ,EAAE,OAAO,CAAC,SAAS;IAC3B,MAAM,EAAE,OAAO,CAAC,OAAO;IACvB,SAAS,EAAE,OAAO,CAAC,UAAU;IAC7B,UAAU,EAAE,OAAO,CAAC,WAAW;IAC/B,UAAU,EAAE,OAAO,CAAC,WAAW;IAC/B,WAAW,EAAE,OAAO,CAAC,YAAY;IACjC,QAAQ,EAAE,OAAO,CAAC,SAAS;IAC3B,EAAE;IACF,KAAK;IACL,OAAO;CACV,CAAC;AAEF,OAAO,EAAE,CAAC,EAAE,CAAC","sourcesContent":["import { bind } from \"./bind.js\";\nimport * as helpers from \"./descriptors.js\";\nimport { field, fieldAt } from \"./field.js\";\nimport { fn } from \"./fn.js\";\n\n/** The descriptor factories and function binders exposed as {@link t}. */\ntype T = {\n /** Binds a symbol in a shared library to a callable that marshals its arguments and return value. */\n bind: typeof bind;\n /** Descriptor for a `gint8`, marshalled as a number. */\n int8: typeof helpers.int8T;\n /** Descriptor for a `guint8`, marshalled as a number. */\n uint8: typeof helpers.uint8T;\n /** Descriptor for a `gint16`, marshalled as a number. */\n int16: typeof helpers.int16T;\n /** Descriptor for a `guint16`, marshalled as a number. */\n uint16: typeof helpers.uint16T;\n /** Descriptor for a `gint32`, marshalled as a number. */\n int32: typeof helpers.int32T;\n /** Descriptor for a `guint32`, marshalled as a number. */\n uint32: typeof helpers.uint32T;\n /** Descriptor for a `gint64`, marshalled as a number and rejected outside the 2^53 safe range. */\n int64: typeof helpers.int64T;\n /** Descriptor for a `guint64`, marshalled as a number and rejected outside the 2^53 safe range. */\n uint64: typeof helpers.uint64T;\n /** Descriptor for a `gint64`, marshalled as a bigint so the full 64-bit range survives. */\n bigint64: typeof helpers.bigint64T;\n /** Descriptor for a `guint64`, marshalled as a bigint so the full 64-bit range survives. */\n biguint64: typeof helpers.biguint64T;\n /** Descriptor for a `GType`, marshalled as a bigint and recognized as a GType by GValue conversion. */\n gtype: typeof helpers.gtypeT;\n /** Descriptor for a `gfloat`. */\n float32: typeof helpers.float32T;\n /** Descriptor for a `gdouble`. */\n float64: typeof helpers.float64T;\n /** Descriptor for a `gboolean`, marshalled as a JavaScript boolean. */\n boolean: typeof helpers.booleanT;\n /** Descriptor for the absence of a value, used as the return descriptor of a `void` function. */\n void: typeof helpers.voidT;\n /** Descriptor for a `gunichar`, marshalled as a single-character string or a codepoint number. */\n unichar: typeof helpers.unicharT;\n /** Descriptor for an opaque `gpointer` argument, taken from a typed array's memory or a numeric address. */\n buffer: typeof helpers.bufferT;\n /**\n * Builds a descriptor for a C string, whose optional length sizes the caller-allocated buffer\n * used when the string is passed by reference.\n */\n string: typeof helpers.stringT;\n /** Builds a descriptor for a `GObject`, wrapped in the class registered for its runtime GType. */\n object: typeof helpers.objectT;\n /** Builds a descriptor for a `GBoxed` value of the named type. */\n boxed: typeof helpers.boxedT;\n /** Builds a descriptor for a plain C struct. */\n struct: typeof helpers.structT;\n /** Builds a descriptor for a fundamental type whose lifetime is managed by named ref and unref functions. */\n fundamental: typeof helpers.fundamentalT;\n /** Wraps a descriptor in a pointer to it, for an output or inout argument. */\n ref: typeof helpers.refT;\n /** Builds a descriptor for a `GHashTable`, marshalled as an array of key/value pairs. */\n hashTable: typeof helpers.hashTableT;\n /** Builds a descriptor for an enumeration, resolving its GType from the named `get_type` function. */\n enum: typeof helpers.enumT;\n /** Builds a descriptor for a flags type, resolving its GType from the named `get_type` function. */\n flags: typeof helpers.flagsT;\n /** Builds a descriptor for an array of items in one of the supported container layouts. */\n array: typeof helpers.arrayT;\n /** Builds a descriptor for a `GList` of items. */\n list: typeof helpers.listT;\n /** Builds a descriptor for a `GSList` of items. */\n slist: typeof helpers.slistT;\n /** Builds a descriptor for a `GPtrArray` of items. */\n ptrArray: typeof helpers.ptrArrayT;\n /** Builds a descriptor for a `GArray` of items. */\n gArray: typeof helpers.gArrayT;\n /** Builds a descriptor for a `GByteArray`. */\n byteArray: typeof helpers.byteArrayT;\n /** Builds a descriptor for a C array whose length is carried by another argument. */\n sizedArray: typeof helpers.sizedArrayT;\n /** Builds a descriptor for a C array of a fixed length. */\n fixedArray: typeof helpers.fixedArrayT;\n /** Builds a descriptor for an out pointer into the buffer another argument supplied. */\n cursorArray: typeof helpers.cursorArrayT;\n /** Builds a descriptor for a function pointer, marshalling a JavaScript function into a native closure. */\n callback: typeof helpers.callbackT;\n /**\n * Binds a native function, wiring up argument directions, `GError` checking, and packing output\n * arguments into the result.\n */\n fn: typeof fn;\n /**\n * Binds a struct field at a fixed offset, compiling its descriptor once into an accessor that\n * reads and writes it.\n */\n field: typeof field;\n /**\n * Binds a struct field whose offset is supplied per access, for walking records stored at a\n * stride in a buffer.\n */\n fieldAt: typeof fieldAt;\n};\n\n/**\n * Type descriptor builder: a collection of factory helpers that describe C types for\n * FFI marshalling, plus `bind` to bind a native function and `fn` to describe a\n * function signature.\n */\nconst t: T = {\n bind,\n int8: helpers.int8T,\n uint8: helpers.uint8T,\n int16: helpers.int16T,\n uint16: helpers.uint16T,\n int32: helpers.int32T,\n uint32: helpers.uint32T,\n int64: helpers.int64T,\n uint64: helpers.uint64T,\n bigint64: helpers.bigint64T,\n biguint64: helpers.biguint64T,\n gtype: helpers.gtypeT,\n float32: helpers.float32T,\n float64: helpers.float64T,\n boolean: helpers.booleanT,\n void: helpers.voidT,\n unichar: helpers.unicharT,\n buffer: helpers.bufferT,\n string: helpers.stringT,\n object: helpers.objectT,\n boxed: helpers.boxedT,\n struct: helpers.structT,\n fundamental: helpers.fundamentalT,\n ref: helpers.refT,\n hashTable: helpers.hashTableT,\n enum: helpers.enumT,\n flags: helpers.flagsT,\n array: helpers.arrayT,\n list: helpers.listT,\n slist: helpers.slistT,\n ptrArray: helpers.ptrArrayT,\n gArray: helpers.gArrayT,\n byteArray: helpers.byteArrayT,\n sizedArray: helpers.sizedArrayT,\n fixedArray: helpers.fixedArrayT,\n cursorArray: helpers.cursorArrayT,\n callback: helpers.callbackT,\n fn,\n field,\n fieldAt,\n};\n\nexport { t };\n"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@gtkx/runtime",
3
- "version": "1.3.0",
4
- "description": "FFI runtime the generated GTKX bindings call into: type descriptors, GValue marshalling, signals, GObject lifecycle",
3
+ "version": "1.5.0",
4
+ "description": "FFI runtime for the generated GTK bindings: types, GValues, signals.",
5
5
  "keywords": [
6
6
  "gtkx",
7
7
  "gtk",
@@ -55,11 +55,11 @@
55
55
  "node": ">=24"
56
56
  },
57
57
  "dependencies": {
58
- "@gtkx/utils": "1.3.0",
59
- "@gtkx/native": "1.3.0"
58
+ "@gtkx/native": "1.5.0",
59
+ "@gtkx/utils": "1.5.0"
60
60
  },
61
61
  "devDependencies": {
62
- "@gtkx/vitest": "1.3.0"
62
+ "@gtkx/vitest": "1.5.0"
63
63
  },
64
64
  "scripts": {
65
65
  "release": "tsx ../../scripts/release-package.ts"
package/src/field.ts ADDED
@@ -0,0 +1,76 @@
1
+ import {
2
+ bindField,
3
+ type Descriptor,
4
+ type ExternalObject,
5
+ type FieldDescriptor,
6
+ type Handle,
7
+ readField,
8
+ writeField,
9
+ } from "@gtkx/native";
10
+
11
+ /**
12
+ * A field of a native struct at a fixed offset, bound once against the descriptor its bytes are
13
+ * marshalled through, so the descriptor is not parsed again on access.
14
+ */
15
+ type Field = {
16
+ /** Reads and decodes the field out of `handle`'s memory. */
17
+ read: (handle: ExternalObject<Handle>) => unknown;
18
+ /** Encodes `value` and writes it into the field of `handle`'s memory. */
19
+ write: (handle: ExternalObject<Handle>, value: unknown) => void;
20
+ };
21
+
22
+ /**
23
+ * A field of a native struct whose offset is supplied per access, bound once against the
24
+ * descriptor its bytes are marshalled through, for walking records stored at a stride.
25
+ */
26
+ type StridedField = {
27
+ /** Reads and decodes the field `offset` bytes into `handle`'s memory. */
28
+ read: (handle: ExternalObject<Handle>, offset: number) => unknown;
29
+ /** Encodes `value` and writes it `offset` bytes into `handle`'s memory. */
30
+ write: (handle: ExternalObject<Handle>, offset: number, value: unknown) => void;
31
+ };
32
+
33
+ /**
34
+ * Binds a struct field whose offset is only known per access, compiling `descriptor` once into an
35
+ * accessor that reads and writes it wherever it is pointed. Use it to walk records stored one
36
+ * after another in a buffer; {@link field} is the one to reach for when the offset is fixed.
37
+ *
38
+ * @param descriptor Describes how the field's bytes are marshalled.
39
+ * @returns An accessor reading and writing that field at any offset of any handle it is given.
40
+ */
41
+ const fieldAt = (descriptor: Descriptor): StridedField => {
42
+ const bound: ExternalObject<FieldDescriptor> = bindField(descriptor);
43
+
44
+ return {
45
+ read: (handle, offset) => readField(bound, handle, offset),
46
+ write: (handle, offset, value) => {
47
+ writeField(bound, handle, offset, value);
48
+ },
49
+ };
50
+ };
51
+
52
+ /**
53
+ * Binds a struct field at a fixed offset, compiling `descriptor` once into an accessor that reads
54
+ * and writes it. The `read` and `write` functions compile the descriptor on every call instead, so
55
+ * they remain the ones to reach for when a descriptor is only known per access.
56
+ *
57
+ * @param descriptor Describes how the field's bytes are marshalled.
58
+ * @param offset Byte offset of the field within its owner's memory.
59
+ * @returns An accessor reading and writing that field of any handle it is given.
60
+ */
61
+ const field = (descriptor: Descriptor, offset: number): Field => {
62
+ if (!Number.isSafeInteger(offset) || offset < 0) {
63
+ throw new RangeError(`A field offset must be a safe whole byte count, got ${String(offset)}`);
64
+ }
65
+
66
+ const bound = fieldAt(descriptor);
67
+
68
+ return {
69
+ read: (handle) => bound.read(handle, offset),
70
+ write: (handle, value) => {
71
+ bound.write(handle, offset, value);
72
+ },
73
+ };
74
+ };
75
+
76
+ export { field, type Field, fieldAt, type StridedField };
package/src/index.ts CHANGED
@@ -7,6 +7,8 @@ export { type ClosureCallback, ClosureMarshalError, toClosure, tryToClosure } fr
7
7
  /** @public */
8
8
  export { createErrorDomain, type ErrorDomain } from "./error.js";
9
9
  /** @public */
10
+ export { type Field, type StridedField } from "./field.js";
11
+ /** @public */
10
12
  export { onExit, quit, quitApplication, runApplication, type RunApplicationResult } from "./lifecycle.js";
11
13
  /** @public */
12
14
  export { offSignal, onceSignal, onSignal } from "./listeners.js";
@@ -28,7 +30,12 @@ export { getParamSpecFlags, getParamSpecOwnerType, getParamSpecValueType } from
28
30
  /** @public */
29
31
  export { promisify, trimFinish } from "./promisify.js";
30
32
  /** @public */
31
- export { coerceObjectProperty, newParamSpecOverride } from "./properties.js";
33
+ export {
34
+ coerceObjectProperty,
35
+ getDeclaredPropertyName,
36
+ isReadableProperty,
37
+ newParamSpecOverride,
38
+ } from "./properties.js";
32
39
  /** @public */
33
40
  export { matchAllRegex, matchRegex } from "./regex.js";
34
41
  /** @public */
@@ -50,7 +57,14 @@ export {
50
57
  type WrapperClassResolver,
51
58
  } from "./registry.js";
52
59
  /** @public */
53
- export { connectSignal, disconnectSignal, emitSignal, getSignalBaseName, type SignalHandler } from "./signal.js";
60
+ export {
61
+ connectSignal,
62
+ disconnectSignal,
63
+ emitSignal,
64
+ getSignalBaseName,
65
+ type SignalHandler,
66
+ signalForHandlerName,
67
+ } from "./signal.js";
54
68
  /** @public */
55
69
  export { t } from "./t.js";
56
70
  /** @public */
package/src/param-spec.ts CHANGED
@@ -154,6 +154,7 @@ const getParamSpecOwnerType = (spec: object): bigint => {
154
154
  };
155
155
 
156
156
  const isParamWritable = (flags: number): boolean => (flags & PARAM_WRITABLE) !== 0;
157
+ const isParamReadable = (flags: number): boolean => (flags & PARAM_READABLE) !== 0;
157
158
  const isParamConstructOnly = (flags: number): boolean => (flags & PARAM_CONSTRUCT_ONLY) !== 0;
158
159
  const isParamLaxlyValidated = (flags: number): boolean => (flags & PARAM_LAX_VALIDATION) !== 0;
159
160
  const isParamExplicitlyNotified = (flags: number): boolean => (flags & PARAM_EXPLICIT_NOTIFY) !== 0;
@@ -308,6 +309,7 @@ export {
308
309
  INT64_MAXIMUM,
309
310
  INT64_MINIMUM,
310
311
  isParamConstructOnly,
312
+ isParamReadable,
311
313
  isStringArray,
312
314
  isParamExplicitlyNotified,
313
315
  isParamLaxlyValidated,
package/src/properties.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import type { ExternalObject, Handle, RegisterClassProperty } from "@gtkx/native";
2
2
  import type { AnyClass } from "@gtkx/utils";
3
- import { camelCase, kebabCase } from "@gtkx/utils";
3
+ import { camelCase, kebabCase, toCamelIdentifier } from "@gtkx/utils";
4
4
  import { bind } from "./bind.js";
5
- import { biguint64T, fundamentalT, stringT, structT, voidT } from "./descriptors.js";
5
+ import { biguint64T, fundamentalT, refT, sizedArrayT, stringT, structT, uint32T, voidT } from "./descriptors.js";
6
6
  import { LIB, PARAM_T, VALUE_T } from "./library.js";
7
7
  import {
8
8
  getParamFlags,
@@ -10,6 +10,7 @@ import {
10
10
  isParamConstructOnly,
11
11
  isParamExplicitlyNotified,
12
12
  isParamLaxlyValidated,
13
+ isParamReadable,
13
14
  isParamWritable,
14
15
  type ValueGuard,
15
16
  valueGuardFor,
@@ -47,14 +48,19 @@ type PropertyCheck = {
47
48
  scratch?: ExternalObject<Handle>;
48
49
  };
49
50
 
50
- type PropertyAccessor = PropertyCheck & {
51
- memberName: string;
51
+ type AccessorBase = PropertyCheck & {
52
52
  storage: symbol;
53
- hasGeneratedMember: boolean;
54
- isInterfaceProperty?: boolean;
55
53
  delegate?: InterfaceProperty;
56
54
  };
57
55
 
56
+ type DeclaredAccessor = AccessorBase & {
57
+ isInterfaceProperty?: false;
58
+ memberName: string;
59
+ hasGeneratedMember: boolean;
60
+ };
61
+
62
+ type InterfaceAccessor = AccessorBase & { isInterfaceProperty: true };
63
+ type PropertyAccessor = DeclaredAccessor | InterfaceAccessor;
58
64
  /** A `GObject.ParamSpec` wrapper describing one property's name, type, flags and default. */
59
65
  type PropertySpec = object;
60
66
 
@@ -82,6 +88,8 @@ const SET_PROPERTY_VFUNC = "vfuncSetProperty";
82
88
  const READ_ONLY_REASON = "the property is read-only";
83
89
  const CONSTRUCT_ONLY_REASON = "the property can only be set when the object is constructed";
84
90
  const CLASS_T = structT("borrowed");
91
+ const CANONICAL_NAME_PATTERN = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;
92
+ const DECLARED_NAMES = Symbol("gtkx:declaredPropertyNames");
85
93
 
86
94
  const OVERRIDE_PARAM_T = fundamentalT(LIB, "g_param_spec_ref_sink", "g_param_spec_unref", {
87
95
  ownership: "borrowed",
@@ -95,6 +103,14 @@ const typeClassRef = bind(LIB, "g_type_class_ref", [biguint64T], CLASS_T);
95
103
  const typeClassUnref = bind(LIB, "g_type_class_unref", [CLASS_T], voidT);
96
104
  const coercionChecks: Map<bigint, Map<string, PropertyCheck | null>> = new Map();
97
105
  const classFindProperty = bind(LIB, "g_object_class_find_property", [CLASS_T, stringT("borrowed")], PARAM_T);
106
+
107
+ const classListProperties = bind(
108
+ LIB,
109
+ "g_object_class_list_properties",
110
+ [CLASS_T, refT(uint32T)],
111
+ sizedArrayT(PARAM_T, 1, "full"),
112
+ );
113
+
98
114
  const defaultInterfaceRef = bind(LIB, "g_type_default_interface_ref", [biguint64T], CLASS_T);
99
115
  const defaultInterfaceUnref = bind(LIB, "g_type_default_interface_unref", [CLASS_T], voidT);
100
116
  const interfaceFindProperty = bind(LIB, "g_object_interface_find_property", [CLASS_T, stringT("borrowed")], PARAM_T);
@@ -128,10 +144,22 @@ function checkFor(handle: ExternalObject<Handle>, name: string): PropertyCheck {
128
144
  };
129
145
  }
130
146
 
147
+ function accessorSpecs(klass: ExternalObject<Handle>): Map<string, ExternalObject<Handle>> {
148
+ const specs: Map<string, ExternalObject<Handle>> = new Map();
149
+ const countRef = { value: 0 };
150
+
151
+ for (const pspec of classListProperties(klass, countRef) as ExternalObject<Handle>[]) {
152
+ specs.set(toCamelIdentifier(paramSpecName(pspec) as string), pspec);
153
+ }
154
+
155
+ return specs;
156
+ }
157
+
131
158
  function findPropertySpec(klass: ExternalObject<Handle>, name: string): ExternalObject<Handle> | null {
132
- const found = classFindProperty(klass, name) as ExternalObject<Handle> | null;
159
+ const found = (classFindProperty(klass, name) ??
160
+ classFindProperty(klass, canonicalCase(name))) as ExternalObject<Handle> | null;
133
161
 
134
- return found ?? (classFindProperty(klass, canonicalCase(name)) as ExternalObject<Handle> | null);
162
+ return found ?? accessorSpecs(klass).get(toCamelIdentifier(name)) ?? null;
135
163
  }
136
164
 
137
165
  const sourceLabel = (source: bigint | AnyClass, gtype: bigint): string =>
@@ -290,6 +318,22 @@ function constructValueFor(wrapper: object, check: PropertyCheck, value: unknown
290
318
  return { name: check.propertyName, value: checkedValueFor(wrapper, check, value) };
291
319
  }
292
320
 
321
+ /**
322
+ * Whether the type declares a readable GObject property under `propertyName`, asking GObject itself
323
+ * rather than looking for a generated accessor, which codegen omits when a method shares the name.
324
+ */
325
+ function isReadableProperty(gtype: bigint, propertyName: string): boolean {
326
+ const klass = typeClassRef(gtype) as ExternalObject<Handle>;
327
+
328
+ try {
329
+ const pspec = findPropertySpec(klass, propertyName);
330
+
331
+ return pspec !== null && isParamReadable(getParamFlags(pspec));
332
+ } finally {
333
+ typeClassUnref(klass);
334
+ }
335
+ }
336
+
293
337
  function lookupCoercionCheck(gtype: bigint, name: string): PropertyCheck | null {
294
338
  const klass = typeClassRef(gtype) as ExternalObject<Handle>;
295
339
 
@@ -436,13 +480,13 @@ function storedGetter(accessor: PropertyAccessor): (this: object) => unknown {
436
480
  };
437
481
  }
438
482
 
439
- function memberGetter(accessor: PropertyAccessor): (this: object) => unknown {
483
+ function memberGetter(accessor: DeclaredAccessor): (this: object) => unknown {
440
484
  return function get(this: object) {
441
485
  return (this as Record<string, unknown>)[accessor.memberName];
442
486
  };
443
487
  }
444
488
 
445
- function memberSetter(accessor: PropertyAccessor): (this: object, value: unknown) => void {
489
+ function memberSetter(accessor: DeclaredAccessor): (this: object, value: unknown) => void {
446
490
  return function set(this: object, value: unknown) {
447
491
  (this as Record<string, unknown>)[accessor.memberName] = value;
448
492
  };
@@ -454,7 +498,7 @@ function refusalFor(accessor: PropertyAccessor, reason: string): (this: object,
454
498
  };
455
499
  }
456
500
 
457
- function checkedSetter(accessor: PropertyAccessor): (this: object, value: unknown) => void {
501
+ function checkedSetter(accessor: DeclaredAccessor): (this: object, value: unknown) => void {
458
502
  if (!isParamWritable(accessor.flags)) {
459
503
  return refusalFor(accessor, READ_ONLY_REASON);
460
504
  }
@@ -480,7 +524,7 @@ function defineAccessor(prototype: object, descriptor: PropertyDescriptor, alias
480
524
  Object.defineProperty(prototype, alias, descriptor);
481
525
  }
482
526
 
483
- function installAccessors(klass: AnyClass, accessor: PropertyAccessor): void {
527
+ function installAccessors(klass: AnyClass, accessor: DeclaredAccessor): void {
484
528
  const prototype = (klass as { prototype: object }).prototype;
485
529
  accessor.hasGeneratedMember = !hasOwnMember(prototype, accessor.memberName);
486
530
 
@@ -509,16 +553,14 @@ function storageFor(propertyName: string): symbol {
509
553
  return created;
510
554
  }
511
555
 
512
- function interfaceAccessorFor(dispatch: PropertyDispatch, pspec: PropertySpec): PropertyAccessor {
556
+ function interfaceAccessorFor(dispatch: PropertyDispatch, pspec: PropertySpec): InterfaceAccessor {
513
557
  const handle = getHandle(pspec);
514
558
  const propertyName = paramSpecName(handle) as string;
515
559
  const delegate = dispatch.delegates.get(propertyName);
516
560
 
517
- const accessor: PropertyAccessor = {
561
+ const accessor: InterfaceAccessor = {
518
562
  ...checkFor(handle, propertyName),
519
- memberName: camelCase(propertyName),
520
563
  storage: storageFor(propertyName),
521
- hasGeneratedMember: false,
522
564
  isInterfaceProperty: true,
523
565
  };
524
566
 
@@ -556,7 +598,7 @@ function callMember(instance: object, member: string, args: unknown[]): unknown
556
598
  return (fn as (...values: unknown[]) => unknown).apply(instance, args);
557
599
  }
558
600
 
559
- function backingMemberFor(instance: object, accessor: PropertyAccessor): string | undefined {
601
+ function backingMemberFor(instance: object, accessor: DeclaredAccessor): string | undefined {
560
602
  if (accessor.hasGeneratedMember && !Object.hasOwn(instance, accessor.memberName)) {
561
603
  return undefined;
562
604
  }
@@ -632,16 +674,17 @@ function makeSetProperty(dispatch: PropertyDispatch) {
632
674
  };
633
675
  }
634
676
 
635
- function assertCanonicalName(klass: AnyClass, name: string, propertyName: string): void {
636
- const canonical = canonicalCase(name);
677
+ const isCanonicalName = (name: string, propertyName: string): boolean =>
678
+ CANONICAL_NAME_PATTERN.test(propertyName) && toCamelIdentifier(propertyName) === toCamelIdentifier(name);
637
679
 
638
- if (propertyName === canonical) {
680
+ function assertCanonicalName(klass: AnyClass, name: string, propertyName: string): void {
681
+ if (isCanonicalName(name, propertyName)) {
639
682
  return;
640
683
  }
641
684
 
642
685
  throw new TypeError(
643
686
  `registerClass: ${klass.name} keys the property '${name}' to a GObject.ParamSpec named ` +
644
- `'${propertyName}', which is the name GObject notifies under; name the ParamSpec '${canonical}'`,
687
+ `'${propertyName}', which is the name GObject notifies under; name the ParamSpec '${canonicalCase(name)}'`,
645
688
  );
646
689
  }
647
690
 
@@ -651,8 +694,8 @@ function assertCanonicalNames(klass: AnyClass, properties: Record<string, Proper
651
694
  }
652
695
  }
653
696
 
654
- function buildAccessor(klass: AnyClass, name: string, pspec: PropertySpec): PropertyAccessor {
655
- const accessor: PropertyAccessor = {
697
+ function buildAccessor(klass: AnyClass, name: string, pspec: PropertySpec): DeclaredAccessor {
698
+ const accessor: DeclaredAccessor = {
656
699
  ...checkFor(getHandle(pspec), name),
657
700
  memberName: camelCase(name),
658
701
  storage: Symbol(`gtkx:property:${name}`),
@@ -664,7 +707,7 @@ function buildAccessor(klass: AnyClass, name: string, pspec: PropertySpec): Prop
664
707
  return accessor;
665
708
  }
666
709
 
667
- function buildAccessors(source: PropertyDispatchSource): PropertyAccessor[] {
710
+ function buildAccessors(source: PropertyDispatchSource): DeclaredAccessor[] {
668
711
  const { klass, properties } = source;
669
712
  assertCanonicalNames(klass, properties);
670
713
 
@@ -691,11 +734,35 @@ function interfaceDelegatesFor(adoptedTypes: bigint[]): Map<string, InterfacePro
691
734
  return delegates;
692
735
  }
693
736
 
737
+ function recordDeclaredNames(klass: AnyClass, accessors: DeclaredAccessor[]): void {
738
+ const proto = klass.prototype as Record<PropertyKey, unknown>;
739
+ const inherited = proto[DECLARED_NAMES] as Record<string, string> | undefined;
740
+ const own: Record<string, string> = {};
741
+
742
+ for (const accessor of accessors) {
743
+ own[accessor.memberName] = accessor.propertyName;
744
+ }
745
+
746
+ Object.defineProperty(proto, DECLARED_NAMES, { value: { ...inherited, ...own }, enumerable: false });
747
+ }
748
+
694
749
  function buildPropertyDispatch(source: PropertyDispatchSource): PropertyDispatch {
695
- return {
696
- accessors: buildAccessors(source),
697
- delegates: interfaceDelegatesFor(source.adoptedTypes),
698
- };
750
+ const declared = buildAccessors(source);
751
+ recordDeclaredNames(source.klass, declared);
752
+
753
+ return { accessors: [...declared], delegates: interfaceDelegatesFor(source.adoptedTypes) };
754
+ }
755
+
756
+ /**
757
+ * The GObject name a class registered with `registerClass` installed an accessor under, or
758
+ * `undefined` when the accessor names no property the class declared itself.
759
+ */
760
+ function getDeclaredPropertyName(object: object, accessor: string): string | undefined {
761
+ const declared = (object as Record<PropertyKey, unknown>)[DECLARED_NAMES] as
762
+ | Record<string, string> |
763
+ undefined;
764
+
765
+ return declared?.[accessor];
699
766
  }
700
767
 
701
768
  function toNativeProperties(properties: Record<string, PropertySpec>): RegisterClassProperty[] {
@@ -708,6 +775,8 @@ function toNativeProperties(properties: Record<string, PropertySpec>): RegisterC
708
775
  export {
709
776
  buildPropertyDispatch,
710
777
  coerceObjectProperty,
778
+ getDeclaredPropertyName,
779
+ isReadableProperty,
711
780
  coercePropertyValue,
712
781
  constructPropertyFor,
713
782
  GET_PROPERTY_VFUNC,
@@ -35,7 +35,9 @@ import {
35
35
  emitDeclaredSignal,
36
36
  getSignalBaseName,
37
37
  overrideSignalClassClosure,
38
+ signalForHandlerName,
38
39
  type SignalHandler,
40
+ signalIdFor,
39
41
  } from "./signal.js";
40
42
  import {
41
43
  TYPE_INTERFACE,
@@ -251,10 +253,15 @@ type RegisterClassOptions<
251
253
  * Properties to install on the new type, keyed by the name JavaScript addresses each one by and
252
254
  * valued with the `GObject.ParamSpec` describing it. A key is read in camelCase however it is
253
255
  * written, so `dewPoint`, `dew_point` and `dew-point` all name the same member, and the ParamSpec
254
- * has to carry the canonical spelling of that name, `dew-point`, or registration throws: the
255
- * ParamSpec's name is the one GObject emits `notify` with, and a name the key does not spell
256
- * reaches nothing that listens for it. Every property gains prototype accessors, one for the key
257
- * as written, one for it with dashes turned into underscores and one for it in camelCase, each
256
+ * has to carry a name that reads back as that same member, such as `dew-point`, or registration
257
+ * throws: the ParamSpec's name is the one GObject emits `notify` with, and a name the key does
258
+ * not spell reaches nothing that listens for it. A word starting with a digit is a word of its
259
+ * own on either side of that reading, so `level2Depth` takes a ParamSpec named `level-2-depth`
260
+ * as readily as one named `level2-depth`, the way `WebKit.Settings` names
261
+ * `enable-2d-canvas-acceleration` for its `enable2dCanvasAcceleration` member. An uppercase
262
+ * letter in the ParamSpec's own name is refused, since GObject notifies under that spelling
263
+ * and nothing else reads it back. Every property gains prototype accessors, one for the key as
264
+ * written, one for it with dashes turned into underscores and one for it in camelCase, each
258
265
  * unless the class already defines that name. They serve the value from storage of their own on
259
266
  * the instance, which is also what the type's `get_property` and `set_property` slots read and
260
267
  * write, so a value set from JavaScript, from `g_object_set_property` and at construction all
@@ -305,9 +312,11 @@ type RegisterClassOptions<
305
312
  properties?: TProperties;
306
313
  /**
307
314
  * Signals to create on the new type, keyed by signal name and valued with the
308
- * {@link SignalSpec} describing each one. A name has to start with a letter, continue in
309
- * letters, digits, `-` and `_`, and be new to the type: one an ancestor type or a listed
310
- * interface already carries throws. The two word separators spell the same signal, so a
315
+ * {@link SignalSpec} describing each one. A name has to start with a lowercase letter, continue
316
+ * in lowercase letters, digits, `-` and `_`, and be new to the type: one an ancestor type or a
317
+ * listed interface already carries throws, and so does one carrying an uppercase letter, which
318
+ * GObject would carry under that exact spelling, out of reach of both its dashed spelling and
319
+ * its `on<SignalName>` default handler. The two word separators spell the same signal, so a
311
320
  * signal declared as `data_changed` is connected to and emitted as `data-changed` too.
312
321
  *
313
322
  * Instances connect and emit by name through the same `connect`, `on`, `once`, `off` and
@@ -373,6 +382,7 @@ const PROPERTY_VFUNC_SPECS: PropertyVfuncSpec[] = [
373
382
 
374
383
  const PROPERTY_VFUNC_NAMES: Set<string> = new Set(PROPERTY_VFUNC_SPECS.map((spec) => spec.methodName));
375
384
  const TYPE_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9\-_+]{2,}$/;
385
+ const UPPER_CASE_PATTERN = /[A-Z]/;
376
386
  const SIGNAL_OVERRIDE_PATTERN = /^on[A-Z]/;
377
387
 
378
388
  /**
@@ -387,9 +397,9 @@ const SIGNAL_OVERRIDE_PATTERN = /^on[A-Z]/;
387
397
  * when the list names `Gio.AsyncInitable` as an interface the parent type does not already
388
398
  * implement and no method on the chain fills `vfuncInitAsync`, since the default `init_async`
389
399
  * would run `vfuncInit` on a worker thread, when an entry in
390
- * `RegisterClassOptions.properties` names its `GObject.ParamSpec` something other than the canonical
391
- * spelling of the key it sits under, when an entry in
392
- * `RegisterClassOptions.signals` carries an invalid name, a name the type already knows, a GType
400
+ * `RegisterClassOptions.properties` names its `GObject.ParamSpec` something the key it sits under
401
+ * does not spell, when an entry in `RegisterClassOptions.signals` carries an invalid name, a name
402
+ * spelled with an uppercase letter rather than dashed, a name the type already knows, a GType
393
403
  * that cannot hold a value, or an accumulator the spec does not admit, and when
394
404
  * `RegisterClassOptions.cssName` is
395
405
  * given for a class that does not extend `Gtk.Widget`. An exception thrown by
@@ -769,15 +779,39 @@ function buildPropertyVfunc(
769
779
 
770
780
  const canonicalSignalName = (name: string): string => name.replaceAll("_", "-");
771
781
 
782
+ function assertLowerCaseSignalName(klass: AnyClass, name: string): void {
783
+ if (!UPPER_CASE_PATTERN.test(name)) {
784
+ return;
785
+ }
786
+
787
+ throw new TypeError(
788
+ `registerClass: ${klass.name} declares the signal '${name}'; GObject would carry it under that ` +
789
+ "exact spelling, out of reach of both its dashed spelling and its default handler; " +
790
+ `declare it as '${kebabCase(name)}'`,
791
+ );
792
+ }
793
+
794
+ function overriddenSignalId(type: bigint, methodName: string): number {
795
+ if (!SIGNAL_OVERRIDE_PATTERN.test(methodName)) {
796
+ return 0;
797
+ }
798
+
799
+ const signal = signalForHandlerName(type, methodName);
800
+
801
+ return signal === undefined ? 0 : signalIdFor(type, signal);
802
+ }
803
+
772
804
  function installSignalOverrides(newType: bigint, methods: MethodTable): void {
773
805
  for (const [methodName, fn] of methods) {
774
- if (!SIGNAL_OVERRIDE_PATTERN.test(methodName)) {
806
+ const signalId = overriddenSignalId(newType, methodName);
807
+
808
+ if (signalId === 0) {
775
809
  continue;
776
810
  }
777
811
 
778
812
  const handler = fn as (...args: unknown[]) => unknown;
779
813
 
780
- overrideSignalClassClosure(newType, kebabCase(methodName.slice(2)), (...args: unknown[]) =>
814
+ overrideSignalClassClosure(newType, signalId, (...args: unknown[]) =>
781
815
  handler.apply(args[0], args.slice(1)),
782
816
  );
783
817
  }
@@ -844,6 +878,7 @@ function resolveDeclaredSignals(klass: AnyClass, signals: Record<string, SignalS
844
878
  const table: Map<string, DeclaredSignalTypes> = new Map();
845
879
 
846
880
  for (const [name, spec] of Object.entries(signals)) {
881
+ assertLowerCaseSignalName(klass, name);
847
882
  const resolved = resolveDeclaredSignal(klass, name, spec);
848
883
  native.push(resolved.native);
849
884
  table.set(canonicalSignalName(name), resolved.declared);