node-gtk 2.2.0 → 4.0.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.
Files changed (51) hide show
  1. package/README.md +45 -161
  2. package/bin/node-gtk.js +12 -1
  3. package/binding.gyp +21 -0
  4. package/lib/binding/node-v127-linux-x64/node_gtk.node +0 -0
  5. package/lib/bootstrap.js +43 -11
  6. package/lib/esm/hooks.mjs +49 -0
  7. package/lib/esm/register.mjs +17 -0
  8. package/lib/index.js +1 -2
  9. package/lib/index.mjs +25 -0
  10. package/lib/inspect.js +1 -1
  11. package/lib/loop.js +5 -0
  12. package/lib/module.js +8 -2
  13. package/lib/overrides/Gtk-4.0.js +6 -27
  14. package/lib/register-class.js +86 -3
  15. package/lib/styles.d.ts +81 -0
  16. package/lib/styles.js +428 -0
  17. package/package.json +15 -2
  18. package/src/boxed.cc +13 -5
  19. package/src/closure.cc +19 -6
  20. package/src/closure.h +8 -4
  21. package/src/function.cc +59 -5
  22. package/src/fundamental.cc +451 -0
  23. package/src/fundamental.h +71 -0
  24. package/src/gi.cc +21 -1
  25. package/src/gobject.cc +268 -9
  26. package/src/gobject.h +5 -0
  27. package/src/modules/cairo/context.cc +103 -103
  28. package/src/modules/cairo/font-extents.cc +6 -2
  29. package/src/modules/cairo/generator.js +1 -1
  30. package/src/modules/cairo/glyph.cc +6 -2
  31. package/src/modules/cairo/path.cc +6 -2
  32. package/src/modules/cairo/rectangle-int.cc +6 -2
  33. package/src/modules/cairo/rectangle.cc +6 -2
  34. package/src/modules/cairo/text-cluster.cc +6 -2
  35. package/src/modules/cairo/text-extents.cc +6 -2
  36. package/src/modules/system.cc +4 -4
  37. package/src/util.h +3 -3
  38. package/src/value.cc +44 -8
  39. package/src/value.h +2 -2
  40. package/tools/README.md +52 -2
  41. package/tools/create-app.js +246 -0
  42. package/tools/generate-types.js +80 -3
  43. package/tools/list-libraries.js +125 -0
  44. package/tools/templates/app/README.md.tmpl +97 -0
  45. package/tools/templates/app/gitignore.tmpl +10 -0
  46. package/tools/templates/app/package.json.tmpl +26 -0
  47. package/tools/templates/app/src/main.ts.tmpl +110 -0
  48. package/tools/templates/app/src/welcome.ts.tmpl +41 -0
  49. package/tools/templates/app/style.css.tmpl +19 -0
  50. package/tools/templates/app/tsconfig.json.tmpl +19 -0
  51. /package/{COPYING → LICENSE} +0 -0
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
  <a>
3
3
  <img
4
4
  alt="NODE-GTK"
5
- width="250"
5
+ width="200"
6
6
  src="https://raw.githubusercontent.com/romgrk/node-gtk/master/img/node-gtk-logo.svg?sanitize=true"
7
7
  />
8
8
  </a>
@@ -12,205 +12,89 @@
12
12
  <p align="center">
13
13
  <b>GTK bindings for NodeJS</b>
14
14
  <br/>
15
- <img src="https://img.shields.io/npm/v/node-gtk" alt="Package Version" />
16
15
  </p>
17
16
 
18
- `node-gtk` is a [gobject-introspection](https://gi.readthedocs.io/en/latest) library
19
- for nodejs. It makes it possible to use any introspected C library, such as GTK,
20
- usable. It is similar in essence to [GJS](https://wiki.gnome.org/action/show/Projects/Gjs)
21
- or [PyGObject](https://pygobject.readthedocs.io). Please note this project is
22
- currently in a _alpha_ state.
23
-
24
- Supported Node.js versions: **20**, **22**, **24** (other versions may work but are untested)<br>
25
- Supported platforms:
26
- - **Linux** — prebuilt binaries available
27
- - **macOS** — prebuilt binaries available
28
- - **Windows** — prebuilt binaries available (but read [Windows](#windows))
29
-
30
-
31
- ### Table of contents
32
-
33
- - [Usage](#usage)
34
- - [ES modules](#es-modules)
35
- - [Documentation](#documentation)
36
- - [TypeScript](#typescript)
37
- - [Installing](#installing)
38
- - [Contributing](#contributing)
39
-
40
- ## Usage
41
-
42
- Below is a [minimal example](./examples/hello-world.js) of how to use node-gtk:
43
-
44
- ```javascript
45
- const gi = require('node-gtk');
46
- const GLib = gi.require('GLib', '2.0');
47
- const Gtk = gi.require('Gtk', '4.0');
48
- const Adw = gi.require('Adw', '1');
49
-
50
- const loop = GLib.MainLoop.new(null, false);
51
- const app = new Adw.Application('com.github.romgrk.node-gtk.hello', 0);
52
-
53
- app.on('activate', () => {
54
- const content = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL });
55
- content.append(new Adw.HeaderBar());
56
- content.append(new Gtk.Label({ label: 'Hello Adwaita!', vexpand: true }));
57
-
58
- const window = new Adw.ApplicationWindow(app);
59
- window.setTitle('node-gtk');
60
- window.setDefaultSize(300, 120);
61
- window.setContent(content);
62
- window.on('close-request', () => (loop.quit(), app.quit(), false));
63
- window.present();
64
-
65
- gi.startLoop();
66
- loop.run();
67
- });
68
-
69
- process.exit(app.run([]));
70
- ```
71
-
72
17
  <p align="center">
73
- <img src="./img/hello-world.png" style="width: 290px; height: auto;"/>
18
+ <a href="#usage">Usage</a> · <a href="#installing">Installing</a> · <a href="./doc/index.md">Documentation</a> · <a href="#contributing">Contributing</a>
74
19
  </p>
75
20
 
76
- You can also easily create custom applications:
77
-
78
- [A web browser (using WebKit2GTK)](./examples/browser.js)
79
-
80
- <p align="center">
81
- <img src="./img/browser.png" style="max-width: 500px; height: auto;"/>
82
- </p>
21
+ <br />
83
22
 
84
- [A system monitor](./examples/system-monitor.js)
23
+ `node-gtk` lets you build native GTK apps on **linux**, **macOS** and **windows**
24
+ with full **ESM**, **TypeScript** and **CSS hot-reload** support. Prebuilt binaries
25
+ are available for Node.js versions **22**, **24** and **26**.
85
26
 
86
27
  <p align="center">
87
- <img src="./img/system-monitor.png" style="width: 400px; height: auto;"/>
28
+ <img src="./img/browser.png" style="max-width: 500px; height: auto;" alt="A web browser build with node-gtk" />
88
29
  </p>
89
30
 
90
- ## ES modules
91
-
92
- The Usage example above is CommonJS. node-gtk also works under ESM, but the
93
- blocking main-loop calls (`GLib.MainLoop.run`, `Gio`/`Gtk.Application.run`,
94
- `Gtk.main`) **return immediately** instead of blocking and **don't return a
95
- value** — so make the run call the last statement and exit from your handler:
96
-
97
- ```javascript
98
- app.on('activate', () => {
99
- // ...build the window...
100
- window.on('close-request', () => (loop.quit(), app.quit(), false));
101
- window.present();
102
-
103
- gi.startLoop();
104
- loop.run(); // returns immediately under ESM; do cleanup/exit in the handler
105
- });
106
-
107
- app.run([]); // not `process.exit(app.run([]))` — the return value is unavailable
108
- ```
109
-
110
- CommonJS (and signal callbacks) are unaffected. For the why and the design
111
- trade-off, see [#449](https://github.com/romgrk/node-gtk/issues/449).
112
-
113
- ## Documentation
114
-
115
- [Read our documentation here](./doc/index.md)
116
-
117
- ## TypeScript
31
+ ## Usage
118
32
 
119
- node-gtk can generate TypeScript declarations for the libraries you use,
120
- straight from the GObject-Introspection typelibs installed on your machine — so
121
- the types always match your actual library versions and node-gtk's own runtime
122
- shape (camelCase methods, signal callbacks, nullability, etc.).
33
+ The **create** tool generates a complete, ready-to-run GTK/Adwaita project, so you
34
+ can start building immediately after [installing GTK4](#installing):
123
35
 
124
36
  ```sh
125
- # generates ./node_modules/.node-gtk-types (a hidden, git-ignored cache)
126
- npx node-gtk generate-types Gtk-4.0 Adw-1
127
- ```
128
-
129
- The command emits one declaration file per namespace (plus the full dependency
130
- closure) and a `node-gtk.d.ts` shim. Point your `tsconfig.json` at it:
131
-
132
- ```jsonc
133
- {
134
- "compilerOptions": {
135
- "moduleResolution": "node16",
136
- "paths": { "node-gtk": ["./node_modules/.node-gtk-types/node-gtk.d.ts"] }
137
- }
138
- }
139
- ```
140
-
141
- Then `gi.require` is fully typed — the namespace is inferred from the string
142
- arguments:
143
-
144
- ```ts
145
- import * as gi from 'node-gtk'
146
-
147
- const Gtk = gi.require('Gtk', '4.0') // typed as the Gtk-4.0 namespace
148
- const win = new Gtk.ApplicationWindow({ title: 'Hello', defaultWidth: 400 })
149
- win.on('close-request', () => false) // signal name + callback are typed
37
+ npx node-gtk create <your-app>
150
38
  ```
151
39
 
152
- You get typed constructor properties (including inherited and interface ones),
153
- camelCase methods with real return types, GI nullability, typed signal
154
- overloads, enums, `bigint` for 64-bit integers, out-parameters surfaced as the
155
- return value, and cross-namespace types. GNOME's API documentation is included
156
- as JSDoc (with `@param`/`@returns`), so editors show it on hover — this reads the
157
- `.gir` files installed by the libraries' `-dev`/`-devel` packages; pass
158
- `--no-docs` for leaner output if they aren't installed or you don't want them.
159
-
160
- Because the output is a generated cache under `node_modules`, add a `postinstall`
161
- script so it regenerates on install:
162
-
163
- ```json
164
- { "scripts": { "postinstall": "node-gtk generate-types Gtk-4.0 Adw-1" } }
165
- ```
40
+ <p align="center">
41
+ <img src="./img/create-app-example.png" style="width: 500px; height: auto;"/>
42
+ </p>
166
43
 
167
- Run `npx node-gtk generate-types --help` for options.
44
+ Also see our [hello world](./examples/hello-world.mjs), [web browser](./examples/browser.mjs)
45
+ or [system monitor](./examples/system-monitor.mjs) examples.
168
46
 
169
47
  ## Installing
170
48
 
171
- 1. Install `node-gtk` itself
172
- 2. Install the native libraries you use (see examples per platform below)
49
+ There are two steps:
173
50
 
174
- ```sh
175
- npm install node-gtk
176
-
177
- # This installs a prebuilt binary when one is available for your platform and
178
- # Node.js version, otherwise it falls back to building from source.
179
- ```
51
+ 1. Install `node-gtk` itself (*done by the create tool*)
52
+ 2. Install the native libraries you use (see examples per platform below)
180
53
 
181
54
  #### Linux
182
55
 
183
56
  ```sh
184
57
  # archlinux
185
- pacman -S gtk4
58
+ pacman -S gtk4 libadwaita
59
+
60
+ # fedora
61
+ dnf install gtk4 libadwaita
186
62
 
187
63
  # ubuntu
188
- apt install libgtk-4-1
64
+ # Already installed :)
189
65
  ```
190
66
 
191
67
  #### macOS
192
68
 
193
69
  ```sh
194
- brew install gtk4
70
+ brew install gtk4 libadwaita adwaita-icon-theme
195
71
  ```
196
72
 
197
73
  #### Windows
198
74
 
199
- Windows doesn't have the dependencies we need in a package manager, therefore
200
- `node-gtk` ships prebuilt versions of GTK 4 / Adwaita runtime (DLLs, typelibs,
201
- icons), so `npm install node-gtk` is all you need **if** your dependency is in
202
- our [list of prebuilt libraries](./windows/runtime-libraries.txt).
75
+ ```sh
76
+ # Already installed :)
77
+ ```
78
+
79
+ > [!NOTE]
80
+ > Windows doesn't have the dependencies we need in a package manager, therefore
81
+ > `node-gtk` ships prebuilt versions of GTK 4 / Adwaita, so `npm install node-gtk`
82
+ > is all you need **if** your dependency is in our
83
+ > [list of prebuilt libraries](./windows/runtime-libraries.txt).
203
84
 
204
85
  ### build from source
205
86
 
206
- Building from source, or contributing? See [Building from source](./doc/building.md).
87
+ Building from source, or [contributing](./doc/contributing.md)? See [Building from source](./doc/building.md).
207
88
 
208
- ## Contributing
89
+ ## Documentation
90
+
91
+ [Read our documentation here](./doc/index.md)
92
+
93
+ ## Other notes
209
94
 
210
- If you'd like to help, we'd be more than happy to have support. To setup your development environment, you can
211
- run `npm run configure`. You can then build the project with `npm run build`. To generate the `compile_commands.json`
212
- for LSP to work nicely, you can use [bear](https://github.com/rizsotto/Bear) as `bear -- npm run build`.
95
+ `node-gtk` is a [gobject-introspection](https://gi.readthedocs.io/en/latest) library
96
+ for nodejs. It makes it possible to use any introspected C library, such as GTK,
97
+ usable. It is similar in essence to [GJS](https://wiki.gnome.org/action/show/Projects/Gjs)
98
+ or [PyGObject](https://pygobject.readthedocs.io).
213
99
 
214
- - https://developer.gnome.org/gi/stable/index.html
215
- - https://v8docs.nodesource.com/
216
- - https://github.com/nodejs/nan#api
100
+ [MIT License](./LICENSE)
package/bin/node-gtk.js CHANGED
@@ -4,6 +4,8 @@
4
4
  *
5
5
  * Subcommands:
6
6
  * generate-types Generate TypeScript declarations from the installed typelibs.
7
+ * create Create a new GTK/Adwaita application.
8
+ * list List the GObject-Introspection libraries available locally.
7
9
  */
8
10
 
9
11
  const cmd = process.argv[2]
@@ -12,6 +14,13 @@ switch (cmd) {
12
14
  case 'generate-types':
13
15
  require('../tools/generate-types.js').run(process.argv.slice(3))
14
16
  break
17
+ case 'create':
18
+ require('../tools/create-app.js').run(process.argv.slice(3))
19
+ break
20
+ case 'list':
21
+ case 'list-libraries':
22
+ require('../tools/list-libraries.js').run(process.argv.slice(3))
23
+ break
15
24
  case undefined:
16
25
  case '-h':
17
26
  case '--help':
@@ -20,9 +29,11 @@ switch (cmd) {
20
29
  Usage: node-gtk <command> [options]
21
30
 
22
31
  Commands:
32
+ create <directory> Create a new GTK/Adwaita app
23
33
  generate-types <Namespace-Version> [...] Generate TypeScript types (.d.ts)
34
+ list [filter] List available libraries & versions
24
35
 
25
- Run \`node-gtk generate-types --help\` for details.`)
36
+ Run \`node-gtk <command> --help\` for details.`)
26
37
  process.exit(cmd ? 0 : 1)
27
38
  break
28
39
  default:
package/binding.gyp CHANGED
@@ -1,4 +1,24 @@
1
1
  {
2
+ # Node 26's official Windows binary is built with ClangCL + ThinLTO, so its
3
+ # common.gypi injects `-flto=thin` and `/opt:lldltojobs=N` into the MSVC
4
+ # AdditionalOptions of every target. MSVC's link.exe rejects those
5
+ # Clang/lld-only options (LNK1117), which breaks the build on Windows +
6
+ # Node 26. Strip them with gyp's list-exclusion filter. This lives under
7
+ # msvs_settings, so it is inert on the make/xcode generators (Linux/macOS).
8
+ "target_defaults": {
9
+ "configurations": {
10
+ "Release": {
11
+ "msvs_settings": {
12
+ "VCCLCompilerTool": {
13
+ "AdditionalOptions/": [ ["exclude", "flto"] ]
14
+ },
15
+ "VCLinkerTool": {
16
+ "AdditionalOptions/": [ ["exclude", "flto"], ["exclude", "lldltojobs"] ]
17
+ }
18
+ }
19
+ }
20
+ }
21
+ },
2
22
  "targets": [
3
23
  {
4
24
  "target_name": "node_gtk",
@@ -11,6 +31,7 @@
11
31
  "src/debug.cc",
12
32
  "src/error.cc",
13
33
  "src/function.cc",
34
+ "src/fundamental.cc",
14
35
  "src/gi.cc",
15
36
  "src/gobject.cc",
16
37
  "src/loop.cc",
package/lib/bootstrap.js CHANGED
@@ -292,23 +292,55 @@ function makeObject(info) {
292
292
  */
293
293
 
294
294
  forLoopFn(info, GI.object_info_get_n_interfaces, GI.object_info_get_interface, (interfaceInfo) => {
295
- const interface_ = getInterface(interfaceInfo)
296
-
297
- interface_._properties.forEach(description => {
298
- define(constructor, description)
299
- })
300
- interface_._methods.forEach(description => {
301
- define(constructor, description)
302
- })
303
- interface_._constants.forEach(description => {
304
- define(constructor, description)
305
- })
295
+ applyInterface(constructor, getInterface(interfaceInfo))
306
296
  })
307
297
 
308
298
 
309
299
  return constructor;
310
300
  }
311
301
 
302
+ /*
303
+ * Mix an interface's properties/methods/constants (as prepared by
304
+ * makeInterface) into a class constructor. Shared by makeObject() and the
305
+ * `applyInterfaceMethods` callback below.
306
+ */
307
+ function applyInterface(constructor, interface_) {
308
+ interface_._properties.forEach(description => define(constructor, description))
309
+ interface_._methods.forEach(description => define(constructor, description))
310
+ interface_._constants.forEach(description => define(constructor, description))
311
+ }
312
+
313
+ /*
314
+ * Called from C++ (GetClassTemplate) the first time a private/non-introspectable
315
+ * concrete type is wrapped, to install the methods of the interfaces it
316
+ * implements onto its prototype. makeObject() never runs for such types, so
317
+ * without this their instances would only expose the base GObject methods
318
+ * (issue #441). `interfaceRefs` is an array of { namespace, name }.
319
+ */
320
+ function applyInterfaceMethods(constructor, interfaceRefs) {
321
+ const repo = GI.Repository_get_default()
322
+
323
+ interfaceRefs.forEach(({ namespace, name }) => {
324
+ const cache = moduleCache[namespace]
325
+ // The interface's module must be loaded for its constructor to exist; if it
326
+ // isn't, there is nothing meaningful to install.
327
+ if (!cache)
328
+ return
329
+
330
+ let interface_ = cache[name]
331
+ if (!interface_) {
332
+ const info = GI.Repository_find_by_name.call(repo, namespace, name)
333
+ if (!info)
334
+ return
335
+ interface_ = getInterface(info)
336
+ }
337
+
338
+ applyInterface(constructor, interface_)
339
+ })
340
+ }
341
+
342
+ internal.SetInterfaceMethodsApplier(applyInterfaceMethods)
343
+
312
344
  function makeBoxed(info) {
313
345
  if (getType(info) == GI.InfoType.UNION) {
314
346
  return makeUnion(info)
@@ -0,0 +1,49 @@
1
+ /*
2
+ * hooks.mjs — Node.js ESM module-customization hooks for the `gi:` scheme.
3
+ *
4
+ * Enables `import Gtk from 'gi:Gtk-4.0'`, where the default export is the
5
+ * namespace object returned by node-gtk's `gi.require('Gtk', '4.0')`. Members are
6
+ * read off that object: `const { Box, Label } = Gtk`.
7
+ *
8
+ * Install them with `node --import node-gtk/register app.mjs` (see register.mjs).
9
+ *
10
+ * The hooks run on a separate loader thread, so they do NO native work: `load`
11
+ * only emits a tiny synthetic ES module whose body calls `gi.require` on the
12
+ * main thread when the module is evaluated. Requires Node >= 20.6 (module.register).
13
+ */
14
+
15
+ const PREFIX = 'gi:'
16
+
17
+ /* Absolute file:// URL to lib/index.js, embedded into the generated source. The
18
+ * synthetic module's parent URL is the schemeless `gi:` URL, which has no
19
+ * filesystem base, so a bare `import 'node-gtk'` could not be resolved from it —
20
+ * the absolute URL sidesteps resolution entirely. */
21
+ const INDEX_URL = new URL('../index.js', import.meta.url).href
22
+
23
+ export async function resolve(specifier, context, nextResolve) {
24
+ if (specifier.startsWith(PREFIX))
25
+ return { url: specifier, shortCircuit: true }
26
+ return nextResolve(specifier, context)
27
+ }
28
+
29
+ export async function load(url, context, nextLoad) {
30
+ if (!url.startsWith(PREFIX))
31
+ return nextLoad(url, context)
32
+
33
+ // `gi:Gtk-4.0` -> ('Gtk', '4.0'); `gi:Adw-1` -> ('Adw', '1'); `gi:cairo` -> ('cairo', null).
34
+ // Split on the first '-' only: GI namespace names never contain '-', versions may.
35
+ const spec = url.slice(PREFIX.length)
36
+ const dash = spec.indexOf('-')
37
+ const name = dash === -1 ? spec : spec.slice(0, dash)
38
+ const version = dash === -1 ? null : spec.slice(dash + 1)
39
+
40
+ const call = version === null
41
+ ? `gi.require(${JSON.stringify(name)})`
42
+ : `gi.require(${JSON.stringify(name)}, ${JSON.stringify(version)})`
43
+
44
+ const source =
45
+ `import gi from ${JSON.stringify(INDEX_URL)};\n` +
46
+ `export default ${call};\n`
47
+
48
+ return { format: 'module', shortCircuit: true, source }
49
+ }
@@ -0,0 +1,17 @@
1
+ /*
2
+ * register.mjs — install the `gi:` import hooks.
3
+ *
4
+ * Usage: node --import node-gtk/register app.mjs
5
+ *
6
+ * Then, in app.mjs:
7
+ * import Gtk from 'gi:Gtk-4.0'
8
+ * const { Box, Label } = Gtk
9
+ *
10
+ * Note: hooks only affect imports evaluated *after* registration. To use a static
11
+ * `import ... from 'gi:...'` in your entry module, register via the `--import`
12
+ * flag above (not a programmatic `import 'node-gtk/register'` in that same file).
13
+ */
14
+
15
+ import { register } from 'node:module'
16
+
17
+ register(new URL('./hooks.mjs', import.meta.url))
package/lib/index.js CHANGED
@@ -8,7 +8,7 @@ const moduleCache = internal.GetModuleCache()
8
8
  // Must be loaded first, to setup the GI functions
9
9
  const bootstrap = require('./bootstrap.js')
10
10
  const module_ = require('./module.js')
11
- const loop = require('./loop.js')
11
+ require('./loop.js') // installs the automatic main-loop integration
12
12
  const registerClass = require('./register-class.js')
13
13
 
14
14
  /**
@@ -42,7 +42,6 @@ function getGType(value) {
42
42
  module.exports = {
43
43
  // Public API
44
44
  ...module_,
45
- startLoop: loop.start,
46
45
  registerClass: registerClass,
47
46
  getGType: getGType,
48
47
  System: internal.System,
package/lib/index.mjs ADDED
@@ -0,0 +1,25 @@
1
+ /*
2
+ * index.mjs
3
+ *
4
+ * ESM facade over the CommonJS entry (index.js), so that named imports work:
5
+ *
6
+ * import gi, { require, registerClass } from 'node-gtk'
7
+ *
8
+ * Node's CommonJS-to-ESM named-export detection (cjs-module-lexer) cannot see
9
+ * through index.js's computed `module.exports`, so we re-export explicitly here.
10
+ * Both this file and `require('node-gtk')` share the same underlying index.js
11
+ * instance (Node caches it by path), so there is no duplicated state.
12
+ */
13
+
14
+ import gi from './index.js'
15
+
16
+ export default gi
17
+
18
+ export const require = gi.require
19
+ export const isLoaded = gi.isLoaded
20
+ export const prependSearchPath = gi.prependSearchPath
21
+ export const prependLibraryPath = gi.prependLibraryPath
22
+ export const listAvailableModules = gi.listAvailableModules
23
+ export const registerClass = gi.registerClass
24
+ export const getGType = gi.getGType
25
+ export const System = gi.System
package/lib/inspect.js CHANGED
@@ -3,7 +3,7 @@ const chalk = require('chalk')
3
3
 
4
4
  const internal = require('./native.js')
5
5
  const gi = require('../lib/index');
6
- gi.startLoop();
6
+ require('./loop.js').start();
7
7
 
8
8
  const infos = []
9
9
 
package/lib/loop.js CHANGED
@@ -58,6 +58,11 @@ function start() {
58
58
  * @returns {*} the native return value, or undefined when deferred
59
59
  */
60
60
  function runLoopEntry(run) {
61
+ // The loop integration must be active before we block in a native main loop,
62
+ // so start it automatically on the first run. Calling startLoop() explicitly
63
+ // still works and is no longer required (start() is idempotent).
64
+ start()
65
+
61
66
  if (internal.IsRunningMicrotasks()) {
62
67
  setImmediate(run)
63
68
  return undefined
package/lib/module.js CHANGED
@@ -129,6 +129,12 @@ function listAvailableModules() {
129
129
  // Helpers
130
130
 
131
131
  function parseModuleFilename(filename) {
132
- const [name, version] = filename.replace('.typelib', '').split('-')
133
- return { name, version }
132
+ // Typelibs are named `Name-Version.typelib`; the version is the trailing
133
+ // `-X.Y`, so split on the LAST dash — namespaces themselves may contain dashes
134
+ // (e.g. `GUPnP-DLNA-2.0` -> name `GUPnP-DLNA`, version `2.0`).
135
+ const base = filename.replace(/\.typelib$/, '')
136
+ const dash = base.lastIndexOf('-')
137
+ if (dash === -1)
138
+ return { name: base, version: '' }
139
+ return { name: base.slice(0, dash), version: base.slice(dash + 1) }
134
140
  }
@@ -2,10 +2,6 @@
2
2
  * Gtk-4.0.js
3
3
  */
4
4
 
5
- const internal = require('../native.js')
6
- const Module = require('../module')
7
- const Gio = Module.require('Gio')
8
-
9
5
  exports.apply = (Gtk) => {
10
6
 
11
7
  Gtk.EVENT_CONTINUE = false
@@ -104,29 +100,12 @@ exports.apply = (Gtk) => {
104
100
  return tickId
105
101
  }
106
102
 
107
- const originalGetFile = Gtk.FileChooser.prototype.getFile
108
-
109
- /**
110
- * Gtk.FileChooserDialog.prototype.getFile
111
- * @returns {GFile} - The file
112
- */
113
- Gtk.FileChooserDialog.prototype.getFile = function getFile() {
114
- const file = originalGetFile.call(this)
115
- if (file)
116
- file.__proto__= Gio.File.prototype
117
- return file
118
- }
119
-
120
- /**
121
- * Gtk.FileChooserWidget.prototype.getFile
122
- * @returns {GFile} - The file
123
- */
124
- Gtk.FileChooserWidget.prototype.getFile = function getFile() {
125
- const file = originalGetFile.call(this)
126
- if (file)
127
- file.__proto__= Gio.File.prototype
128
- return file
129
- }
103
+ /* getFile() used to need a manual `file.__proto__ = Gio.File.prototype` fixup
104
+ * because the returned GLocalFile is a private type whose GFile interface
105
+ * methods weren't mixed into its prototype. That is now handled generically
106
+ * for every private type at wrap time (see issue #441), so the override is no
107
+ * longer necessary — the introspected getFile() returns a fully-usable file
108
+ * (or null when nothing is selected). */
130
109
  }
131
110
 
132
111
  function easeOutCubic(t) {