@gjsify/node-gi 0.64.1 → 0.64.4
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 +9 -0
- package/cairo.d.ts +38 -0
- package/cairo.js +95 -17
- package/gettext.js +29 -7
- package/gi.js +514 -34
- package/host.nativescript.js +19 -0
- package/host.node.js +16 -0
- package/index.d.ts +12 -0
- package/index.js +245 -76
- package/package.json +1 -1
- package/prebuilds/darwin-arm64/node_gi.node +0 -0
- package/prebuilds/darwin-x64/node_gi.node +0 -0
- package/prebuilds/linux-arm64/node_gi.node +0 -0
- package/prebuilds/linux-x64/node_gi.node +0 -0
- package/prebuilds/win32-arm64/node_gi.node +0 -0
- package/prebuilds/win32-x64/node_gi.node +0 -0
- package/src/cairo.cc +96 -0
- package/src/class.cc +15 -0
- package/src/common.h +21 -3
- package/src/marshal.cc +39 -17
- package/src/signals.cc +45 -22
package/host.nativescript.js
CHANGED
|
@@ -44,6 +44,25 @@
|
|
|
44
44
|
/** @type {'nativescript'} */
|
|
45
45
|
export const RUNTIME = 'nativescript';
|
|
46
46
|
|
|
47
|
+
/**
|
|
48
|
+
* Whether the addon may be loaded on first use instead of at import — see index.js.
|
|
49
|
+
* @type {boolean}
|
|
50
|
+
*/
|
|
51
|
+
export const LAZY_NATIVE = true;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Whether `GObject.registerClass` may register a class whose JS parent is NOT a node-gi
|
|
55
|
+
* GObject class — `class MyLabel extends Label {}`, where `Label` is a
|
|
56
|
+
* `@nativescript/core` widget. GJS rejects that outright and so does node-gi on every
|
|
57
|
+
* other host; this is the ONE deliberate divergence, and it is deliberate because on
|
|
58
|
+
* NativeScript there is no GJS to be compatible with: node-gi is the only GI runtime that
|
|
59
|
+
* ever executes here, so a program written against this cannot be a program that behaves
|
|
60
|
+
* differently under GJS. See the FOREIGN-PARENT section in gi.js, ADR 0105 § 2 and
|
|
61
|
+
* docs/node-gi-gjs-surface.md.
|
|
62
|
+
* @type {boolean}
|
|
63
|
+
*/
|
|
64
|
+
export const FOREIGN_PARENT_CLASSES = true;
|
|
65
|
+
|
|
47
66
|
// Timers from the global scope: NativeScript has no node:timers, and there is no
|
|
48
67
|
// GLib-backed replacement to avoid here. setImmediate is not a V8 builtin.
|
|
49
68
|
export const setImmediate =
|
package/host.node.js
CHANGED
|
@@ -55,6 +55,22 @@ export const RUNTIME =
|
|
|
55
55
|
? 'deno'
|
|
56
56
|
: 'node';
|
|
57
57
|
|
|
58
|
+
/**
|
|
59
|
+
* Whether the addon may be loaded on first use instead of at import — see index.js.
|
|
60
|
+
* @type {boolean}
|
|
61
|
+
*/
|
|
62
|
+
export const LAZY_NATIVE = false;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Whether `GObject.registerClass` may register a class whose JS parent is NOT a node-gi
|
|
66
|
+
* GObject class. FALSE here, and that is the point: on Node/Bun/Deno/GJS a program can be
|
|
67
|
+
* run under real GJS, so registerClass must keep throwing GJS's invalid-base error
|
|
68
|
+
* (refs/gjs/modules/core/overrides/GObject.js) rather than quietly accept a class GJS
|
|
69
|
+
* would reject. Only host.nativescript.js enables it — see the note there.
|
|
70
|
+
* @type {boolean}
|
|
71
|
+
*/
|
|
72
|
+
export const FOREIGN_PARENT_CLASSES = false;
|
|
73
|
+
|
|
58
74
|
// GJS host mode: `GjsifyNapi.init()` installs the loader as
|
|
59
75
|
// `globalThis.__gjsifyNapiLoadAddon` and re-installs it if another consumer (the
|
|
60
76
|
// @gjsify/napi L1) already captured + deleted it. Needs the GjsifyNapi typelib
|
package/index.d.ts
CHANGED
|
@@ -542,6 +542,18 @@ export function emitSignal(handle: GObjectHandle, signalName: string, args?: unk
|
|
|
542
542
|
/** Disconnect a previously connected signal handler. */
|
|
543
543
|
export function disconnectSignal(handle: GObjectHandle, handlerId: number): void;
|
|
544
544
|
|
|
545
|
+
/**
|
|
546
|
+
* INTERNAL. Keep a callback alive for as long as `handle` is alive. Signal and
|
|
547
|
+
* template-callback GClosures hold their JS callback through a WEAK napi_ref, so
|
|
548
|
+
* a connected handler is not a GC root (which is what makes a handler capturing
|
|
549
|
+
* its own emitter collectable, as in GJS); this handle-keyed WeakMap is the
|
|
550
|
+
* ephemeron that keeps it callable meanwhile. `connectSignal` does it for you.
|
|
551
|
+
*/
|
|
552
|
+
export function retainClosure(handle: GObjectHandle, key: number | symbol, fn: Function): void;
|
|
553
|
+
|
|
554
|
+
/** INTERNAL. Drop a closure retained by {@link retainClosure}. */
|
|
555
|
+
export function releaseClosure(handle: GObjectHandle, key: number | symbol): void;
|
|
556
|
+
|
|
545
557
|
/**
|
|
546
558
|
* Register the L1 resolver mapping a Gtk.Template `<signal handler="…">` handler
|
|
547
559
|
* name to the instance's bound JS method (engine template-callback scope).
|
package/index.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// a native package's JS entry is a loader, not a tsc artifact, and the repo
|
|
6
6
|
// ignores `lib/`. The addon is Node-API (ABI-stable), so the same binary loads on
|
|
7
7
|
// Node, Bun and Deno; runtime-specific behaviour is gated off RUNTIME below.
|
|
8
|
-
import { RUNTIME, loadNativeHost } from '#host';
|
|
8
|
+
import { RUNTIME, loadNativeHost, LAZY_NATIVE } from '#host';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Which JS runtime we are on. Detection — and why each runtime gets the main-loop
|
|
@@ -17,121 +17,290 @@ export { RUNTIME };
|
|
|
17
17
|
/** Whether we are on Node.js (the only runtime with the libuv main-loop bridge). */
|
|
18
18
|
export const isNodeRuntime = RUNTIME === 'node';
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
/**
|
|
21
|
+
* The native engine. Desktop runtimes load it HERE, while this module evaluates, exactly as
|
|
22
|
+
* before. A host that sets `LAZY_NATIVE` (NativeScript: dlopen of `libnode_gi.so` plus its
|
|
23
|
+
* closure costs real cold-start time) defers the load to the first `ensureNative()`, so an app
|
|
24
|
+
* that imports this package but never asks for GObject pays nothing. A failed load throws from
|
|
25
|
+
* `ensureNative()` every time it is asked — a selected real engine still fails loudly, there
|
|
26
|
+
* is no fallback here (ADR 0105 § 4).
|
|
27
|
+
* @type {Record<string, any>}
|
|
28
|
+
*/
|
|
29
|
+
let native;
|
|
30
|
+
let loaded = false;
|
|
31
|
+
/** @type {Array<() => void>} */
|
|
32
|
+
const loadHooks = [];
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Load the addon if that has not happened yet, and return it. The L1 layers call this before
|
|
36
|
+
* they touch any binding below.
|
|
37
|
+
* @returns {Record<string, any>}
|
|
38
|
+
*/
|
|
39
|
+
export function ensureNative() {
|
|
40
|
+
if (loaded) return native;
|
|
41
|
+
const addon = loadNativeHost();
|
|
42
|
+
native = addon;
|
|
43
|
+
requireNamespace = addon.requireNamespace;
|
|
44
|
+
listInfoNames = addon.listInfoNames;
|
|
45
|
+
findInfo = addon.findInfo;
|
|
46
|
+
getConstantValue = addon.getConstantValue;
|
|
47
|
+
getEnumValues = addon.getEnumValues;
|
|
48
|
+
getErrorDomain = addon.getErrorDomain;
|
|
49
|
+
errorDomainInfo = addon.errorDomainInfo;
|
|
50
|
+
setErrorBuilder = addon.setErrorBuilder;
|
|
51
|
+
prependSearchPath = addon.prependSearchPath;
|
|
52
|
+
prependLibraryPath = addon.prependLibraryPath;
|
|
53
|
+
callFunction = addon.callFunction;
|
|
54
|
+
callMethod = addon.callMethod;
|
|
55
|
+
hasMethod = addon.hasMethod;
|
|
56
|
+
hasClassMethod = addon.hasClassMethod;
|
|
57
|
+
classMethodArity = addon.classMethodArity;
|
|
58
|
+
callStaticMethod = addon.callStaticMethod;
|
|
59
|
+
constructStruct = addon.constructStruct;
|
|
60
|
+
newObject = addon.newObject;
|
|
61
|
+
registerClass = addon.registerClass;
|
|
62
|
+
registerClassFromGType = addon.registerClassFromGType;
|
|
63
|
+
constructType = addon.constructType;
|
|
64
|
+
callParentVfunc = addon.callParentVfunc;
|
|
65
|
+
hasClassVfunc = addon.hasClassVfunc;
|
|
66
|
+
callClassVfunc = addon.callClassVfunc;
|
|
67
|
+
getTemplateChild = addon.getTemplateChild;
|
|
68
|
+
getProperty = addon.getProperty;
|
|
69
|
+
setProperty = addon.setProperty;
|
|
70
|
+
hasProperty = addon.hasProperty;
|
|
71
|
+
getTypeName = addon.getTypeName;
|
|
72
|
+
classInfoForTypeName = addon.classInfoForTypeName;
|
|
73
|
+
getGType = addon.getGType;
|
|
74
|
+
isInstanceOf = addon.isInstanceOf;
|
|
75
|
+
isGObjectHandle = addon.isGObjectHandle;
|
|
76
|
+
newGValue = addon.newGValue;
|
|
77
|
+
callBoxedMethod = addon.callBoxedMethod;
|
|
78
|
+
isBoxedHandle = addon.isBoxedHandle;
|
|
79
|
+
boxedMemberKind = addon.boxedMemberKind;
|
|
80
|
+
getBoxedField = addon.getBoxedField;
|
|
81
|
+
setBoxedField = addon.setBoxedField;
|
|
82
|
+
boxedTypeName = addon.boxedTypeName;
|
|
83
|
+
__boxedAddress = addon.__boxedAddress;
|
|
84
|
+
isParamSpecHandle = addon.isParamSpecHandle;
|
|
85
|
+
isFundamentalHandle = addon.isFundamentalHandle;
|
|
86
|
+
paramSpecProp = addon.paramSpecProp;
|
|
87
|
+
variantNew = addon.variantNew;
|
|
88
|
+
variantUnpack = addon.variantUnpack;
|
|
89
|
+
variantGetTypeString = addon.variantGetTypeString;
|
|
90
|
+
isVariantHandle = addon.isVariantHandle;
|
|
91
|
+
startMainLoop = addon.startMainLoop;
|
|
92
|
+
iterateMainContext = addon.iterateMainContext;
|
|
93
|
+
mainContextHasPending = addon.mainContextHasPending;
|
|
94
|
+
makePumpPendingCount = addon.makePumpPendingCount;
|
|
95
|
+
pumpKick = addon.pumpKick;
|
|
96
|
+
setMicrotaskDrain = addon.setMicrotaskDrain;
|
|
97
|
+
emitSignal = addon.emitSignal;
|
|
98
|
+
connectSignal = (handle, signalName, callback, after) => {
|
|
99
|
+
const id = addon.connectSignal(handle, signalName, callback, after);
|
|
100
|
+
retainClosure(handle, id, callback);
|
|
101
|
+
return id;
|
|
102
|
+
};
|
|
103
|
+
disconnectSignal = (handle, handlerId) => {
|
|
104
|
+
releaseClosure(handle, handlerId);
|
|
105
|
+
return addon.disconnectSignal(handle, handlerId);
|
|
106
|
+
};
|
|
107
|
+
setTemplateCallbackResolver = addon.setTemplateCallbackResolver;
|
|
108
|
+
setConstructCallback = addon.setConstructCallback;
|
|
109
|
+
setPropertySetCallback = addon.setPropertySetCallback;
|
|
110
|
+
storedPropertyNames = addon.storedPropertyNames;
|
|
111
|
+
logSetWriterFunc = addon.logSetWriterFunc;
|
|
112
|
+
logSetWriterDefault = addon.logSetWriterDefault;
|
|
113
|
+
bindPropertyFull = addon.bindPropertyFull;
|
|
114
|
+
bindingGroupBindFull = addon.bindingGroupBindFull;
|
|
115
|
+
setThreadLocale = addon.setThreadLocale;
|
|
116
|
+
textdomain = addon.textdomain;
|
|
117
|
+
bindtextdomain = addon.bindtextdomain;
|
|
118
|
+
localeCategories = addon.localeCategories;
|
|
119
|
+
probeHostOpenGL = addon.probeHostOpenGL;
|
|
120
|
+
currentGLStrings = addon.currentGLStrings;
|
|
121
|
+
loaded = true;
|
|
122
|
+
for (const hook of loadHooks.splice(0)) hook();
|
|
123
|
+
return native;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** Whether the addon is loaded now — what the lazy host makes observable. */
|
|
127
|
+
export function isNativeLoaded() {
|
|
128
|
+
return loaded;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Run `hook` once the addon is loaded: now if it is, else straight after the first load. The
|
|
133
|
+
* L1 module-scope registrations (error builder, template resolver, …) go through this, so they
|
|
134
|
+
* run at import on desktop and at first use on a lazy host.
|
|
135
|
+
* @param {() => void} hook
|
|
136
|
+
*/
|
|
137
|
+
export function onNativeLoaded(hook) {
|
|
138
|
+
if (loaded) hook();
|
|
139
|
+
else loadHooks.push(hook);
|
|
140
|
+
}
|
|
21
141
|
|
|
22
142
|
// Straight re-exports of the native engine surface. Each contract — arguments,
|
|
23
143
|
// ownership, the OUT/INOUT return-tuple convention, the keep-alive rules of the
|
|
24
144
|
// main-loop group — is documented once in index.d.ts, the published type surface;
|
|
25
145
|
// only what index.d.ts does not carry is noted here.
|
|
26
|
-
export
|
|
27
|
-
export
|
|
28
|
-
export
|
|
29
|
-
export
|
|
30
|
-
export
|
|
31
|
-
export
|
|
32
|
-
export
|
|
33
|
-
export
|
|
34
|
-
export
|
|
35
|
-
export
|
|
36
|
-
export
|
|
37
|
-
export
|
|
38
|
-
export
|
|
39
|
-
export
|
|
40
|
-
export
|
|
41
|
-
export
|
|
42
|
-
export
|
|
43
|
-
export
|
|
44
|
-
export
|
|
146
|
+
export let requireNamespace;
|
|
147
|
+
export let listInfoNames;
|
|
148
|
+
export let findInfo;
|
|
149
|
+
export let getConstantValue;
|
|
150
|
+
export let getEnumValues;
|
|
151
|
+
export let getErrorDomain;
|
|
152
|
+
export let errorDomainInfo;
|
|
153
|
+
export let setErrorBuilder;
|
|
154
|
+
export let prependSearchPath;
|
|
155
|
+
export let prependLibraryPath;
|
|
156
|
+
export let callFunction;
|
|
157
|
+
export let callMethod;
|
|
158
|
+
export let hasMethod;
|
|
159
|
+
export let hasClassMethod;
|
|
160
|
+
export let classMethodArity;
|
|
161
|
+
export let callStaticMethod;
|
|
162
|
+
export let constructStruct;
|
|
163
|
+
export let newObject;
|
|
164
|
+
export let registerClass;
|
|
45
165
|
|
|
46
166
|
// Subclass an ALREADY-REGISTERED parent by its GType handle instead of a
|
|
47
167
|
// `namespace.typeName`: a registered (dynamic) parent has no introspection entry, so it
|
|
48
168
|
// cannot be resolved by name. Custom properties, signals and vfunc slots of registered
|
|
49
169
|
// ancestors compose through ordinary GObject inheritance. The L1 `GObject.registerClass`
|
|
50
170
|
// picks this variant when the nearest base is itself a registered class.
|
|
51
|
-
export
|
|
52
|
-
|
|
53
|
-
export
|
|
54
|
-
export
|
|
55
|
-
export
|
|
56
|
-
export
|
|
57
|
-
export
|
|
58
|
-
export
|
|
59
|
-
export
|
|
60
|
-
export
|
|
61
|
-
export
|
|
62
|
-
export
|
|
63
|
-
export
|
|
64
|
-
export
|
|
65
|
-
export
|
|
66
|
-
export
|
|
67
|
-
export
|
|
68
|
-
export
|
|
69
|
-
export
|
|
70
|
-
export
|
|
71
|
-
export
|
|
72
|
-
export
|
|
171
|
+
export let registerClassFromGType;
|
|
172
|
+
|
|
173
|
+
export let constructType;
|
|
174
|
+
export let callParentVfunc;
|
|
175
|
+
export let hasClassVfunc;
|
|
176
|
+
export let callClassVfunc;
|
|
177
|
+
export let getTemplateChild;
|
|
178
|
+
export let getProperty;
|
|
179
|
+
export let setProperty;
|
|
180
|
+
export let hasProperty;
|
|
181
|
+
export let getTypeName;
|
|
182
|
+
export let classInfoForTypeName;
|
|
183
|
+
export let getGType;
|
|
184
|
+
export let isInstanceOf;
|
|
185
|
+
export let isGObjectHandle;
|
|
186
|
+
export let newGValue;
|
|
187
|
+
export let callBoxedMethod;
|
|
188
|
+
export let isBoxedHandle;
|
|
189
|
+
export let boxedMemberKind;
|
|
190
|
+
export let getBoxedField;
|
|
191
|
+
export let setBoxedField;
|
|
192
|
+
export let boxedTypeName;
|
|
73
193
|
/** TEST-ONLY: the address a boxed handle wraps, so an ownership rule about pointer
|
|
74
194
|
* IDENTITY can be asserted instead of waited on (see marshal.cc BoxedAddress). */
|
|
75
|
-
export
|
|
76
|
-
export
|
|
195
|
+
export let __boxedAddress;
|
|
196
|
+
export let isParamSpecHandle;
|
|
77
197
|
|
|
78
198
|
// True for a non-GObject GObject-fundamental handle (a GskRenderNode from
|
|
79
199
|
// Gtk.Snapshot.to_node, a GdkEvent): introspected as object info, but ref-counted
|
|
80
200
|
// through its own ref/unref funcs, NOT g_object_ref. L1 surfaces it as an opaque,
|
|
81
201
|
// round-trippable pass-through handle.
|
|
82
|
-
export
|
|
83
|
-
|
|
84
|
-
export
|
|
85
|
-
export
|
|
86
|
-
export
|
|
87
|
-
export
|
|
88
|
-
export
|
|
89
|
-
export
|
|
90
|
-
export
|
|
91
|
-
export
|
|
92
|
-
export
|
|
93
|
-
export
|
|
94
|
-
export
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
202
|
+
export let isFundamentalHandle;
|
|
203
|
+
|
|
204
|
+
export let paramSpecProp;
|
|
205
|
+
export let variantNew;
|
|
206
|
+
export let variantUnpack;
|
|
207
|
+
export let variantGetTypeString;
|
|
208
|
+
export let isVariantHandle;
|
|
209
|
+
export let startMainLoop;
|
|
210
|
+
export let iterateMainContext;
|
|
211
|
+
export let mainContextHasPending;
|
|
212
|
+
export let makePumpPendingCount;
|
|
213
|
+
export let pumpKick;
|
|
214
|
+
export let setMicrotaskDrain;
|
|
215
|
+
// ---- signal-closure keep-alive (GJS's associate_closure, built in JS) ----
|
|
216
|
+
//
|
|
217
|
+
// A signal / template-callback GClosure holds its JS callback through a WEAK
|
|
218
|
+
// napi_ref (src/signals.cc), so a C-held closure is NOT a GC root. That is what
|
|
219
|
+
// makes `obj.connect('x', () => obj.y)` collectable instead of immortal, and it
|
|
220
|
+
// is GJS's design: its signal closures keep the callable unrooted and TRACE it
|
|
221
|
+
// from the owning wrapper (ObjectInstance::m_closures).
|
|
222
|
+
//
|
|
223
|
+
// node-gi has no tracing hook into V8, so the same edge is expressed as the one
|
|
224
|
+
// ephemeron a non-extensible External can take part in: a WeakMap keyed by the
|
|
225
|
+
// canonical handle. Handle reachable (JS holds the wrapper, or C owns the object
|
|
226
|
+
// and the toggle ref roots the handle) → callback reachable → a connected handler
|
|
227
|
+
// still fires. Handle reachable ONLY through the callback it retains → key and
|
|
228
|
+
// value die together, which is exactly the cycle that used to leak.
|
|
229
|
+
//
|
|
230
|
+
// Pruned on disconnect. A handler disconnected by a route that bypasses
|
|
231
|
+
// `disconnectSignal` (the introspected GObject.signal_handler_disconnect) leaves
|
|
232
|
+
// its entry behind until the handle itself goes — harmless, the closure is
|
|
233
|
+
// already gone, and the same divergence the gi.js handler-id registry documents.
|
|
234
|
+
const retainedClosures = new WeakMap();
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Keep `fn` alive for as long as `handle` is alive. INTERNAL — the JS half of the
|
|
238
|
+
* weak closure refs in src/signals.cc.
|
|
239
|
+
* @param {object} handle canonical GObject handle (the WeakMap key / lifetime)
|
|
240
|
+
* @param {number | symbol} key handler id, or a unique key for a non-signal closure
|
|
241
|
+
* @param {Function} fn the callback the native closure holds weakly
|
|
242
|
+
*/
|
|
243
|
+
export function retainClosure(handle, key, fn) {
|
|
244
|
+
let byKey = retainedClosures.get(handle);
|
|
245
|
+
if (byKey === undefined) {
|
|
246
|
+
byKey = new Map();
|
|
247
|
+
retainedClosures.set(handle, byKey);
|
|
248
|
+
}
|
|
249
|
+
byKey.set(key, fn);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Drop a retained closure (on disconnect). INTERNAL — see retainClosure.
|
|
254
|
+
* @param {object} handle canonical GObject handle
|
|
255
|
+
* @param {number | symbol} key the key it was retained under
|
|
256
|
+
*/
|
|
257
|
+
export function releaseClosure(handle, key) {
|
|
258
|
+
retainedClosures.get(handle)?.delete(key);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export let connectSignal;
|
|
262
|
+
export let emitSignal;
|
|
263
|
+
export let disconnectSignal;
|
|
264
|
+
export let setTemplateCallbackResolver;
|
|
99
265
|
|
|
100
266
|
// Registers the L1 callback the engine's overridden `constructor` vfunc invokes to run a
|
|
101
267
|
// registered class's JS constructor for a GObject that C instantiated (a GtkBuilder
|
|
102
268
|
// composite-template InternalChild): given (instanceHandle, gtypeName) it
|
|
103
269
|
// Reflect.constructs the class in adopt mode — see gi.js runCtorForCObject.
|
|
104
|
-
export
|
|
270
|
+
export let setConstructCallback;
|
|
105
271
|
|
|
106
272
|
// Registers the L1 callback the engine's set_property vfunc invokes after storing a
|
|
107
273
|
// custom property: given (instanceHandle, propertyName) it runs the class's own JS
|
|
108
274
|
// setter — see gi.js runJsPropertySetter. The engine calls it only once the instance
|
|
109
275
|
// HAS a wrapper, so a construct-time set never reaches it; those are replayed from
|
|
110
276
|
// the base ctor over storedPropertyNames.
|
|
111
|
-
export
|
|
277
|
+
export let setPropertySetCallback;
|
|
112
278
|
|
|
113
279
|
// The custom properties actually SET on an instance (the per-instance store's keys) —
|
|
114
280
|
// what that construct-time replay has to push through the class's JS setters.
|
|
115
|
-
export
|
|
281
|
+
export let storedPropertyNames;
|
|
116
282
|
|
|
117
|
-
export
|
|
118
|
-
export
|
|
119
|
-
export
|
|
120
|
-
export
|
|
283
|
+
export let logSetWriterFunc;
|
|
284
|
+
export let logSetWriterDefault;
|
|
285
|
+
export let bindPropertyFull;
|
|
286
|
+
export let bindingGroupBindFull;
|
|
121
287
|
|
|
122
288
|
// The libintl half of GjsPrivate — the binders GLib's GIR does not publish, plus
|
|
123
289
|
// the LC_* constants read from the host's own <locale.h>. Consumed by
|
|
124
290
|
// `./gettext.js`; loading THIS module is also what puts the process in the
|
|
125
291
|
// environment's locale (the addon's Init does it, as gjs's entry point does).
|
|
126
|
-
export
|
|
127
|
-
export
|
|
128
|
-
export
|
|
129
|
-
export
|
|
292
|
+
export let setThreadLocale;
|
|
293
|
+
export let textdomain;
|
|
294
|
+
export let bindtextdomain;
|
|
295
|
+
export let localeCategories;
|
|
130
296
|
|
|
131
297
|
// win32 only (undefined elsewhere): which OpenGL the process got — see gtk-runtime.js
|
|
132
298
|
// activateBundledOpenGL. currentGLStrings asks the implementation behind the context
|
|
133
299
|
// current on this thread, so a caller can tell Mesa from a vendor driver.
|
|
134
|
-
export
|
|
135
|
-
export
|
|
300
|
+
export let probeHostOpenGL;
|
|
301
|
+
export let currentGLStrings;
|
|
302
|
+
|
|
303
|
+
export { native as default };
|
|
136
304
|
|
|
137
|
-
export
|
|
305
|
+
// Last, so every `export let` above is initialised before the eager load assigns it.
|
|
306
|
+
if (!LAZY_NATIVE) ensureNative();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gjsify/node-gi",
|
|
3
|
-
"version": "0.64.
|
|
3
|
+
"version": "0.64.4",
|
|
4
4
|
"description": "GObject-Introspection runtime for Node.js — load gi:// namespaces (GLib/GObject/Gio/…) with GJS-compatible semantics. Vendored + rewritten from node-gtk under MIT.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/src/cairo.cc
CHANGED
|
@@ -621,6 +621,11 @@ void InitCairo(Napi::Env env, Napi::Object exports) {
|
|
|
621
621
|
c.Set("paint", Napi::Function::New(env, CTX_0(cairo_paint)));
|
|
622
622
|
c.Set("clip", Napi::Function::New(env, CTX_0(cairo_clip)));
|
|
623
623
|
c.Set("clipPreserve", Napi::Function::New(env, CTX_0(cairo_clip_preserve)));
|
|
624
|
+
c.Set("resetClip", Napi::Function::New(env, CTX_0(cairo_reset_clip)));
|
|
625
|
+
c.Set("pushGroup", Napi::Function::New(env, CTX_0(cairo_push_group)));
|
|
626
|
+
c.Set("popGroupToSource", Napi::Function::New(env, CTX_0(cairo_pop_group_to_source)));
|
|
627
|
+
c.Set("copyPage", Napi::Function::New(env, CTX_0(cairo_copy_page)));
|
|
628
|
+
c.Set("showPage", Napi::Function::New(env, CTX_0(cairo_show_page)));
|
|
624
629
|
|
|
625
630
|
c.Set("setLineWidth", Napi::Function::New(env, CTX_1D(cairo_set_line_width)));
|
|
626
631
|
c.Set("paintWithAlpha", Napi::Function::New(env, CTX_1D(cairo_paint_with_alpha)));
|
|
@@ -641,7 +646,10 @@ void InitCairo(Napi::Env env, Napi::Object exports) {
|
|
|
641
646
|
c.Set("arc", Napi::Function::New(env, CTX_5D(cairo_arc)));
|
|
642
647
|
c.Set("arcNegative", Napi::Function::New(env, CTX_5D(cairo_arc_negative)));
|
|
643
648
|
c.Set("curveTo", Napi::Function::New(env, CTX_6D(cairo_curve_to)));
|
|
649
|
+
c.Set("relCurveTo", Napi::Function::New(env, CTX_6D(cairo_rel_curve_to)));
|
|
644
650
|
|
|
651
|
+
c.Set("pushGroupWithContent",
|
|
652
|
+
Napi::Function::New(env, CTX_ENUM(cairo_push_group_with_content, cairo_content_t)));
|
|
645
653
|
c.Set("setOperator", Napi::Function::New(env, CTX_ENUM(cairo_set_operator, cairo_operator_t)));
|
|
646
654
|
c.Set("setLineCap", Napi::Function::New(env, CTX_ENUM(cairo_set_line_cap, cairo_line_cap_t)));
|
|
647
655
|
c.Set("setLineJoin", Napi::Function::New(env, CTX_ENUM(cairo_set_line_join, cairo_line_join_t)));
|
|
@@ -798,6 +806,45 @@ void InitCairo(Napi::Env env, Napi::Object exports) {
|
|
|
798
806
|
o.Set("maxYAdvance", Napi::Number::New(env, e.max_y_advance));
|
|
799
807
|
return o;
|
|
800
808
|
}));
|
|
809
|
+
// The font matrix is a plain `{xx, yx, xy, yy, x0, y0}` object in GJS — cairo's
|
|
810
|
+
// matrix type has no wrapper there (getMatrix/setMatrix stay unbound for that
|
|
811
|
+
// reason), so the font matrix travels as the six doubles.
|
|
812
|
+
c.Set("getFontMatrix", Napi::Function::New(env, +[](const Napi::CallbackInfo& info) -> Napi::Value {
|
|
813
|
+
Napi::Env env = info.Env();
|
|
814
|
+
cairo_t* cr = CtxArg(env, info);
|
|
815
|
+
if (cr == nullptr) return env.Undefined();
|
|
816
|
+
cairo_matrix_t m;
|
|
817
|
+
cairo_get_font_matrix(cr, &m);
|
|
818
|
+
if (!CheckStatus(env, cairo_status(cr), "context")) return env.Undefined();
|
|
819
|
+
Napi::Object o = Napi::Object::New(env);
|
|
820
|
+
o.Set("xx", Napi::Number::New(env, m.xx));
|
|
821
|
+
o.Set("yx", Napi::Number::New(env, m.yx));
|
|
822
|
+
o.Set("xy", Napi::Number::New(env, m.xy));
|
|
823
|
+
o.Set("yy", Napi::Number::New(env, m.yy));
|
|
824
|
+
o.Set("x0", Napi::Number::New(env, m.x0));
|
|
825
|
+
o.Set("y0", Napi::Number::New(env, m.y0));
|
|
826
|
+
return o;
|
|
827
|
+
}));
|
|
828
|
+
c.Set("setFontMatrix", Napi::Function::New(env, +[](const Napi::CallbackInfo& info) -> Napi::Value {
|
|
829
|
+
Napi::Env env = info.Env();
|
|
830
|
+
cairo_t* cr = CtxArg(env, info);
|
|
831
|
+
if (cr == nullptr) return env.Undefined();
|
|
832
|
+
if (!info[1].IsObject()) {
|
|
833
|
+
Napi::TypeError::New(env, "matrix must be an object").ThrowAsJavaScriptException();
|
|
834
|
+
return env.Undefined();
|
|
835
|
+
}
|
|
836
|
+
Napi::Object o = info[1].As<Napi::Object>();
|
|
837
|
+
cairo_matrix_t m;
|
|
838
|
+
m.xx = o.Get("xx").ToNumber().DoubleValue();
|
|
839
|
+
m.yx = o.Get("yx").ToNumber().DoubleValue();
|
|
840
|
+
m.xy = o.Get("xy").ToNumber().DoubleValue();
|
|
841
|
+
m.yy = o.Get("yy").ToNumber().DoubleValue();
|
|
842
|
+
m.x0 = o.Get("x0").ToNumber().DoubleValue();
|
|
843
|
+
m.y0 = o.Get("y0").ToNumber().DoubleValue();
|
|
844
|
+
cairo_set_font_matrix(cr, &m);
|
|
845
|
+
CheckStatus(env, cairo_status(cr), "context");
|
|
846
|
+
return env.Undefined();
|
|
847
|
+
}));
|
|
801
848
|
c.Set("status", Napi::Function::New(env, +[](const Napi::CallbackInfo& info) -> Napi::Value {
|
|
802
849
|
Napi::Env env = info.Env();
|
|
803
850
|
cairo_t* cr = CtxArg(env, info);
|
|
@@ -824,6 +871,26 @@ void InitCairo(Napi::Env env, Napi::Object exports) {
|
|
|
824
871
|
CheckStatus(env, cairo_status(cr), "context");
|
|
825
872
|
return env.Undefined();
|
|
826
873
|
}));
|
|
874
|
+
c.Set("mask", Napi::Function::New(env, +[](const Napi::CallbackInfo& info) -> Napi::Value {
|
|
875
|
+
Napi::Env env = info.Env();
|
|
876
|
+
cairo_t* cr = CtxArg(env, info);
|
|
877
|
+
if (cr == nullptr) return env.Undefined();
|
|
878
|
+
cairo_pattern_t* p = PatternFromExt(env, info[1]);
|
|
879
|
+
if (p == nullptr) return env.Undefined();
|
|
880
|
+
cairo_mask(cr, p);
|
|
881
|
+
CheckStatus(env, cairo_status(cr), "context");
|
|
882
|
+
return env.Undefined();
|
|
883
|
+
}));
|
|
884
|
+
c.Set("maskSurface", Napi::Function::New(env, +[](const Napi::CallbackInfo& info) -> Napi::Value {
|
|
885
|
+
Napi::Env env = info.Env();
|
|
886
|
+
cairo_t* cr = CtxArg(env, info);
|
|
887
|
+
if (cr == nullptr) return env.Undefined();
|
|
888
|
+
cairo_surface_t* s = SurfaceFromExt(env, info[1]);
|
|
889
|
+
if (s == nullptr) return env.Undefined();
|
|
890
|
+
cairo_mask_surface(cr, s, Num(info, 2), Num(info, 3));
|
|
891
|
+
CheckStatus(env, cairo_status(cr), "context");
|
|
892
|
+
return env.Undefined();
|
|
893
|
+
}));
|
|
827
894
|
|
|
828
895
|
c.Set("identityMatrix", Napi::Function::New(env, CTX_0(cairo_identity_matrix)));
|
|
829
896
|
c.Set("newSubPath", Napi::Function::New(env, CTX_0(cairo_new_sub_path)));
|
|
@@ -970,6 +1037,35 @@ void InitCairo(Napi::Env env, Napi::Object exports) {
|
|
|
970
1037
|
return MakeCairoHandle(env, p, CairoKind::Pattern);
|
|
971
1038
|
}));
|
|
972
1039
|
|
|
1040
|
+
// popGroup: cairo_pop_group returns a NEW pattern (transfer-full) — the handle
|
|
1041
|
+
// adopts that ref directly, no extra reference (gjs wraps and then drops its own).
|
|
1042
|
+
c.Set("popGroup", Napi::Function::New(env, +[](const Napi::CallbackInfo& info) -> Napi::Value {
|
|
1043
|
+
Napi::Env env = info.Env();
|
|
1044
|
+
cairo_t* cr = CtxArg(env, info);
|
|
1045
|
+
if (cr == nullptr) return env.Undefined();
|
|
1046
|
+
cairo_pattern_t* p = cairo_pop_group(cr);
|
|
1047
|
+
if (!CheckStatus(env, cairo_status(cr), "context")) {
|
|
1048
|
+
cairo_pattern_destroy(p);
|
|
1049
|
+
return env.Undefined();
|
|
1050
|
+
}
|
|
1051
|
+
return MakeCairoHandle(env, p, CairoKind::Pattern);
|
|
1052
|
+
}));
|
|
1053
|
+
|
|
1054
|
+
// getTarget/getGroupTarget: the surface belongs to the context — ref it so the
|
|
1055
|
+
// JS wrapper owns one (mirrors CairoSurface::from_c_ptr's kept reference).
|
|
1056
|
+
#define CTX_SURFACE_GETTER(NAME, CFN) \
|
|
1057
|
+
c.Set(NAME, Napi::Function::New(env, +[](const Napi::CallbackInfo& info) -> Napi::Value { \
|
|
1058
|
+
Napi::Env env = info.Env(); \
|
|
1059
|
+
cairo_t* cr = CtxArg(env, info); \
|
|
1060
|
+
if (cr == nullptr) return env.Undefined(); \
|
|
1061
|
+
cairo_surface_t* s = CFN(cr); \
|
|
1062
|
+
if (!CheckStatus(env, cairo_status(cr), "context")) return env.Undefined(); \
|
|
1063
|
+
cairo_surface_reference(s); \
|
|
1064
|
+
return MakeCairoHandle(env, s, CairoKind::Surface); \
|
|
1065
|
+
}));
|
|
1066
|
+
CTX_SURFACE_GETTER("getTarget", cairo_get_target)
|
|
1067
|
+
CTX_SURFACE_GETTER("getGroupTarget", cairo_get_group_target)
|
|
1068
|
+
|
|
973
1069
|
// ---- $dispose (Context / Surface / Pattern / Path) ----
|
|
974
1070
|
// Eagerly destroy the underlying cairo object + mark the handle so the finalizer
|
|
975
1071
|
// won't double-free and any further method call throws (a live-handle check).
|
package/src/class.cc
CHANGED
|
@@ -44,6 +44,8 @@ static GType UnwrapGType(Napi::Env env, Napi::Value v) {
|
|
|
44
44
|
static GType TypeNameToGType(const std::string& t) {
|
|
45
45
|
if (t == "string" || t == "utf8") return G_TYPE_STRING;
|
|
46
46
|
if (t == "boolean" || t == "bool") return G_TYPE_BOOLEAN;
|
|
47
|
+
if (t == "char") return G_TYPE_CHAR;
|
|
48
|
+
if (t == "uchar") return G_TYPE_UCHAR;
|
|
47
49
|
if (t == "int") return G_TYPE_INT;
|
|
48
50
|
if (t == "uint") return G_TYPE_UINT;
|
|
49
51
|
if (t == "int64") return G_TYPE_INT64;
|
|
@@ -95,6 +97,19 @@ static GParamSpec* BuildParamSpec(Napi::Env env, Napi::Object spec, std::string*
|
|
|
95
97
|
: (def.IsNumber() ? NodeGiToBool(def) : FALSE);
|
|
96
98
|
return g_param_spec_boolean(nm, nm, nm, d, flags);
|
|
97
99
|
}
|
|
100
|
+
if (type == "char") {
|
|
101
|
+
return g_param_spec_char(nm, nm, nm, hasMin ? static_cast<gint8>(mn) : G_MININT8,
|
|
102
|
+
hasMax ? static_cast<gint8>(mx) : G_MAXINT8,
|
|
103
|
+
def.IsNumber() ? static_cast<gint8>(def.As<Napi::Number>().Int32Value()) : 0,
|
|
104
|
+
flags);
|
|
105
|
+
}
|
|
106
|
+
if (type == "uchar") {
|
|
107
|
+
return g_param_spec_uchar(nm, nm, nm, hasMin ? static_cast<guint8>(mn) : 0,
|
|
108
|
+
hasMax ? static_cast<guint8>(mx) : G_MAXUINT8,
|
|
109
|
+
def.IsNumber() ? static_cast<guint8>(def.As<Napi::Number>().Uint32Value())
|
|
110
|
+
: 0,
|
|
111
|
+
flags);
|
|
112
|
+
}
|
|
98
113
|
if (type == "int") {
|
|
99
114
|
return g_param_spec_int(nm, nm, nm, hasMin ? static_cast<gint>(mn) : G_MININT,
|
|
100
115
|
hasMax ? static_cast<gint>(mx) : G_MAXINT,
|