node-gtk 1.0.0 → 2.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.
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. 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
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,123 @@ 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 the code, but take a look at
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 Gtk = gi.require('Gtk', '3.0')
51
-
52
- gi.startLoop()
53
- Gtk.init()
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([]));
63
+ ```
54
64
 
55
- const win = new Gtk.Window()
56
- win.on('destroy', () => Gtk.mainQuit())
57
- win.on('delete-event', () => false)
65
+ <p align="center">
66
+ <img src="./img/hello-world.png" style="width: 290px; height: auto;"/>
67
+ </p>
58
68
 
59
- win.setDefaultSize(200, 80)
60
- win.add(new Gtk.Label({ label: 'Hello Gtk+' }))
69
+ You can also easily create custom applications:
61
70
 
62
- win.showAll()
63
- Gtk.main()
64
- ```
71
+ [A web browser (using WebKit2GTK)](./examples/browser.js)
65
72
 
66
73
  <p align="center">
67
- <img src="./img/hello-node-gtk.png" alt="Hello Gtk" style="width: 220px; height: auto;"/>
74
+ <img src="./img/browser.png" style="max-width: 500px; height: auto;"/>
68
75
  </p>
69
76
 
70
- See our [examples](./examples) folder for more examples, and in particular the
71
- [browser demo source](https://github.com/romgrk/node-gtk/blob/master/examples/browser.js) for
72
- a more complex application.
77
+ [A system monitor](./examples/system-monitor.js)
73
78
 
74
79
  <p align="center">
75
- <img src="./img/browser.png" alt="Hello Gtk" style="max-width: 500px; height: auto;"/>
80
+ <img src="./img/system-monitor.png" style="width: 400px; height: auto;"/>
76
81
  </p>
77
82
 
83
+ #### Other projects
84
+
85
+ The [react-gtk](https://github.com/codejamninja/react-gtk) project may also allow you to use GTK via React (unmaintained).
78
86
 
79
87
  ## Documentation
80
88
 
81
89
  [Read our documentation here](./doc/index.md)
82
90
 
83
- ## Installing and building
84
-
85
- Note that prebuilt binaries are available for common systems, in those cases building is not necessary.
91
+ ## TypeScript
86
92
 
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.
93
+ node-gtk can generate TypeScript declarations for the libraries you use,
94
+ straight from the GObject-Introspection typelibs installed on your machine — so
95
+ the types always match your actual library versions and node-gtk's own runtime
96
+ shape (camelCase methods, signal callbacks, nullability, etc.).
103
97
 
104
98
  ```sh
105
- sudo apt-get install \
106
- build-essential git \
107
- gobject-introspection \
108
- libgirepository1.0-dev \
109
- libcairo2 \
110
- libcairo2-dev
99
+ # generates ./node_modules/.node-gtk-types (a hidden, git-ignored cache)
100
+ npx node-gtk generate-types Gtk-4.0 Adw-1
111
101
  ```
112
102
 
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:
103
+ The command emits one declaration file per namespace (plus the full dependency
104
+ closure) and a `node-gtk.d.ts` shim. Point your `tsconfig.json` at it:
118
105
 
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
106
+ ```jsonc
107
+ {
108
+ "compilerOptions": {
109
+ "moduleResolution": "node16",
110
+ "paths": { "node-gtk": ["./node_modules/.node-gtk-types/node-gtk.d.ts"] }
111
+ }
112
+ }
130
113
  ```
131
114
 
132
- After installing of packages, run `npm install node-gtk`.
133
-
134
- ### How to build on ArchLinux
115
+ Then `gi.require` is fully typed the namespace is inferred from the string
116
+ arguments:
135
117
 
136
- The following should be the bare minimum to be able to build the project.
118
+ ```ts
119
+ import * as gi from 'node-gtk'
137
120
 
138
- ```sh
139
- pacman -S --needed \
140
- base-devel git \
141
- nodejs npm \
142
- gtk3 gobject-introspection \
143
- cairo
121
+ const Gtk = gi.require('Gtk', '4.0') // typed as the Gtk-4.0 namespace
122
+ const win = new Gtk.ApplicationWindow({ title: 'Hello', defaultWidth: 400 })
123
+ win.on('close-request', () => false) // signal name + callback are typed
144
124
  ```
145
125
 
146
- Feel free to install all `base-devel` utilities.
147
-
148
- After installing those packages, `npm install node-gtk` would do.
126
+ You get typed constructor properties (including inherited and interface ones),
127
+ camelCase methods with real return types, GI nullability, typed signal
128
+ overloads, enums, `bigint` for 64-bit integers, out-parameters surfaced as the
129
+ return value, and cross-namespace types. GNOME's API documentation is included
130
+ as JSDoc (with `@param`/`@returns`), so editors show it on hover — this reads the
131
+ `.gir` files installed by the libraries' `-dev`/`-devel` packages; pass
132
+ `--no-docs` for leaner output if they aren't installed or you don't want them.
149
133
 
150
- ### How to build on macOS
134
+ Because the output is a generated cache under `node_modules`, add a `postinstall`
135
+ script so it regenerates on install:
151
136
 
152
- Assuming you have [brew](http://brew.sh) installed, the following has been successfully tested on El Captain.
153
-
154
- ```sh
155
- brew install git node gobject-introspection gtk+3 cairo
137
+ ```json
138
+ { "scripts": { "postinstall": "node-gtk generate-types Gtk-4.0 Adw-1" } }
156
139
  ```
157
140
 
158
- At this point `npm install node-gtk` should already install, fallback and build `node-gtk` without problems.
159
-
160
- ### How to build on Windows
161
-
162
- Mandatory dependency is Visual C++ Build Environment: Visual Studio Build Tools (using "Visual C++ build tools" workload) or Visual Studio Community (using the "Desktop development with C++" workload).
163
-
164
- The easiest/tested way to build this repository is within a _MinGW shell_ provided by the [MSYS2 installer](https://msys2.github.io/).
165
-
166
- Once VS and its C++ compiler is available and MSYS2 installed, launch the MinGW shell.
167
-
168
- ```sh
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
141
+ Run `npx node-gtk generate-types --help` for options.
185
142
 
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
195
- ```
196
-
197
- The `GYP_MSVS_VERSION` could be 2017 or above.
198
- Please verify [which version you should use](https://github.com/nodejs/node-gyp#installation)
199
-
200
- The below blog post series will help you get started:
201
-
202
- 1. [Node.js GTK Hello World on Windows](https://ten0s.github.io/blog/2022/07/22/nodejs-gtk-hello-world-on-windows)
203
- 2. [Find DLLs and Typelibs dependencies for Node.js GTK Application on Windows](https://ten0s.github.io/blog/2022/07/25/find-dlls-and-typelibs-dependencies-for-nodejs-gtk-application-on-windows)
204
- 3. [Package Node.js GTK Application on Windows](https://ten0s.github.io/blog/2022/07/27/package-nodejs-gtk-application-on-windows)
205
-
206
- #### Possible issue on MinGW shell
207
-
208
- In case you are launching the general executable without knowing the correct platform,
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
219
- ```
220
-
221
- This should do the trick. You can also check if there is any python at all via `pacman -Qs python`.
222
-
223
- ### Testing the project
224
-
225
- If you'd like to test everything builds and work properly, after installing and building you can run any of the
226
- examples:
227
-
228
- ```sh
229
- node ./examples/hello-gtk.js
230
- ```
231
-
232
- If you'll see a little window saying hello that's it: it works!
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.
236
-
237
- #### Browser demo
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
143
+ ## Installing and building
251
144
 
252
- # Usage: ./examples/browser.js <url> [theme]
253
- ./examples/browser.js google.com dark
254
- ```
145
+ 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
146
 
256
147
  ## Contributing
257
148
 
@@ -259,9 +150,9 @@ If you'd like to help, we'd be more than happy to have support. To setup your de
259
150
  run `npm run configure`. You can then build the project with `npm run build`. To generate the `compile_commands.json`
260
151
  for LSP to work nicely, you can use [bear](https://github.com/rizsotto/Bear) as `bear -- npm run build`.
261
152
 
262
- - https://developer.gnome.org/gi/stable/index.html
263
- - https://v8docs.nodesource.com/
264
- - https://github.com/nodejs/nan#api
153
+ - https://developer.gnome.org/gi/stable/index.html
154
+ - https://v8docs.nodesource.com/
155
+ - https://github.com/nodejs/nan#api
265
156
 
266
157
  There is a [Discord channel](https://discord.gg/r2VqPUV) but it receives little monitoring, use github issues or
267
158
  discussions preferably.
@@ -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(...args)
87
- this.off(event, newCallback)
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
- this.on(event, newCallback, after)
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
- const value = GI.value_info_get_value(valueInfo)
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
@@ -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.__proto__= Gio.File.prototype
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.__proto__= Gio.File.prototype
126
+ if (file)
127
+ file.__proto__= Gio.File.prototype
126
128
  return file
127
129
  }
128
130
  }
@@ -119,7 +119,7 @@ function findVFuncOnParents(info, name) {
119
119
  function findVFuncOnInterfaces(gtype, name) {
120
120
  const interfaces = GObject.typeInterfaces(gtype);
121
121
 
122
- for (i = 0; i < interfaces.length; i++) {
122
+ for (let i = 0; i < interfaces.length; i++) {
123
123
  const interfaceInfo = findInfoByGtype(interfaces[i])
124
124
 
125
125
  /* The interface doesn't have to exist, it could be private
package/package.json CHANGED
@@ -1,10 +1,15 @@
1
1
  {
2
2
  "name": "node-gtk",
3
- "version": "1.0.0",
3
+ "version": "2.0.0",
4
4
  "description": "GNOME Gtk+ bindings for NodeJS",
5
5
  "main": "lib/index.js",
6
+ "bin": {
7
+ "node-gtk": "bin/node-gtk.js"
8
+ },
6
9
  "scripts": {
7
10
  "install": "npx node-pre-gyp install --fallback-to-build",
11
+ "build:test-fixtures": "node scripts/build-test-fixtures.js",
12
+ "pretest": "node scripts/build-test-fixtures.js",
8
13
  "test": "mocha tests/__run__.js",
9
14
  "build": "npx node-pre-gyp build",
10
15
  "build:full": "npx node-pre-gyp rebuild",
@@ -36,7 +41,7 @@
36
41
  "lodash.camelcase": "4.3.0",
37
42
  "lodash.isequal": "4.5.0",
38
43
  "lodash.snakecase": "^4.1.1",
39
- "nan": "^2.23.0",
44
+ "nan": "^2.27.0",
40
45
  "node-gyp": "^11.2.0",
41
46
  "remove-trailing-spaces": "^1.0.7",
42
47
  "unindent": "^2.0.0"
@@ -51,9 +56,11 @@
51
56
  "node-pre-gyp-github": "^1.4.5"
52
57
  },
53
58
  "files": [
59
+ "/bin",
54
60
  "/lib",
55
61
  "/src",
56
62
  "/scripts",
63
+ "/tools",
57
64
  "binding.gyp"
58
65
  ],
59
66
  "binary": {