@gjsify/node-gi 0.64.0 → 0.64.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/cairo.d.ts CHANGED
@@ -17,6 +17,35 @@ export const Operator: CairoEnum;
17
17
  export const PatternType: CairoEnum;
18
18
  export const SurfaceType: CairoEnum;
19
19
 
20
+ /** `cairo_text_extents_t`, in the shape GJS returns. */
21
+ export interface TextExtents {
22
+ xBearing: number;
23
+ yBearing: number;
24
+ width: number;
25
+ height: number;
26
+ xAdvance: number;
27
+ yAdvance: number;
28
+ }
29
+
30
+ /** `cairo_font_extents_t`, in the shape GJS returns. */
31
+ export interface FontExtents {
32
+ ascent: number;
33
+ descent: number;
34
+ height: number;
35
+ maxXAdvance: number;
36
+ maxYAdvance: number;
37
+ }
38
+
39
+ /** `cairo_matrix_t` as the plain object GJS passes around (no matrix wrapper). */
40
+ export interface Matrix {
41
+ xx: number;
42
+ yx: number;
43
+ xy: number;
44
+ yy: number;
45
+ x0: number;
46
+ y0: number;
47
+ }
48
+
20
49
  /** cairo drawing context (`cairo_t`). */
21
50
  export class Context {
22
51
  constructor(surface: Surface);
@@ -28,10 +57,23 @@ export class Context {
28
57
  lineTo(x: number, y: number): void;
29
58
  relMoveTo(dx: number, dy: number): void;
30
59
  relLineTo(dx: number, dy: number): void;
60
+ selectFontFace(family: string, slant: number, weight: number): void;
61
+ setFontSize(size: number): void;
62
+ showText(utf8: string): void;
63
+ textPath(utf8: string): void;
64
+ textExtents(utf8: string): TextExtents;
65
+ fontExtents(): FontExtents;
66
+ /**
67
+ * The current font matrix. GJS has no cairo matrix wrapper
68
+ * (`getMatrix`/`setMatrix` stay unbound), so it travels as a plain object.
69
+ */
70
+ getFontMatrix(): Matrix;
71
+ setFontMatrix(matrix: Matrix): void;
31
72
  rectangle(x: number, y: number, width: number, height: number): void;
32
73
  arc(xc: number, yc: number, radius: number, angle1: number, angle2: number): void;
33
74
  arcNegative(xc: number, yc: number, radius: number, angle1: number, angle2: number): void;
34
75
  curveTo(x1: number, y1: number, x2: number, y2: number, x3: number, y3: number): void;
76
+ relCurveTo(dx1: number, dy1: number, dx2: number, dy2: number, dx3: number, dy3: number): void;
35
77
  fill(): void;
36
78
  fillPreserve(): void;
37
79
  stroke(): void;
@@ -40,6 +82,19 @@ export class Context {
40
82
  paintWithAlpha(alpha: number): void;
41
83
  clip(): void;
42
84
  clipPreserve(): void;
85
+ resetClip(): void;
86
+ /** Redirect drawing to an intermediate group surface. */
87
+ pushGroup(): void;
88
+ /** Like {@link pushGroup} but with an explicit {@link Content} for the group surface. */
89
+ pushGroupWithContent(content: number): void;
90
+ /** End the current group and return it as a pattern (concrete subclass). */
91
+ popGroup(): Pattern;
92
+ /** End the current group and install it as this context's source. */
93
+ popGroupToSource(): void;
94
+ /** Paint the current source through `pattern`'s alpha. */
95
+ mask(pattern: Pattern): void;
96
+ /** Paint the current source through `surface`'s alpha, offset to `(x, y)`. */
97
+ maskSurface(surface: Surface, x: number, y: number): void;
43
98
  translate(tx: number, ty: number): void;
44
99
  scale(sx: number, sy: number): void;
45
100
  rotate(angle: number): void;
@@ -49,6 +104,10 @@ export class Context {
49
104
  setSourceSurface(surface: Surface, x: number, y: number): void;
50
105
  /** The context's current source pattern (own reference; concrete subclass). */
51
106
  getSource(): Pattern;
107
+ /** The surface this context draws to (own reference; concrete subclass). */
108
+ getTarget(): Surface;
109
+ /** The target of the current group, or {@link getTarget} outside a group. */
110
+ getGroupTarget(): Surface;
52
111
  identityMatrix(): void;
53
112
  newSubPath(): void;
54
113
  /** User → device coordinates as `[x, y]` (GJS return shape). */
@@ -86,6 +145,10 @@ export class Context {
86
145
  setOperator(op: number): void;
87
146
  setMiterLimit(limit: number): void;
88
147
  setTolerance(tolerance: number): void;
148
+ /** Emit the current page and clear it (paginated surfaces; a no-op elsewhere). */
149
+ copyPage(): void;
150
+ /** Like {@link copyPage} but also clears the current page's contents. */
151
+ showPage(): void;
89
152
  status(): number;
90
153
  $dispose(): void;
91
154
  }
package/cairo.js CHANGED
@@ -23,16 +23,25 @@
23
23
  // (the `Object.assign({}, imports._cairo, cairoNative)` merge shape).
24
24
  // Copyright (c) 2010 litl, LLC / GJS contributors. MIT OR LGPL-2.0-or-later.
25
25
 
26
- import native from './index.js';
26
+ import native, { ensureNative, onNativeLoaded } from './index.js';
27
27
 
28
- const c = native.__cairo;
29
- if (c === undefined) {
30
- throw new Error(
28
+ // Bound when the addon loads: at import on desktop, on first use on a lazy host (NativeScript).
29
+ // Every call that can run before any cairo object exists goes through `engine()`.
30
+ let c;
31
+
32
+ function missingBinding() {
33
+ return new Error(
31
34
  '@gjsify/node-gi/cairo: the native cairo binding is missing from the addon. ' +
32
35
  'Rebuild @gjsify/node-gi (node-gyp rebuild) — the addon must link libcairo.',
33
36
  );
34
37
  }
35
38
 
39
+ function engine() {
40
+ ensureNative();
41
+ if (c === undefined) throw missingBinding();
42
+ return c;
43
+ }
44
+
36
45
  // ---- enums (ported verbatim from refs/gjs/modules/core/_cairo.js) -----------
37
46
 
38
47
  export const Antialias = { DEFAULT: 0, NONE: 1, GRAY: 2, SUBPIXEL: 3 };
@@ -141,7 +150,7 @@ function handleOf(x, what) {
141
150
  /** cairo drawing context. `new cairo.Context(surface)` wraps `cairo_create`. */
142
151
  export class Context {
143
152
  constructor(surface) {
144
- setHandle(this, c.contextCreate(handleOf(surface, 'cairo.Surface')));
153
+ setHandle(this, engine().contextCreate(handleOf(surface, 'cairo.Surface')));
145
154
  }
146
155
 
147
156
  save() {
@@ -181,6 +190,9 @@ export class Context {
181
190
  curveTo(x1, y1, x2, y2, x3, y3) {
182
191
  c.curveTo(this[H], x1, y1, x2, y2, x3, y3);
183
192
  }
193
+ relCurveTo(dx1, dy1, dx2, dy2, dx3, dy3) {
194
+ c.relCurveTo(this[H], dx1, dy1, dx2, dy2, dx3, dy3);
195
+ }
184
196
 
185
197
  fill() {
186
198
  c.fill(this[H]);
@@ -206,6 +218,33 @@ export class Context {
206
218
  clipPreserve() {
207
219
  c.clipPreserve(this[H]);
208
220
  }
221
+ resetClip() {
222
+ c.resetClip(this[H]);
223
+ }
224
+ /** Redirect drawing to an intermediate group surface. */
225
+ pushGroup() {
226
+ c.pushGroup(this[H]);
227
+ }
228
+ /** Like {@link pushGroup} but with an explicit {@link Content} for the group surface. */
229
+ pushGroupWithContent(content) {
230
+ c.pushGroupWithContent(this[H], content);
231
+ }
232
+ /** End the current group and return it as a pattern (concrete subclass). */
233
+ popGroup() {
234
+ return wrapPatternHandle(c.popGroup(this[H]));
235
+ }
236
+ /** End the current group and install it as this context's source. */
237
+ popGroupToSource() {
238
+ c.popGroupToSource(this[H]);
239
+ }
240
+ /** Paint the current source through `pattern`'s alpha. */
241
+ mask(pattern) {
242
+ c.mask(this[H], handleOf(pattern, 'cairo.Pattern'));
243
+ }
244
+ /** Paint the current source through `surface`'s alpha, offset to `(x, y)`. */
245
+ maskSurface(surface, x, y) {
246
+ c.maskSurface(this[H], handleOf(surface, 'cairo.Surface'), x, y);
247
+ }
209
248
 
210
249
  translate(tx, ty) {
211
250
  c.translate(this[H], tx, ty);
@@ -289,6 +328,14 @@ export class Context {
289
328
  getSource() {
290
329
  return wrapPatternHandle(c.getSource(this[H]));
291
330
  }
331
+ /** The surface this context draws to (a ref of its own — safe to keep). */
332
+ getTarget() {
333
+ return wrapSurfaceHandle(c.getTarget(this[H]));
334
+ }
335
+ /** The target of the current group, or {@link getTarget} outside a group. */
336
+ getGroupTarget() {
337
+ return wrapSurfaceHandle(c.getGroupTarget(this[H]));
338
+ }
292
339
 
293
340
  setLineWidth(width) {
294
341
  c.setLineWidth(this[H], width);
@@ -332,6 +379,41 @@ export class Context {
332
379
  fillExtents() {
333
380
  return c.fillExtents(this[H]);
334
381
  }
382
+ /** Select the toy-API font: `family` with a {@link FontSlant} and {@link FontWeight}. */
383
+ selectFontFace(family, slant, weight) {
384
+ c.selectFontFace(this[H], family, slant, weight);
385
+ }
386
+ setFontSize(size) {
387
+ c.setFontSize(this[H], size);
388
+ }
389
+ /** Draw `utf8` at the current point with the current source. */
390
+ showText(utf8) {
391
+ c.showText(this[H], utf8);
392
+ }
393
+ /** Append the outline of `utf8` to the current path. */
394
+ textPath(utf8) {
395
+ c.textPath(this[H], utf8);
396
+ }
397
+ /** `{ xBearing, yBearing, width, height, xAdvance, yAdvance }` of `utf8` in the current font. */
398
+ textExtents(utf8) {
399
+ return c.textExtents(this[H], utf8);
400
+ }
401
+ /** `{ ascent, descent, height, maxXAdvance, maxYAdvance }` of the current font. */
402
+ fontExtents() {
403
+ return c.fontExtents(this[H]);
404
+ }
405
+ /**
406
+ * The current font matrix as `{ xx, yx, xy, yy, x0, y0}`. GJS has no cairo
407
+ * matrix wrapper (`getMatrix`/`setMatrix` stay unbound), so it travels as
408
+ * a plain object of the six doubles.
409
+ */
410
+ getFontMatrix() {
411
+ return c.getFontMatrix(this[H]);
412
+ }
413
+ /** Set the font matrix from `{ xx, yx, xy, yy, x0, y0}`. */
414
+ setFontMatrix(matrix) {
415
+ c.setFontMatrix(this[H], matrix);
416
+ }
335
417
  /** Stroke bounding box as `[x1, y1, x2, y2]`. */
336
418
  strokeExtents() {
337
419
  return c.strokeExtents(this[H]);
@@ -362,6 +444,15 @@ export class Context {
362
444
  c.setTolerance(this[H], tolerance);
363
445
  }
364
446
 
447
+ /** Emit the current page and clear it (paginated surfaces; a no-op elsewhere). */
448
+ copyPage() {
449
+ c.copyPage(this[H]);
450
+ }
451
+ /** Like {@link copyPage} but also clears the current page's contents. */
452
+ showPage() {
453
+ c.showPage(this[H]);
454
+ }
455
+
365
456
  status() {
366
457
  return c.status(this[H]);
367
458
  }
@@ -399,14 +490,14 @@ export class Surface {
399
490
  export class ImageSurface extends Surface {
400
491
  constructor(format, width, height) {
401
492
  super();
402
- setHandle(this, c.imageSurfaceCreate(format, width, height));
493
+ setHandle(this, engine().imageSurfaceCreate(format, width, height));
403
494
  }
404
495
 
405
496
  static create(format, width, height) {
406
497
  return new ImageSurface(format, width, height);
407
498
  }
408
499
  static createFromPNG(filename) {
409
- return wrapRaw(ImageSurface, c.imageSurfaceCreateFromPNG(filename));
500
+ return wrapRaw(ImageSurface, engine().imageSurfaceCreateFromPNG(filename));
410
501
  }
411
502
 
412
503
  getWidth() {
@@ -448,10 +539,10 @@ export class Pattern {
448
539
  /** A solid-color pattern (`cairo_pattern_create_rgb[a]`). */
449
540
  export class SolidPattern extends Pattern {
450
541
  static createRGB(red, green, blue) {
451
- return wrapRaw(SolidPattern, c.solidPatternCreateRGB(red, green, blue));
542
+ return wrapRaw(SolidPattern, engine().solidPatternCreateRGB(red, green, blue));
452
543
  }
453
544
  static createRGBA(red, green, blue, alpha) {
454
- return wrapRaw(SolidPattern, c.solidPatternCreateRGBA(red, green, blue, alpha));
545
+ return wrapRaw(SolidPattern, engine().solidPatternCreateRGBA(red, green, blue, alpha));
455
546
  }
456
547
  }
457
548
 
@@ -469,7 +560,7 @@ export class Gradient extends Pattern {
469
560
  export class LinearGradient extends Gradient {
470
561
  constructor(x0, y0, x1, y1) {
471
562
  super();
472
- setHandle(this, c.linearGradientCreate(x0, y0, x1, y1));
563
+ setHandle(this, engine().linearGradientCreate(x0, y0, x1, y1));
473
564
  }
474
565
  }
475
566
 
@@ -477,7 +568,7 @@ export class LinearGradient extends Gradient {
477
568
  export class RadialGradient extends Gradient {
478
569
  constructor(cx0, cy0, radius0, cx1, cy1, radius1) {
479
570
  super();
480
- setHandle(this, c.radialGradientCreate(cx0, cy0, radius0, cx1, cy1, radius1));
571
+ setHandle(this, engine().radialGradientCreate(cx0, cy0, radius0, cx1, cy1, radius1));
481
572
  }
482
573
  }
483
574
 
@@ -485,7 +576,7 @@ export class RadialGradient extends Gradient {
485
576
  export class SurfacePattern extends Pattern {
486
577
  constructor(surface) {
487
578
  super();
488
- setHandle(this, c.surfacePatternCreate(handleOf(surface, 'cairo.Surface')));
579
+ setHandle(this, engine().surfacePatternCreate(handleOf(surface, 'cairo.Surface')));
489
580
  }
490
581
 
491
582
  setExtend(extend) {
@@ -509,6 +600,12 @@ export class SurfacePattern extends Pattern {
509
600
  */
510
601
  export class Path {}
511
602
 
603
+ // Fan a surface handle out to the concrete subclass by cairo runtime type —
604
+ // mirrors CairoSurface::from_c_ptr (only the image surface has a subclass here).
605
+ function wrapSurfaceHandle(handle) {
606
+ return wrapRaw(c.surfaceGetType(handle) === SurfaceType.IMAGE ? ImageSurface : Surface, handle);
607
+ }
608
+
512
609
  // Fan a pattern handle out to the concrete subclass by cairo runtime type —
513
610
  // mirrors gjs_cairo_pattern_from_pattern (MESH/RASTER_SOURCE fall back to the
514
611
  // base Pattern; gjs throws for those, but they are unreachable from this module).
@@ -531,11 +628,15 @@ function wrapPatternHandle(handle) {
531
628
  // a returned/callback cairo pointer into the right JS instance. Surface + Pattern
532
629
  // fan out to the concrete subclass by cairo runtime type (mirrors
533
630
  // CairoSurface::from_c_ptr / gjs_cairo_pattern_from_pattern).
534
- c.setup({
535
- context: (handle) => wrapRaw(Context, handle),
536
- surface: (handle) => wrapRaw(c.surfaceGetType(handle) === SurfaceType.IMAGE ? ImageSurface : Surface, handle),
537
- pattern: (handle) => wrapPatternHandle(handle),
538
- path: (handle) => wrapRaw(Path, handle),
631
+ onNativeLoaded(() => {
632
+ if (native.__cairo === undefined) throw missingBinding();
633
+ c = native.__cairo;
634
+ c.setup({
635
+ context: (handle) => wrapRaw(Context, handle),
636
+ surface: (handle) => wrapSurfaceHandle(handle),
637
+ pattern: (handle) => wrapPatternHandle(handle),
638
+ path: (handle) => wrapRaw(Path, handle),
639
+ });
539
640
  });
540
641
 
541
642
  // The default export mirrors GJS's ESM `cairo` object (enums + constructors), the
package/gettext.js CHANGED
@@ -23,6 +23,7 @@
23
23
  // default export. The gjsify `--app node` build aliases the bare `gettext`
24
24
  // specifier to this module (kept external — `ALIASES_GJS_FOR_NODE`).
25
25
  import * as native from './index.js';
26
+ import { ensureNative } from './index.js';
26
27
  import { requireGi } from './gi.js';
27
28
 
28
29
  // GLib is resolved on first lookup, not at import: loading a namespace walks the
@@ -48,7 +49,10 @@ function GLib() {
48
49
  // lookups still work (they go through GLib, which every addon version has), and
49
50
  // the binders are the no-ops they used to be. It is announced once, because a
50
51
  // silently untranslated UI is the defect this file exists to close.
51
- const haveLocaleBinders = typeof native.localeCategories === 'function';
52
+ function haveLocaleBinders() {
53
+ ensureNative();
54
+ return typeof native.localeCategories === 'function';
55
+ }
52
56
 
53
57
  let announcedStaleAddon = false;
54
58
  function noLocaleBinders(fn) {
@@ -78,6 +82,18 @@ const FALLBACK_CATEGORIES = {
78
82
  win32: { ALL: 0, COLLATE: 1, CTYPE: 2, MONETARY: 3, NUMERIC: 4, TIME: 5, MESSAGES: 1729 },
79
83
  };
80
84
 
85
+ // Resolved on first read, not at import, so a lazy host (NativeScript) that only imports this
86
+ // module has not loaded the addon yet; a failed load throws here, there is no fallback table.
87
+ let categories = null;
88
+ function localeCategories() {
89
+ if (categories === null) {
90
+ categories = haveLocaleBinders()
91
+ ? native.localeCategories()
92
+ : (FALLBACK_CATEGORIES[globalThis.process?.platform] ?? FALLBACK_CATEGORIES.linux);
93
+ }
94
+ return categories;
95
+ }
96
+
81
97
  /** Look up `msgid` in the default domain (the one `textdomain()` last named). */
82
98
  export function gettext(msgid) {
83
99
  return GLib().dgettext(null, msgid);
@@ -145,13 +161,13 @@ export function domain(domainName) {
145
161
  * @returns {string|null} the prior locale name, or null if it could not be set
146
162
  */
147
163
  export function setlocale(category, locale) {
148
- if (!haveLocaleBinders) return noLocaleBinders('setlocale');
164
+ if (!haveLocaleBinders()) return noLocaleBinders('setlocale');
149
165
  return native.setThreadLocale(category, locale ?? null);
150
166
  }
151
167
 
152
168
  /** Set the default text domain, or report it when `domainName` is null. */
153
169
  export function textdomain(domainName) {
154
- if (!haveLocaleBinders) return noLocaleBinders('textdomain');
170
+ if (!haveLocaleBinders()) return noLocaleBinders('textdomain');
155
171
  return native.textdomain(domainName ?? null);
156
172
  }
157
173
 
@@ -162,7 +178,7 @@ export function textdomain(domainName) {
162
178
  * `gjs_bindtextdomain` does — see the note in private.cc.
163
179
  */
164
180
  export function bindtextdomain(domainName, dirName) {
165
- if (!haveLocaleBinders) return noLocaleBinders('bindtextdomain');
181
+ if (!haveLocaleBinders()) return noLocaleBinders('bindtextdomain');
166
182
  return native.bindtextdomain(domainName, dirName ?? null);
167
183
  }
168
184
 
@@ -188,9 +204,15 @@ export function bindtextdomainCodeset(_domainName, _codeset) {
188
204
  * claiming to address LC_MESSAGES — on two of the three platforms the translation
189
205
  * blocker was reported from.
190
206
  */
191
- export const LocaleCategory = haveLocaleBinders
192
- ? native.localeCategories()
193
- : (FALLBACK_CATEGORIES[globalThis.process?.platform] ?? FALLBACK_CATEGORIES.linux);
207
+ export const LocaleCategory = Object.defineProperties(
208
+ {},
209
+ Object.fromEntries(
210
+ Object.keys(FALLBACK_CATEGORIES.linux).map((name) => [
211
+ name,
212
+ { enumerable: true, get: () => localeCategories()[name] },
213
+ ]),
214
+ ),
215
+ );
194
216
 
195
217
  /**
196
218
  * The GJS `Gettext` module as a default export — the object shape