@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 +63 -0
- package/cairo.js +118 -17
- package/gettext.js +29 -7
- package/gi.js +204 -87
- package/host.nativescript.js +6 -0
- package/host.node.js +6 -0
- package/index.js +192 -76
- package/overrides/gio-settings.js +2 -24
- 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 +162 -1
- package/src/calls.cc +59 -26
- package/src/class.cc +15 -0
- package/src/common.h +177 -1
- package/src/marshal.cc +77 -26
- package/src/object.cc +79 -10
- package/src/private.cc +33 -0
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,237 @@ 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
|
+
connectSignal = addon.connectSignal;
|
|
98
|
+
emitSignal = addon.emitSignal;
|
|
99
|
+
disconnectSignal = addon.disconnectSignal;
|
|
100
|
+
setTemplateCallbackResolver = addon.setTemplateCallbackResolver;
|
|
101
|
+
setConstructCallback = addon.setConstructCallback;
|
|
102
|
+
setPropertySetCallback = addon.setPropertySetCallback;
|
|
103
|
+
storedPropertyNames = addon.storedPropertyNames;
|
|
104
|
+
logSetWriterFunc = addon.logSetWriterFunc;
|
|
105
|
+
logSetWriterDefault = addon.logSetWriterDefault;
|
|
106
|
+
bindPropertyFull = addon.bindPropertyFull;
|
|
107
|
+
bindingGroupBindFull = addon.bindingGroupBindFull;
|
|
108
|
+
setThreadLocale = addon.setThreadLocale;
|
|
109
|
+
textdomain = addon.textdomain;
|
|
110
|
+
bindtextdomain = addon.bindtextdomain;
|
|
111
|
+
localeCategories = addon.localeCategories;
|
|
112
|
+
probeHostOpenGL = addon.probeHostOpenGL;
|
|
113
|
+
currentGLStrings = addon.currentGLStrings;
|
|
114
|
+
loaded = true;
|
|
115
|
+
for (const hook of loadHooks.splice(0)) hook();
|
|
116
|
+
return native;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** Whether the addon is loaded now — what the lazy host makes observable. */
|
|
120
|
+
export function isNativeLoaded() {
|
|
121
|
+
return loaded;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Run `hook` once the addon is loaded: now if it is, else straight after the first load. The
|
|
126
|
+
* L1 module-scope registrations (error builder, template resolver, …) go through this, so they
|
|
127
|
+
* run at import on desktop and at first use on a lazy host.
|
|
128
|
+
* @param {() => void} hook
|
|
129
|
+
*/
|
|
130
|
+
export function onNativeLoaded(hook) {
|
|
131
|
+
if (loaded) hook();
|
|
132
|
+
else loadHooks.push(hook);
|
|
133
|
+
}
|
|
21
134
|
|
|
22
135
|
// Straight re-exports of the native engine surface. Each contract — arguments,
|
|
23
136
|
// ownership, the OUT/INOUT return-tuple convention, the keep-alive rules of the
|
|
24
137
|
// main-loop group — is documented once in index.d.ts, the published type surface;
|
|
25
138
|
// 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
|
|
139
|
+
export let requireNamespace;
|
|
140
|
+
export let listInfoNames;
|
|
141
|
+
export let findInfo;
|
|
142
|
+
export let getConstantValue;
|
|
143
|
+
export let getEnumValues;
|
|
144
|
+
export let getErrorDomain;
|
|
145
|
+
export let errorDomainInfo;
|
|
146
|
+
export let setErrorBuilder;
|
|
147
|
+
export let prependSearchPath;
|
|
148
|
+
export let prependLibraryPath;
|
|
149
|
+
export let callFunction;
|
|
150
|
+
export let callMethod;
|
|
151
|
+
export let hasMethod;
|
|
152
|
+
export let hasClassMethod;
|
|
153
|
+
export let classMethodArity;
|
|
154
|
+
export let callStaticMethod;
|
|
155
|
+
export let constructStruct;
|
|
156
|
+
export let newObject;
|
|
157
|
+
export let registerClass;
|
|
45
158
|
|
|
46
159
|
// Subclass an ALREADY-REGISTERED parent by its GType handle instead of a
|
|
47
160
|
// `namespace.typeName`: a registered (dynamic) parent has no introspection entry, so it
|
|
48
161
|
// cannot be resolved by name. Custom properties, signals and vfunc slots of registered
|
|
49
162
|
// ancestors compose through ordinary GObject inheritance. The L1 `GObject.registerClass`
|
|
50
163
|
// 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
|
|
164
|
+
export let registerClassFromGType;
|
|
165
|
+
|
|
166
|
+
export let constructType;
|
|
167
|
+
export let callParentVfunc;
|
|
168
|
+
export let hasClassVfunc;
|
|
169
|
+
export let callClassVfunc;
|
|
170
|
+
export let getTemplateChild;
|
|
171
|
+
export let getProperty;
|
|
172
|
+
export let setProperty;
|
|
173
|
+
export let hasProperty;
|
|
174
|
+
export let getTypeName;
|
|
175
|
+
export let classInfoForTypeName;
|
|
176
|
+
export let getGType;
|
|
177
|
+
export let isInstanceOf;
|
|
178
|
+
export let isGObjectHandle;
|
|
179
|
+
export let newGValue;
|
|
180
|
+
export let callBoxedMethod;
|
|
181
|
+
export let isBoxedHandle;
|
|
182
|
+
export let boxedMemberKind;
|
|
183
|
+
export let getBoxedField;
|
|
184
|
+
export let setBoxedField;
|
|
185
|
+
export let boxedTypeName;
|
|
73
186
|
/** TEST-ONLY: the address a boxed handle wraps, so an ownership rule about pointer
|
|
74
187
|
* IDENTITY can be asserted instead of waited on (see marshal.cc BoxedAddress). */
|
|
75
|
-
export
|
|
76
|
-
export
|
|
188
|
+
export let __boxedAddress;
|
|
189
|
+
export let isParamSpecHandle;
|
|
77
190
|
|
|
78
191
|
// True for a non-GObject GObject-fundamental handle (a GskRenderNode from
|
|
79
192
|
// Gtk.Snapshot.to_node, a GdkEvent): introspected as object info, but ref-counted
|
|
80
193
|
// through its own ref/unref funcs, NOT g_object_ref. L1 surfaces it as an opaque,
|
|
81
194
|
// 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
|
-
export
|
|
96
|
-
export
|
|
97
|
-
export
|
|
98
|
-
export
|
|
195
|
+
export let isFundamentalHandle;
|
|
196
|
+
|
|
197
|
+
export let paramSpecProp;
|
|
198
|
+
export let variantNew;
|
|
199
|
+
export let variantUnpack;
|
|
200
|
+
export let variantGetTypeString;
|
|
201
|
+
export let isVariantHandle;
|
|
202
|
+
export let startMainLoop;
|
|
203
|
+
export let iterateMainContext;
|
|
204
|
+
export let mainContextHasPending;
|
|
205
|
+
export let makePumpPendingCount;
|
|
206
|
+
export let pumpKick;
|
|
207
|
+
export let setMicrotaskDrain;
|
|
208
|
+
export let connectSignal;
|
|
209
|
+
export let emitSignal;
|
|
210
|
+
export let disconnectSignal;
|
|
211
|
+
export let setTemplateCallbackResolver;
|
|
99
212
|
|
|
100
213
|
// Registers the L1 callback the engine's overridden `constructor` vfunc invokes to run a
|
|
101
214
|
// registered class's JS constructor for a GObject that C instantiated (a GtkBuilder
|
|
102
215
|
// composite-template InternalChild): given (instanceHandle, gtypeName) it
|
|
103
216
|
// Reflect.constructs the class in adopt mode — see gi.js runCtorForCObject.
|
|
104
|
-
export
|
|
217
|
+
export let setConstructCallback;
|
|
105
218
|
|
|
106
219
|
// Registers the L1 callback the engine's set_property vfunc invokes after storing a
|
|
107
220
|
// custom property: given (instanceHandle, propertyName) it runs the class's own JS
|
|
108
221
|
// setter — see gi.js runJsPropertySetter. The engine calls it only once the instance
|
|
109
222
|
// HAS a wrapper, so a construct-time set never reaches it; those are replayed from
|
|
110
223
|
// the base ctor over storedPropertyNames.
|
|
111
|
-
export
|
|
224
|
+
export let setPropertySetCallback;
|
|
112
225
|
|
|
113
226
|
// The custom properties actually SET on an instance (the per-instance store's keys) —
|
|
114
227
|
// what that construct-time replay has to push through the class's JS setters.
|
|
115
|
-
export
|
|
228
|
+
export let storedPropertyNames;
|
|
116
229
|
|
|
117
|
-
export
|
|
118
|
-
export
|
|
119
|
-
export
|
|
120
|
-
export
|
|
230
|
+
export let logSetWriterFunc;
|
|
231
|
+
export let logSetWriterDefault;
|
|
232
|
+
export let bindPropertyFull;
|
|
233
|
+
export let bindingGroupBindFull;
|
|
121
234
|
|
|
122
235
|
// The libintl half of GjsPrivate — the binders GLib's GIR does not publish, plus
|
|
123
236
|
// the LC_* constants read from the host's own <locale.h>. Consumed by
|
|
124
237
|
// `./gettext.js`; loading THIS module is also what puts the process in the
|
|
125
238
|
// environment's locale (the addon's Init does it, as gjs's entry point does).
|
|
126
|
-
export
|
|
127
|
-
export
|
|
128
|
-
export
|
|
129
|
-
export
|
|
239
|
+
export let setThreadLocale;
|
|
240
|
+
export let textdomain;
|
|
241
|
+
export let bindtextdomain;
|
|
242
|
+
export let localeCategories;
|
|
130
243
|
|
|
131
244
|
// win32 only (undefined elsewhere): which OpenGL the process got — see gtk-runtime.js
|
|
132
245
|
// activateBundledOpenGL. currentGLStrings asks the implementation behind the context
|
|
133
246
|
// current on this thread, so a caller can tell Mesa from a vendor driver.
|
|
134
|
-
export
|
|
135
|
-
export
|
|
247
|
+
export let probeHostOpenGL;
|
|
248
|
+
export let currentGLStrings;
|
|
249
|
+
|
|
250
|
+
export { native as default };
|
|
136
251
|
|
|
137
|
-
export
|
|
252
|
+
// Last, so every `export let` above is initialised before the eager load assigns it.
|
|
253
|
+
if (!LAZY_NATIVE) ensureNative();
|
|
@@ -10,16 +10,8 @@
|
|
|
10
10
|
// throws; this is the same wrapper, so `try { … } catch` means the same on either runtime
|
|
11
11
|
// (ADR 0099 conformance row `errors`).
|
|
12
12
|
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
// • `set_int` / `set_uint` range-check their value, which in GJS is the argument marshaller's
|
|
16
|
-
// job. node-gi's marshaller wraps modulo 2^32 (`set_uint('k', -1)` stores 4294967295), a
|
|
17
|
-
// general divergence recorded in status/open-todos/node-gi.md; this keeps the settings
|
|
18
|
-
// surface honest without touching the addon, so it also holds in an already-built payload.
|
|
19
|
-
|
|
20
|
-
const INT32_MIN = -(2 ** 31);
|
|
21
|
-
const INT32_MAX = 2 ** 31 - 1;
|
|
22
|
-
const UINT32_MAX = 2 ** 32 - 1;
|
|
13
|
+
// One deliberate difference from the GJS override: the constructor check is a Proxy `construct`
|
|
14
|
+
// trap — a node-gi class has no `_init` to wrap.
|
|
23
15
|
|
|
24
16
|
const CHECKED = [
|
|
25
17
|
'get_boolean',
|
|
@@ -53,12 +45,6 @@ const CHECKED = [
|
|
|
53
45
|
'reset',
|
|
54
46
|
];
|
|
55
47
|
|
|
56
|
-
/** The range a setter's JS number must be in; GJS's marshaller throws this exact message. */
|
|
57
|
-
const RANGES = {
|
|
58
|
-
set_int: [INT32_MIN, INT32_MAX, 'int32'],
|
|
59
|
-
set_uint: [0, UINT32_MAX, 'uint32'],
|
|
60
|
-
};
|
|
61
|
-
|
|
62
48
|
/**
|
|
63
49
|
* Patch `Gio.Settings` / `Gio.SettingsSchema` like GJS does, once, and return the class to hand
|
|
64
50
|
* out (a Proxy whose `new` refuses a schema that is not installed instead of aborting).
|
|
@@ -87,16 +73,8 @@ export function createGioSettings(Gio) {
|
|
|
87
73
|
for (const name of CHECKED) {
|
|
88
74
|
const real = proto[name];
|
|
89
75
|
if (typeof real !== 'function') continue;
|
|
90
|
-
const range = RANGES[name];
|
|
91
76
|
proto[name] = function (key, ...args) {
|
|
92
77
|
checkKey(this, key);
|
|
93
|
-
if (range !== undefined) {
|
|
94
|
-
const [min, max, type] = range;
|
|
95
|
-
const value = args[0];
|
|
96
|
-
if (typeof value !== 'number' || !Number.isFinite(value) || value < min || value > max) {
|
|
97
|
-
throw new Error(`Argument value: value is out of range for ${type}`);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
78
|
return real.call(this, key, ...args);
|
|
101
79
|
};
|
|
102
80
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gjsify/node-gi",
|
|
3
|
-
"version": "0.64.
|
|
3
|
+
"version": "0.64.2",
|
|
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
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
// WrapBoxed; cairo_path_t is NOT ref-counted, so a borrowed path is COPIED via
|
|
18
18
|
// the gjs CairoPath::copy_ptr dummy-context trick instead of ref'd). Slice:
|
|
19
19
|
// Context (drawing ops, path copy/append, dash, matrix point transforms,
|
|
20
|
-
// in-fill/in-stroke, getSource
|
|
20
|
+
// in-fill/in-stroke, getSource, the toy-text API: selectFontFace/setFontSize/
|
|
21
|
+
// showText/textPath/textExtents/fontExtents), Surface + ImageSurface (headless pixels),
|
|
21
22
|
// SolidPattern, LinearGradient/RadialGradient (color stops), SurfacePattern
|
|
22
23
|
// (extend/filter), Path handles. Deferred: region, PDF/SVG/PS surfaces,
|
|
23
24
|
// text/font ops (showText/selectFontFace — canvas2d text rides PangoCairo).
|
|
@@ -620,6 +621,11 @@ void InitCairo(Napi::Env env, Napi::Object exports) {
|
|
|
620
621
|
c.Set("paint", Napi::Function::New(env, CTX_0(cairo_paint)));
|
|
621
622
|
c.Set("clip", Napi::Function::New(env, CTX_0(cairo_clip)));
|
|
622
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)));
|
|
623
629
|
|
|
624
630
|
c.Set("setLineWidth", Napi::Function::New(env, CTX_1D(cairo_set_line_width)));
|
|
625
631
|
c.Set("paintWithAlpha", Napi::Function::New(env, CTX_1D(cairo_paint_with_alpha)));
|
|
@@ -640,7 +646,10 @@ void InitCairo(Napi::Env env, Napi::Object exports) {
|
|
|
640
646
|
c.Set("arc", Napi::Function::New(env, CTX_5D(cairo_arc)));
|
|
641
647
|
c.Set("arcNegative", Napi::Function::New(env, CTX_5D(cairo_arc_negative)));
|
|
642
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)));
|
|
643
650
|
|
|
651
|
+
c.Set("pushGroupWithContent",
|
|
652
|
+
Napi::Function::New(env, CTX_ENUM(cairo_push_group_with_content, cairo_content_t)));
|
|
644
653
|
c.Set("setOperator", Napi::Function::New(env, CTX_ENUM(cairo_set_operator, cairo_operator_t)));
|
|
645
654
|
c.Set("setLineCap", Napi::Function::New(env, CTX_ENUM(cairo_set_line_cap, cairo_line_cap_t)));
|
|
646
655
|
c.Set("setLineJoin", Napi::Function::New(env, CTX_ENUM(cairo_set_line_join, cairo_line_join_t)));
|
|
@@ -733,6 +742,109 @@ void InitCairo(Napi::Env env, Napi::Object exports) {
|
|
|
733
742
|
CTX_EXTENTS("fillExtents", cairo_fill_extents)
|
|
734
743
|
CTX_EXTENTS("strokeExtents", cairo_stroke_extents)
|
|
735
744
|
CTX_EXTENTS("clipExtents", cairo_clip_extents)
|
|
745
|
+
// Toy text API (cairo_select_font_face & co), the GJS-compatible Context text ops.
|
|
746
|
+
// textExtents/fontExtents return the object shape gjs's cairo-context.cpp builds.
|
|
747
|
+
c.Set("selectFontFace", Napi::Function::New(env, +[](const Napi::CallbackInfo& info) -> Napi::Value {
|
|
748
|
+
Napi::Env env = info.Env();
|
|
749
|
+
cairo_t* cr = CtxArg(env, info);
|
|
750
|
+
if (cr == nullptr) return env.Undefined();
|
|
751
|
+
std::string family = info[1].ToString().Utf8Value();
|
|
752
|
+
cairo_select_font_face(cr, family.c_str(),
|
|
753
|
+
static_cast<cairo_font_slant_t>(info[2].ToNumber().Int32Value()),
|
|
754
|
+
static_cast<cairo_font_weight_t>(info[3].ToNumber().Int32Value()));
|
|
755
|
+
CheckStatus(env, cairo_status(cr), "selectFontFace");
|
|
756
|
+
return env.Undefined();
|
|
757
|
+
}));
|
|
758
|
+
c.Set("setFontSize", Napi::Function::New(env, CTX_1D(cairo_set_font_size)));
|
|
759
|
+
c.Set("showText", Napi::Function::New(env, +[](const Napi::CallbackInfo& info) -> Napi::Value {
|
|
760
|
+
Napi::Env env = info.Env();
|
|
761
|
+
cairo_t* cr = CtxArg(env, info);
|
|
762
|
+
if (cr == nullptr) return env.Undefined();
|
|
763
|
+
std::string text = info[1].ToString().Utf8Value();
|
|
764
|
+
cairo_show_text(cr, text.c_str());
|
|
765
|
+
CheckStatus(env, cairo_status(cr), "showText");
|
|
766
|
+
return env.Undefined();
|
|
767
|
+
}));
|
|
768
|
+
c.Set("textPath", Napi::Function::New(env, +[](const Napi::CallbackInfo& info) -> Napi::Value {
|
|
769
|
+
Napi::Env env = info.Env();
|
|
770
|
+
cairo_t* cr = CtxArg(env, info);
|
|
771
|
+
if (cr == nullptr) return env.Undefined();
|
|
772
|
+
std::string text = info[1].ToString().Utf8Value();
|
|
773
|
+
cairo_text_path(cr, text.c_str());
|
|
774
|
+
CheckStatus(env, cairo_status(cr), "textPath");
|
|
775
|
+
return env.Undefined();
|
|
776
|
+
}));
|
|
777
|
+
c.Set("textExtents", Napi::Function::New(env, +[](const Napi::CallbackInfo& info) -> Napi::Value {
|
|
778
|
+
Napi::Env env = info.Env();
|
|
779
|
+
cairo_t* cr = CtxArg(env, info);
|
|
780
|
+
if (cr == nullptr) return env.Undefined();
|
|
781
|
+
std::string text = info[1].ToString().Utf8Value();
|
|
782
|
+
cairo_text_extents_t e;
|
|
783
|
+
cairo_text_extents(cr, text.c_str(), &e);
|
|
784
|
+
if (!CheckStatus(env, cairo_status(cr), "textExtents")) return env.Undefined();
|
|
785
|
+
Napi::Object o = Napi::Object::New(env);
|
|
786
|
+
o.Set("xBearing", Napi::Number::New(env, e.x_bearing));
|
|
787
|
+
o.Set("yBearing", Napi::Number::New(env, e.y_bearing));
|
|
788
|
+
o.Set("width", Napi::Number::New(env, e.width));
|
|
789
|
+
o.Set("height", Napi::Number::New(env, e.height));
|
|
790
|
+
o.Set("xAdvance", Napi::Number::New(env, e.x_advance));
|
|
791
|
+
o.Set("yAdvance", Napi::Number::New(env, e.y_advance));
|
|
792
|
+
return o;
|
|
793
|
+
}));
|
|
794
|
+
c.Set("fontExtents", Napi::Function::New(env, +[](const Napi::CallbackInfo& info) -> Napi::Value {
|
|
795
|
+
Napi::Env env = info.Env();
|
|
796
|
+
cairo_t* cr = CtxArg(env, info);
|
|
797
|
+
if (cr == nullptr) return env.Undefined();
|
|
798
|
+
cairo_font_extents_t e;
|
|
799
|
+
cairo_font_extents(cr, &e);
|
|
800
|
+
if (!CheckStatus(env, cairo_status(cr), "fontExtents")) return env.Undefined();
|
|
801
|
+
Napi::Object o = Napi::Object::New(env);
|
|
802
|
+
o.Set("ascent", Napi::Number::New(env, e.ascent));
|
|
803
|
+
o.Set("descent", Napi::Number::New(env, e.descent));
|
|
804
|
+
o.Set("height", Napi::Number::New(env, e.height));
|
|
805
|
+
o.Set("maxXAdvance", Napi::Number::New(env, e.max_x_advance));
|
|
806
|
+
o.Set("maxYAdvance", Napi::Number::New(env, e.max_y_advance));
|
|
807
|
+
return o;
|
|
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
|
+
}));
|
|
736
848
|
c.Set("status", Napi::Function::New(env, +[](const Napi::CallbackInfo& info) -> Napi::Value {
|
|
737
849
|
Napi::Env env = info.Env();
|
|
738
850
|
cairo_t* cr = CtxArg(env, info);
|
|
@@ -759,6 +871,26 @@ void InitCairo(Napi::Env env, Napi::Object exports) {
|
|
|
759
871
|
CheckStatus(env, cairo_status(cr), "context");
|
|
760
872
|
return env.Undefined();
|
|
761
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
|
+
}));
|
|
762
894
|
|
|
763
895
|
c.Set("identityMatrix", Napi::Function::New(env, CTX_0(cairo_identity_matrix)));
|
|
764
896
|
c.Set("newSubPath", Napi::Function::New(env, CTX_0(cairo_new_sub_path)));
|
|
@@ -905,6 +1037,35 @@ void InitCairo(Napi::Env env, Napi::Object exports) {
|
|
|
905
1037
|
return MakeCairoHandle(env, p, CairoKind::Pattern);
|
|
906
1038
|
}));
|
|
907
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
|
+
|
|
908
1069
|
// ---- $dispose (Context / Surface / Pattern / Path) ----
|
|
909
1070
|
// Eagerly destroy the underlying cairo object + mark the handle so the finalizer
|
|
910
1071
|
// won't double-free and any further method call throws (a live-handle check).
|