node-gtk 1.0.0 → 2.1.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/README.md +123 -190
- package/bin/node-gtk.js +31 -0
- package/lib/bootstrap.js +22 -4
- package/lib/index.js +25 -0
- package/lib/loop.js +34 -0
- package/lib/overrides/GLib-2.0.js +12 -9
- package/lib/overrides/Gio-2.0.js +26 -0
- package/lib/overrides/Gtk-3.0.js +17 -12
- package/lib/overrides/Gtk-4.0.js +4 -2
- package/lib/register-class.js +1 -1
- package/package.json +9 -2
- package/scripts/build-test-fixtures.js +237 -0
- package/scripts/ci.sh +5 -3
- package/src/boxed.cc +33 -3
- package/src/boxed.h +13 -0
- package/src/callback.cc +12 -0
- package/src/callback.h +1 -0
- package/src/closure.cc +110 -2
- package/src/function.cc +68 -7
- package/src/function.h +1 -0
- package/src/gi.cc +72 -0
- package/src/gobject.cc +148 -27
- package/src/loop.cc +39 -3
- package/src/loop.h +2 -0
- package/src/type.cc +3 -2
- package/src/value.cc +369 -31
- package/src/value.h +22 -0
- package/tools/README.md +91 -0
- package/tools/generate-types.js +1045 -0
- package/lib/binding/node-v102-linux-x64/node_gtk.node +0 -0
- package/lib/binding/node-v108-linux-x64/node_gtk.node +0 -0
- package/lib/binding/node-v115-linux-x64/node_gtk.node +0 -0
- package/lib/binding/node-v127-linux-x64/node_gtk.node +0 -0
- package/lib/binding/node-v93-linux-x64/node_gtk.node +0 -0
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
</p>
|
|
17
17
|
|
|
18
18
|
Node-Gtk is a [gobject-introspection](https://gi.readthedocs.io/en/latest) library for nodejs. It makes it possible to
|
|
19
|
-
use any introspected library, such as Gtk+, usable.
|
|
19
|
+
use any introspected library, such as Gtk+, usable. It is similar in essence to [GJS](https://wiki.gnome.org/action/show/Projects/Gjs) or [PyGObject](https://pygobject.readthedocs.io). Please note this project is currently in a _beta_ state and is being developed. Any contributors willing to help
|
|
20
20
|
will be welcomed.
|
|
21
21
|
|
|
22
22
|
Supported Node.js versions: **20**, **22**, **24** (other versions may work but are untested)<br>
|
|
@@ -26,232 +26,165 @@ Pre-built binaries available for: **Linux**, **macOS**
|
|
|
26
26
|
|
|
27
27
|
- [Usage](#usage)
|
|
28
28
|
- [Documentation](#documentation)
|
|
29
|
+
- [TypeScript](#typescript)
|
|
29
30
|
- [Installing and building](#installing-and-building)
|
|
30
|
-
- [Target Platforms](#target-platforms)
|
|
31
|
-
- [Requirements](#requirements)
|
|
32
|
-
- [How to build on Ubuntu](#how-to-build-on-ubuntu)
|
|
33
|
-
- [How to build on Fedora](#how-to-build-on-fedora)
|
|
34
|
-
- [How to build on ArchLinux](#how-to-build-on-archlinux)
|
|
35
|
-
- [How to build on macOS](#how-to-build-on-macos)
|
|
36
|
-
- [How to build on Windows](#how-to-build-on-windows)
|
|
37
|
-
- [Testing the project](#testing-the-project)
|
|
38
|
-
- [Browser demo](#browser-demo)
|
|
39
31
|
- [Contributing](#contributing)
|
|
40
32
|
|
|
41
33
|
## Usage
|
|
42
34
|
|
|
43
|
-
Below is a minimal example of how to use
|
|
44
|
-
our [template](https://github.com/romgrk/node-gtk-template) or at
|
|
45
|
-
[react-gtk](https://github.com/codejamninja/react-gtk) to bootstrap your
|
|
46
|
-
project.
|
|
35
|
+
Below is a [minimal example](./examples/hello-world.js) of how to use node-gtk:
|
|
47
36
|
|
|
48
37
|
```javascript
|
|
49
|
-
const gi = require('node-gtk')
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
gi.
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
38
|
+
const gi = require('node-gtk');
|
|
39
|
+
const GLib = gi.require('GLib', '2.0');
|
|
40
|
+
const Gtk = gi.require('Gtk', '4.0');
|
|
41
|
+
const Adw = gi.require('Adw', '1');
|
|
42
|
+
|
|
43
|
+
const loop = GLib.MainLoop.new(null, false);
|
|
44
|
+
const app = new Adw.Application('com.github.romgrk.node-gtk.hello', 0);
|
|
45
|
+
|
|
46
|
+
app.on('activate', () => {
|
|
47
|
+
const content = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL });
|
|
48
|
+
content.append(new Adw.HeaderBar());
|
|
49
|
+
content.append(new Gtk.Label({ label: 'Hello Adwaita!', vexpand: true }));
|
|
50
|
+
|
|
51
|
+
const window = new Adw.ApplicationWindow(app);
|
|
52
|
+
window.setTitle('node-gtk');
|
|
53
|
+
window.setDefaultSize(300, 120);
|
|
54
|
+
window.setContent(content);
|
|
55
|
+
window.on('close-request', () => (loop.quit(), app.quit(), false));
|
|
56
|
+
window.present();
|
|
57
|
+
|
|
58
|
+
gi.startLoop();
|
|
59
|
+
loop.run();
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
process.exit(app.run([]));
|
|
64
63
|
```
|
|
65
64
|
|
|
66
65
|
<p align="center">
|
|
67
|
-
<img src="./img/hello-
|
|
66
|
+
<img src="./img/hello-world.png" style="width: 290px; height: auto;"/>
|
|
68
67
|
</p>
|
|
69
68
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
69
|
+
#### ES modules
|
|
70
|
+
|
|
71
|
+
The example above is CommonJS. node-gtk also works under ES modules, but with one
|
|
72
|
+
behavioral difference around the blocking main-loop calls (`GLib.MainLoop.run`,
|
|
73
|
+
`Gio`/`Gtk.Application.run`, `Gtk.main`).
|
|
74
|
+
|
|
75
|
+
Under ESM, a module's top-level body runs as a V8 *microtask*. If a blocking
|
|
76
|
+
loop call ran synchronously from there, it would nest inside V8's microtask
|
|
77
|
+
drain and starve every `Promise`/`async` continuation (so `await fetch(...)`,
|
|
78
|
+
`fs/promises`, etc. would never settle) for the entire lifetime of the loop —
|
|
79
|
+
see [#442](https://github.com/romgrk/node-gtk/issues/442). To avoid this,
|
|
80
|
+
node-gtk defers the blocking call to the next event-loop tick when it detects it
|
|
81
|
+
is being invoked from within a microtask. Two consequences for ESM code:
|
|
82
|
+
|
|
83
|
+
- The run call **returns immediately** instead of blocking until quit, so any
|
|
84
|
+
code placed *after* it executes *before* the loop. Make the run call the last
|
|
85
|
+
statement, and do cleanup/exit from your quit/`close-request` handler.
|
|
86
|
+
- Its **return value is not available** (e.g. the application's exit status), so
|
|
87
|
+
don't wrap it like `process.exit(app.run([]))` — call `app.run([])` on its own
|
|
88
|
+
and exit from the handler instead.
|
|
89
|
+
|
|
90
|
+
Under CommonJS (and inside signal callbacks) nothing changes: the run calls block
|
|
91
|
+
synchronously and return their value exactly as before.
|
|
92
|
+
|
|
93
|
+
> **Design note (may be reconsidered).** Two approaches were considered for #442:
|
|
94
|
+
>
|
|
95
|
+
> - **Transparent auto-defer (chosen).** node-gtk detects the microtask context
|
|
96
|
+
> and defers the blocking call automatically, so existing ESM code keeps
|
|
97
|
+
> working with no changes. The cost is the leaky behavior above: under ESM the
|
|
98
|
+
> run call no longer blocks, which is surprising and silently breaks idioms
|
|
99
|
+
> like `process.exit(app.run([]))`.
|
|
100
|
+
> - **Explicit async API (alternative).** Keep the blocking calls strictly
|
|
101
|
+
> synchronous and add awaitable variants (e.g. `await loop.runAsync()`),
|
|
102
|
+
> leaving ESM users to opt in. This has clean, predictable semantics and no
|
|
103
|
+
> hidden return-immediately behavior, but it does *not* transparently fix
|
|
104
|
+
> existing ESM code — users must change their code, and plain `loop.run()`
|
|
105
|
+
> would still starve microtasks under ESM (or would need to throw/warn).
|
|
106
|
+
>
|
|
107
|
+
> The transparent approach was chosen to fix existing code out of the box, but
|
|
108
|
+
> given the leaky semantics this trade-off may be revisited — possibly moving to
|
|
109
|
+
> (or also offering) the explicit async API.
|
|
110
|
+
|
|
111
|
+
You can also easily create custom applications:
|
|
112
|
+
|
|
113
|
+
[A web browser (using WebKit2GTK)](./examples/browser.js)
|
|
73
114
|
|
|
74
115
|
<p align="center">
|
|
75
|
-
<img src="./img/browser.png"
|
|
116
|
+
<img src="./img/browser.png" style="max-width: 500px; height: auto;"/>
|
|
76
117
|
</p>
|
|
77
118
|
|
|
119
|
+
[A system monitor](./examples/system-monitor.js)
|
|
78
120
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
## Installing and building
|
|
84
|
-
|
|
85
|
-
Note that prebuilt binaries are available for common systems, in those cases building is not necessary.
|
|
86
|
-
|
|
87
|
-
##### Target Platforms
|
|
88
|
-
|
|
89
|
-
- **Linux**: prebuilt binaries available
|
|
90
|
-
- **macOS**: prebuilt binaries available
|
|
91
|
-
- **Windows**: no prebuilt binaries
|
|
92
|
-
|
|
93
|
-
### Requirements
|
|
94
|
-
|
|
95
|
-
- `git`
|
|
96
|
-
- `nodejs@10` or higher
|
|
97
|
-
- `python3` (for `node-gyp`)
|
|
98
|
-
- C compiler (`gcc@8` or higher, or `clang`)
|
|
99
|
-
|
|
100
|
-
### How to build on Ubuntu
|
|
101
|
-
|
|
102
|
-
Install basic dependencies.
|
|
103
|
-
|
|
104
|
-
```sh
|
|
105
|
-
sudo apt-get install \
|
|
106
|
-
build-essential git \
|
|
107
|
-
gobject-introspection \
|
|
108
|
-
libgirepository1.0-dev \
|
|
109
|
-
libcairo2 \
|
|
110
|
-
libcairo2-dev
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
At this point `npm install node-gtk` should already install, fallback and build `node-gtk` without problems.
|
|
114
|
-
|
|
115
|
-
### How to build on Fedora
|
|
116
|
-
|
|
117
|
-
Install basic dependencies:
|
|
118
|
-
|
|
119
|
-
```sh
|
|
120
|
-
sudo dnf install \
|
|
121
|
-
@development-tools \
|
|
122
|
-
nodejs \
|
|
123
|
-
nodejs-devel \
|
|
124
|
-
gobject-introspection \
|
|
125
|
-
gobject-introspection-devel \
|
|
126
|
-
gtk3 \
|
|
127
|
-
gtk3-devel \
|
|
128
|
-
cairo \
|
|
129
|
-
cairo-devel
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
After installing of packages, run `npm install node-gtk`.
|
|
133
|
-
|
|
134
|
-
### How to build on ArchLinux
|
|
121
|
+
<p align="center">
|
|
122
|
+
<img src="./img/system-monitor.png" style="width: 400px; height: auto;"/>
|
|
123
|
+
</p>
|
|
135
124
|
|
|
136
|
-
|
|
125
|
+
#### Other projects
|
|
137
126
|
|
|
138
|
-
|
|
139
|
-
pacman -S --needed \
|
|
140
|
-
base-devel git \
|
|
141
|
-
nodejs npm \
|
|
142
|
-
gtk3 gobject-introspection \
|
|
143
|
-
cairo
|
|
144
|
-
```
|
|
127
|
+
The [react-gtk](https://github.com/codejamninja/react-gtk) project may also allow you to use GTK via React (unmaintained).
|
|
145
128
|
|
|
146
|
-
|
|
129
|
+
## Documentation
|
|
147
130
|
|
|
148
|
-
|
|
131
|
+
[Read our documentation here](./doc/index.md)
|
|
149
132
|
|
|
150
|
-
|
|
133
|
+
## TypeScript
|
|
151
134
|
|
|
152
|
-
|
|
135
|
+
node-gtk can generate TypeScript declarations for the libraries you use,
|
|
136
|
+
straight from the GObject-Introspection typelibs installed on your machine — so
|
|
137
|
+
the types always match your actual library versions and node-gtk's own runtime
|
|
138
|
+
shape (camelCase methods, signal callbacks, nullability, etc.).
|
|
153
139
|
|
|
154
140
|
```sh
|
|
155
|
-
|
|
141
|
+
# generates ./node_modules/.node-gtk-types (a hidden, git-ignored cache)
|
|
142
|
+
npx node-gtk generate-types Gtk-4.0 Adw-1
|
|
156
143
|
```
|
|
157
144
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
### How to build on Windows
|
|
145
|
+
The command emits one declaration file per namespace (plus the full dependency
|
|
146
|
+
closure) and a `node-gtk.d.ts` shim. Point your `tsconfig.json` at it:
|
|
161
147
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
# update the system
|
|
170
|
-
# in case of errors, wait for the update to complete
|
|
171
|
-
# then close and open again MingW shell
|
|
172
|
-
pacman -Syyu --noconfirm
|
|
173
|
-
|
|
174
|
-
# install git, gtk3 and extra dependencie
|
|
175
|
-
pacman -S --needed --noconfirm git mingw-w64-$(uname -m)-{gtk3,gobject-introspection,pkg-config,cairo}
|
|
176
|
-
|
|
177
|
-
# where to put the repository clone?
|
|
178
|
-
# pick your flder or use ~/oss (Open Source Software)
|
|
179
|
-
mkdir -p ~/oss/
|
|
180
|
-
cd ~/oss
|
|
181
|
-
|
|
182
|
-
# clone node-gtk there
|
|
183
|
-
git clone https://github.com/romgrk/node-gtk
|
|
184
|
-
cd node-gtk
|
|
185
|
-
|
|
186
|
-
# don't include /mingw64/include directly since it conflicts with
|
|
187
|
-
# Windows SDK headers. we copy needed headers to __extra__ directory:
|
|
188
|
-
./windows/mingw_include_extra.sh
|
|
189
|
-
|
|
190
|
-
# if MSYS2 is NOT installed in C:/msys64 run:
|
|
191
|
-
export MINGW_WINDOWS_PATH=$(./windows/mingw_windows_path.sh)
|
|
192
|
-
|
|
193
|
-
# first run might take a while
|
|
194
|
-
GYP_MSVS_VERSION=2017 npm install
|
|
148
|
+
```jsonc
|
|
149
|
+
{
|
|
150
|
+
"compilerOptions": {
|
|
151
|
+
"moduleResolution": "node16",
|
|
152
|
+
"paths": { "node-gtk": ["./node_modules/.node-gtk-types/node-gtk.d.ts"] }
|
|
153
|
+
}
|
|
154
|
+
}
|
|
195
155
|
```
|
|
196
156
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
The below blog post series will help you get started:
|
|
157
|
+
Then `gi.require` is fully typed — the namespace is inferred from the string
|
|
158
|
+
arguments:
|
|
201
159
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
3. [Package Node.js GTK Application on Windows](https://ten0s.github.io/blog/2022/07/27/package-nodejs-gtk-application-on-windows)
|
|
160
|
+
```ts
|
|
161
|
+
import * as gi from 'node-gtk'
|
|
205
162
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
the binary path might not be available.
|
|
210
|
-
|
|
211
|
-
In such case `python` won't be available either, and you can check via `which python` command.
|
|
212
|
-
|
|
213
|
-
If not found, you need to export the platform related binary path:
|
|
214
|
-
|
|
215
|
-
```sh
|
|
216
|
-
# example for the 32bit version
|
|
217
|
-
export PATH="/mingw32/bin:$PATH"
|
|
218
|
-
npm run install
|
|
163
|
+
const Gtk = gi.require('Gtk', '4.0') // typed as the Gtk-4.0 namespace
|
|
164
|
+
const win = new Gtk.ApplicationWindow({ title: 'Hello', defaultWidth: 400 })
|
|
165
|
+
win.on('close-request', () => false) // signal name + callback are typed
|
|
219
166
|
```
|
|
220
167
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
168
|
+
You get typed constructor properties (including inherited and interface ones),
|
|
169
|
+
camelCase methods with real return types, GI nullability, typed signal
|
|
170
|
+
overloads, enums, `bigint` for 64-bit integers, out-parameters surfaced as the
|
|
171
|
+
return value, and cross-namespace types. GNOME's API documentation is included
|
|
172
|
+
as JSDoc (with `@param`/`@returns`), so editors show it on hover — this reads the
|
|
173
|
+
`.gir` files installed by the libraries' `-dev`/`-devel` packages; pass
|
|
174
|
+
`--no-docs` for leaner output if they aren't installed or you don't want them.
|
|
224
175
|
|
|
225
|
-
|
|
226
|
-
|
|
176
|
+
Because the output is a generated cache under `node_modules`, add a `postinstall`
|
|
177
|
+
script so it regenerates on install:
|
|
227
178
|
|
|
228
|
-
```
|
|
229
|
-
node
|
|
179
|
+
```json
|
|
180
|
+
{ "scripts": { "postinstall": "node-gtk generate-types Gtk-4.0 Adw-1" } }
|
|
230
181
|
```
|
|
231
182
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
Please note in macOS the window doesn't automatically open above other windows.
|
|
235
|
-
Try <kbd>Cmd</kbd> + <kbd>Tab</kbd> if you don't see it.
|
|
183
|
+
Run `npx node-gtk generate-types --help` for options.
|
|
236
184
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
If you'd like to test `./examples/browser.js` you'll need [WebKit2 GTK+](http://webkitgtk.org/) libary.
|
|
240
|
-
|
|
241
|
-
- in **Ubuntu**, you can `apt-get install libwebkit2gtk-3.0` (`4.0` works too) and try it out.
|
|
242
|
-
- in **Fedora**, you should run `sudo dnf install webkit2gtk3`
|
|
243
|
-
- in **ArchLinux**, you can `pacman -S --needed webkitgtk` and try it out.
|
|
244
|
-
- in **macOS**, there is no way to run it right now because `webkitgtk` was removed from homebrew
|
|
245
|
-
|
|
246
|
-
Once installed, you can `./examples/browser.js google.com` or any other page, and you might try the _dark theme_ out too:
|
|
247
|
-
|
|
248
|
-
```sh
|
|
249
|
-
# macOS needs to have the Adwaita theme installed
|
|
250
|
-
# brew install adwaita-icon-theme
|
|
185
|
+
## Installing and building
|
|
251
186
|
|
|
252
|
-
|
|
253
|
-
./examples/browser.js google.com dark
|
|
254
|
-
```
|
|
187
|
+
See [Installing & building](./doc/installation.md) for prebuilt-binary notes, per-platform build instructions (Linux, macOS, Windows), and how to run the tests and examples.
|
|
255
188
|
|
|
256
189
|
## Contributing
|
|
257
190
|
|
|
@@ -259,9 +192,9 @@ If you'd like to help, we'd be more than happy to have support. To setup your de
|
|
|
259
192
|
run `npm run configure`. You can then build the project with `npm run build`. To generate the `compile_commands.json`
|
|
260
193
|
for LSP to work nicely, you can use [bear](https://github.com/rizsotto/Bear) as `bear -- npm run build`.
|
|
261
194
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
195
|
+
- https://developer.gnome.org/gi/stable/index.html
|
|
196
|
+
- https://v8docs.nodesource.com/
|
|
197
|
+
- https://github.com/nodejs/nan#api
|
|
265
198
|
|
|
266
199
|
There is a [Discord channel](https://discord.gg/r2VqPUV) but it receives little monitoring, use github issues or
|
|
267
200
|
discussions preferably.
|
package/bin/node-gtk.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/*
|
|
3
|
+
* node-gtk CLI
|
|
4
|
+
*
|
|
5
|
+
* Subcommands:
|
|
6
|
+
* generate-types Generate TypeScript declarations from the installed typelibs.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const cmd = process.argv[2]
|
|
10
|
+
|
|
11
|
+
switch (cmd) {
|
|
12
|
+
case 'generate-types':
|
|
13
|
+
require('../tools/generate-types.js').run(process.argv.slice(3))
|
|
14
|
+
break
|
|
15
|
+
case undefined:
|
|
16
|
+
case '-h':
|
|
17
|
+
case '--help':
|
|
18
|
+
console.log(`node-gtk — GNOME GObject-Introspection bindings for Node.js
|
|
19
|
+
|
|
20
|
+
Usage: node-gtk <command> [options]
|
|
21
|
+
|
|
22
|
+
Commands:
|
|
23
|
+
generate-types <Namespace-Version> [...] Generate TypeScript types (.d.ts)
|
|
24
|
+
|
|
25
|
+
Run \`node-gtk generate-types --help\` for details.`)
|
|
26
|
+
process.exit(cmd ? 0 : 1)
|
|
27
|
+
break
|
|
28
|
+
default:
|
|
29
|
+
console.error(`node-gtk: unknown command '${cmd}'. Try 'node-gtk --help'.`)
|
|
30
|
+
process.exit(1)
|
|
31
|
+
}
|
package/lib/bootstrap.js
CHANGED
|
@@ -82,11 +82,26 @@ function extendBaseClass(BaseClass) {
|
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
BaseClass.prototype.once = function once(event, callback, after) {
|
|
85
|
+
defineListeners(this)
|
|
86
|
+
|
|
87
|
+
if (!this._listeners.has(event))
|
|
88
|
+
this._listeners.set(event, new WeakMap())
|
|
89
|
+
|
|
90
|
+
const fnMap = this._listeners.get(event)
|
|
91
|
+
|
|
85
92
|
const newCallback = (...args) => {
|
|
86
|
-
callback
|
|
87
|
-
|
|
93
|
+
this.off(event, callback)
|
|
94
|
+
return callback(...args)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const handlerID = this.connect(event, newCallback, after)
|
|
98
|
+
if (handlerID === 0) {
|
|
99
|
+
throw new Error(`Could not connect to signal ${event}`)
|
|
88
100
|
}
|
|
89
|
-
|
|
101
|
+
// Key the listener map by the user's original callback so that it can be
|
|
102
|
+
// cancelled with off(event, callback), even though the connected handler
|
|
103
|
+
// is the wrapper.
|
|
104
|
+
fnMap.set(callback, handlerID)
|
|
90
105
|
return this
|
|
91
106
|
}
|
|
92
107
|
|
|
@@ -220,7 +235,10 @@ function makeEnum(info) {
|
|
|
220
235
|
for (let i = 0; i < nValues; i++) {
|
|
221
236
|
const valueInfo = GI.enum_info_get_value(info, i);
|
|
222
237
|
const valueName = getName(valueInfo).toUpperCase()
|
|
223
|
-
|
|
238
|
+
// g_value_info_get_value returns a gint64 (-> BigInt since #323), but enum
|
|
239
|
+
// and flags members are small integers and are compared/bitwise-combined
|
|
240
|
+
// as Numbers throughout, so coerce here.
|
|
241
|
+
const value = Number(GI.value_info_get_value(valueInfo))
|
|
224
242
|
Object.defineProperty(object, valueName, {
|
|
225
243
|
configurable: true,
|
|
226
244
|
enumerable: true,
|
package/lib/index.js
CHANGED
|
@@ -11,6 +11,30 @@ const module_ = require('./module.js')
|
|
|
11
11
|
const loop = require('./loop.js')
|
|
12
12
|
const registerClass = require('./register-class.js')
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Returns the GType (as a BigInt) of a GObject/boxed class, an instance of one,
|
|
16
|
+
* or a GType passed through as-is. See #286.
|
|
17
|
+
*
|
|
18
|
+
* @param {Function|object|bigint} value a class, an instance, or a GType
|
|
19
|
+
* @returns {bigint} the associated GType
|
|
20
|
+
*/
|
|
21
|
+
function getGType(value) {
|
|
22
|
+
if (typeof value === 'bigint')
|
|
23
|
+
return value
|
|
24
|
+
|
|
25
|
+
if (value != null) {
|
|
26
|
+
// A class: the GType lives on its prototype.
|
|
27
|
+
if (typeof value === 'function' && value.prototype != null && value.prototype.__gtype__ !== undefined)
|
|
28
|
+
return value.prototype.__gtype__
|
|
29
|
+
|
|
30
|
+
// An instance (or prototype).
|
|
31
|
+
if (value.__gtype__ !== undefined)
|
|
32
|
+
return value.__gtype__
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
throw new TypeError('getGType: expected a GObject/boxed class, instance, or GType')
|
|
36
|
+
}
|
|
37
|
+
|
|
14
38
|
/*
|
|
15
39
|
* Exports
|
|
16
40
|
*/
|
|
@@ -20,6 +44,7 @@ module.exports = {
|
|
|
20
44
|
...module_,
|
|
21
45
|
startLoop: loop.start,
|
|
22
46
|
registerClass: registerClass,
|
|
47
|
+
getGType: getGType,
|
|
23
48
|
System: internal.System,
|
|
24
49
|
|
|
25
50
|
// Private API
|
package/lib/loop.js
CHANGED
|
@@ -6,6 +6,7 @@ const internal = require('./native.js')
|
|
|
6
6
|
|
|
7
7
|
module.exports = {
|
|
8
8
|
start,
|
|
9
|
+
runLoopEntry,
|
|
9
10
|
}
|
|
10
11
|
|
|
11
12
|
|
|
@@ -32,6 +33,39 @@ function start() {
|
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
|
|
36
|
+
/**
|
|
37
|
+
* Runs a blocking main-loop entry point (e.g. GLib.MainLoop.run,
|
|
38
|
+
* Gio.Application.run, Gtk.main) so that Promise/async continuations keep
|
|
39
|
+
* draining while it blocks.
|
|
40
|
+
*
|
|
41
|
+
* Under ES modules the top-level body executes as a V8 microtask, so a blocking
|
|
42
|
+
* call made directly from it nests inside V8's microtask drain. V8 refuses
|
|
43
|
+
* nested microtask checkpoints, so any pending Promise/async continuation is
|
|
44
|
+
* starved for the entire lifetime of the loop. Deferring the blocking call to a
|
|
45
|
+
* macrotask lets the module's top-level microtask return first, so the queue
|
|
46
|
+
* drains and the loop integration takes over from a clean (non-nested) state.
|
|
47
|
+
*
|
|
48
|
+
* Under CommonJS (and inside signal callbacks) we are not in a microtask, so we
|
|
49
|
+
* run synchronously and preserve the original blocking semantics exactly.
|
|
50
|
+
*
|
|
51
|
+
* https://github.com/romgrk/node-gtk/issues/442
|
|
52
|
+
*
|
|
53
|
+
* Returns the native call's result when run synchronously (so e.g.
|
|
54
|
+
* `const status = app.run()` keeps working under CommonJS); returns undefined
|
|
55
|
+
* when deferred, since the result is not yet available.
|
|
56
|
+
*
|
|
57
|
+
* @param {Function} run - performs the blocking native call
|
|
58
|
+
* @returns {*} the native return value, or undefined when deferred
|
|
59
|
+
*/
|
|
60
|
+
function runLoopEntry(run) {
|
|
61
|
+
if (internal.IsRunningMicrotasks()) {
|
|
62
|
+
setImmediate(run)
|
|
63
|
+
return undefined
|
|
64
|
+
}
|
|
65
|
+
return run()
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
|
|
35
69
|
// Helpers
|
|
36
70
|
|
|
37
71
|
function wrappedLoopFunction(fn) {
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
const internal = require('../native.js')
|
|
6
|
+
const { runLoopEntry } = require('../loop.js')
|
|
6
7
|
|
|
7
8
|
exports.apply = (GLib) => {
|
|
8
9
|
|
|
@@ -10,17 +11,19 @@ exports.apply = (GLib) => {
|
|
|
10
11
|
GLib.MainLoop.prototype._quit = GLib.MainLoop.prototype.quit
|
|
11
12
|
|
|
12
13
|
GLib.MainLoop.prototype.run = function run() {
|
|
13
|
-
/* Run before we enter the loop otherwise pending microtasks
|
|
14
|
-
* are not run */
|
|
15
|
-
process._tickCallback()
|
|
16
|
-
|
|
17
14
|
const loopStack = internal.GetLoopStack()
|
|
18
|
-
|
|
19
15
|
loopStack.push(() => this.quit())
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
16
|
+
|
|
17
|
+
return runLoopEntry(() => {
|
|
18
|
+
/* Run before we enter the loop otherwise pending microtasks
|
|
19
|
+
* are not run */
|
|
20
|
+
process._tickCallback()
|
|
21
|
+
|
|
22
|
+
this._run()
|
|
23
|
+
if (this._userQuit)
|
|
24
|
+
loopStack.pop()
|
|
25
|
+
delete this._userQuit
|
|
26
|
+
})
|
|
24
27
|
}
|
|
25
28
|
GLib.MainLoop.prototype.quit = function quit() {
|
|
26
29
|
this._userQuit = true
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Gio-2.0.js
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const { runLoopEntry } = require('../loop.js')
|
|
6
|
+
|
|
7
|
+
exports.apply = (Gio) => {
|
|
8
|
+
|
|
9
|
+
Gio.Application.prototype._run = Gio.Application.prototype.run
|
|
10
|
+
|
|
11
|
+
/* g_application_run() blocks until the application quits. Under ES modules
|
|
12
|
+
* this would starve Promise/async continuations; runLoopEntry() defers the
|
|
13
|
+
* blocking call when needed so they keep draining. See loop.js / #442.
|
|
14
|
+
*
|
|
15
|
+
* Note: when deferred (ESM), the exit status is not available synchronously,
|
|
16
|
+
* so `app.run()` returns undefined instead of the status code in that case. */
|
|
17
|
+
Gio.Application.prototype.run = function run(...args) {
|
|
18
|
+
return runLoopEntry(() => {
|
|
19
|
+
/* Run before we enter the loop otherwise pending microtasks
|
|
20
|
+
* are not run */
|
|
21
|
+
process._tickCallback()
|
|
22
|
+
|
|
23
|
+
return this._run(...args)
|
|
24
|
+
})
|
|
25
|
+
}
|
|
26
|
+
}
|
package/lib/overrides/Gtk-3.0.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
const internal = require('../native.js')
|
|
6
|
+
const { runLoopEntry } = require('../loop.js')
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* @typedef {Object} Dimension
|
|
@@ -27,10 +28,6 @@ exports.apply = (Gtk) => {
|
|
|
27
28
|
let userCallingQuit = false
|
|
28
29
|
|
|
29
30
|
Gtk.main = function main() {
|
|
30
|
-
/* Run before we enter the loop otherwise pending microtasks
|
|
31
|
-
* are not run */
|
|
32
|
-
process._tickCallback()
|
|
33
|
-
|
|
34
31
|
const loopStack = internal.GetLoopStack()
|
|
35
32
|
|
|
36
33
|
/*
|
|
@@ -42,17 +39,25 @@ exports.apply = (Gtk) => {
|
|
|
42
39
|
|
|
43
40
|
loopStack.push(originalQuit)
|
|
44
41
|
|
|
45
|
-
|
|
42
|
+
/* runLoopEntry() defers the blocking call under ES modules so pending
|
|
43
|
+
* Promise/async continuations keep draining. See loop.js / #442. */
|
|
44
|
+
return runLoopEntry(() => {
|
|
45
|
+
/* Run before we enter the loop otherwise pending microtasks
|
|
46
|
+
* are not run */
|
|
47
|
+
process._tickCallback()
|
|
46
48
|
|
|
47
|
-
|
|
48
|
-
loopStack.pop()
|
|
49
|
-
}
|
|
49
|
+
originalMain()
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
if (userCallingQuit) {
|
|
52
|
+
loopStack.pop()
|
|
53
|
+
}
|
|
52
54
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
55
|
+
userCallingQuit = false
|
|
56
|
+
|
|
57
|
+
if (Gtk.mainLevel() === 0) {
|
|
58
|
+
placeholderIntervalID = clearInterval(placeholderIntervalID)
|
|
59
|
+
}
|
|
60
|
+
})
|
|
56
61
|
}
|
|
57
62
|
|
|
58
63
|
Gtk.mainQuit = function mainQuit() {
|
package/lib/overrides/Gtk-4.0.js
CHANGED
|
@@ -112,7 +112,8 @@ exports.apply = (Gtk) => {
|
|
|
112
112
|
*/
|
|
113
113
|
Gtk.FileChooserDialog.prototype.getFile = function getFile() {
|
|
114
114
|
const file = originalGetFile.call(this)
|
|
115
|
-
file
|
|
115
|
+
if (file)
|
|
116
|
+
file.__proto__= Gio.File.prototype
|
|
116
117
|
return file
|
|
117
118
|
}
|
|
118
119
|
|
|
@@ -122,7 +123,8 @@ exports.apply = (Gtk) => {
|
|
|
122
123
|
*/
|
|
123
124
|
Gtk.FileChooserWidget.prototype.getFile = function getFile() {
|
|
124
125
|
const file = originalGetFile.call(this)
|
|
125
|
-
file
|
|
126
|
+
if (file)
|
|
127
|
+
file.__proto__= Gio.File.prototype
|
|
126
128
|
return file
|
|
127
129
|
}
|
|
128
130
|
}
|