├── .gitignore
├── .npmignore
├── COPYING
├── Makefile
├── README.md
├── binding.gyp
├── examples
├── browser.js
├── hello-gtk.js
└── test.js
├── img
└── hello-node-gtk.png
├── lib
├── index.js
└── overrides
│ └── GLib.js
├── node-gtk.doap
├── package.json
└── src
├── boxed.cc
├── boxed.h
├── closure.cc
├── closure.h
├── function.cc
├── function.h
├── gi.cc
├── gobject.cc
├── gobject.h
├── loop.cc
├── loop.h
├── value.cc
└── value.h
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | *~
3 | \#*\#
4 | build/
5 | node_modules/
6 | npm-debug.log
7 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | build
2 | examples
3 | node_modules
4 | .DS_Store
5 | .gitignore
6 | Makefile
7 | node-gtk.doap
8 | npm-debug.log
9 |
--------------------------------------------------------------------------------
/COPYING:
--------------------------------------------------------------------------------
1 | Copyright (c) 2015-2016 Jasper St. Pierre, Andrea Giammarchi & Contributors
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a
4 | copy of this software and associated documentation files (the "Software"),
5 | to deal in the Software without restriction, including without limitation
6 | the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 | and/or sell copies of the Software, and to permit persons to whom the
8 | Software is furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice (including the next
11 | paragraph) shall be included in all copies or substantial portions of the
12 | Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 | DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | .PHONY: build default release
2 |
3 | VERSION := $(shell node -e "console.log(require('./package.json').version)")
4 | BOLD := $(shell tput bold)
5 | RED := $(shell tput setaf 1)
6 | RESET := $(shell tput sgr0)
7 |
8 | build:
9 | @echo "Did you mean 'make release' ?"
10 |
11 | default:
12 | @echo "Did you mean 'make release' ?"
13 |
14 | release:
15 | @if [ $(NODE_PRE_GYP_GITHUB_TOKEN) = "" ]; then echo "$(RED)Please specify a $(BOLD)NODE_PRE_GYP_GITHUB_TOKEN$(RESET)"; echo ""; exit 1; fi
16 | @echo "Current Version: $(BOLD)$(VERSION)$(RESET)"
17 | @if [ "$(TAG)" = "" ]; then echo "$(BOLD)$(RED)Please specify a new version$(RESET)"; fi
18 | @if [ "$(TAG)" = "" ]; then echo "$(BOLD)TAG=$(VERSION)1 make release$(RESET)"; echo ""; exit 1; fi
19 | @if [ "$(TAG)" = $(VERSION) ]; then echo "$(BOLD)$(RED)Please specify a different version$(RESET)"; echo ""; exit 1; fi
20 | @echo "Creating Release: $(BOLD)$(TAG)$(RESET)"
21 | @echo ""
22 | @node -e "var pkg=require('./package.json');pkg.version='$(TAG)';pkg.binary.host=pkg.binary.host.replace(/\/\d+\.\d+\.\d+\$$/,'/$(TAG)');require('fs').writeFileSync('./package.json', JSON.stringify(pkg,null,' '));"
23 | @echo "Building Package"
24 | ./node_modules/node-pre-gyp/bin/node-pre-gyp configure
25 | ./node_modules/node-pre-gyp/bin/node-pre-gyp rebuild
26 | node-pre-gyp-github package
27 | @echo "Adding updated package.json"
28 | @git add .
29 | @git commit -m "Release $(TAG)"
30 | @git push
31 | @echo "Tagging master Release $(BOLD)$(TAG)$(RESET)"
32 | @git tag -m "Release $(TAG)" $(TAG)
33 | @echo "Pushing master tags to GitHub"
34 | @git push --tags
35 | NODE_PRE_GYP_GITHUB_TOKEN="$(NODE_PRE_GYP_GITHUB_TOKEN)" node-pre-gyp-github publish
36 | @echo "Publishing to npm"
37 | npm publish
38 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Moved to [romgrk/node-gtk](https://github.com/romgrk/node-gtk)
2 | This project is abandoned, it not even a work in progress anymore, but @romgrk is working in his fork.
3 |
--------------------------------------------------------------------------------
/binding.gyp:
--------------------------------------------------------------------------------
1 | {
2 | "targets": [
3 | {
4 | "target_name": "node-gtk",
5 | "sources": [
6 | "src/loop.cc",
7 | "src/gi.cc",
8 | "src/value.cc",
9 | "src/function.cc",
10 | "src/gobject.cc",
11 | "src/closure.cc",
12 | "src/boxed.cc",
13 | ],
14 | "cflags": [
15 | " color === 'dark')) {
43 | let gtkSettings = Gtk.Settings.get_default();
44 | gtkSettings.gtk_application_prefer_dark_theme = true;
45 | gtkSettings.gtk_theme_name = 'Adwaita';
46 | }
47 |
48 | // open first argument or Google
49 | webView.load_uri(url(process.argv[2] || 'google.com'));
50 |
51 | // whenever a new page is loaded ...
52 | webView.connect('load-changed', (widget, load_event, data) => {
53 | switch (load_event) {
54 | case 2: // XXX: where is WEBKIT_LOAD_COMMITTED ?
55 | // ... update the URL bar with the current adress
56 | urlBar.set_text(widget.get_uri());
57 | button.back.set_sensitive(webView.can_go_back());
58 | button.forward.set_sensitive(webView.can_go_forward());
59 | break;
60 | }
61 | });
62 |
63 | // configure buttons actions
64 | button.back.connect('clicked', () => webView.go_back());
65 | button.forward.connect('clicked', () => webView.go_forward());
66 | button.refresh.connect('clicked', () => webView.reload());
67 |
68 | // enrich the toolbar
69 | toolbar.add(button.back);
70 | toolbar.add(button.forward);
71 | toolbar.add(button.refresh);
72 |
73 | // define "enter" / call-to-action event
74 | // whenever the url changes on the bar
75 | urlBar.connect('activate', () => {
76 | let href = url(urlBar.get_text());
77 | urlBar.set_text(href);
78 | webView.load_uri(href);
79 | });
80 |
81 | // make the container scrollable
82 | scrollWindow.add(webView);
83 |
84 | // pack horizontally toolbar and url bar
85 | hbox.pack_start(toolbar, false, false, 0);
86 | hbox.pack_start(urlBar, true, true, 8);
87 |
88 | // pack vertically top bar (hbox) and scrollable window
89 | vbox.pack_start(hbox, false, true, 0);
90 | vbox.pack_start(scrollWindow, true, true, 0);
91 |
92 | // configure main window
93 | window.set_default_size(1024, 720);
94 | window.set_resizable(true);
95 | window.connect('show', () => {
96 | // bring it on top in OSX
97 | window.set_keep_above(true);
98 | Gtk.main()
99 | });
100 | window.connect('destroy', () => Gtk.main_quit());
101 | window.connect('delete_event', () => false);
102 |
103 | // add vertical ui and show them all
104 | window.add(vbox);
105 | window.show_all();
106 |
107 | // little helper
108 | // if link doesn't have a protocol, prefixes it via http://
109 | function url(href) {
110 | return /^([a-z]{2,}):/.test(href) ? href : ('http://' + href);
111 | }
112 |
113 | }(
114 | ngtk.importNS('Gtk'),
115 | ngtk.importNS('WebKit2')
116 | ));
117 |
--------------------------------------------------------------------------------
/examples/hello-gtk.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | var
4 | GNode = require('../lib/'),
5 | Gtk = GNode.importNS('Gtk'),
6 | settings,
7 | win
8 | ;
9 |
10 | GNode.startLoop();
11 | Gtk.init(null);
12 |
13 | settings = Gtk.Settings.get_default(),
14 | settings.gtk_application_prefer_dark_theme = true;
15 | settings.gtk_theme_name = "Adwaita";
16 |
17 | console.log(settings.gtk_enable_accels);
18 |
19 | win = new Gtk.Window({
20 | title: 'node-gtk',
21 | window_position: Gtk.WindowPosition.CENTER
22 | });
23 |
24 | win.connect('show', Gtk.main);
25 | win.connect('destroy', Gtk.main_quit);
26 |
27 | win.set_default_size(200, 80);
28 | win.add(new Gtk.Label({label: 'Hello Gtk+'}));
29 |
30 | win.show_all();
31 |
--------------------------------------------------------------------------------
/examples/test.js:
--------------------------------------------------------------------------------
1 |
2 | const GNode = require('../lib/');
3 | GNode.startLoop();
4 |
5 | const GLib = GNode.importNS("GLib");
6 | console.log(GLib.ascii_strup("foo", -1));
7 |
8 | const GUdev = GNode.importNS("GUdev");
9 | var client = new GUdev.Client();
10 | var obj = client.query_by_device_file("/dev/dri/card0");
11 | console.log(obj.get_name());
12 |
13 | console.log(GLib.test_override());
14 |
15 | const Gtk = GNode.importNS("Gtk");
16 | Gtk.init(null);
17 |
18 | var w = new Gtk.Window();
19 | var b = new Gtk.Button({ label: "Hi!" });
20 | b.connect('clicked', function() { console.log("BB"); });
21 | w.add(b);
22 | w.show_all();
23 |
24 | console.log(GLib.MainLoop);
25 |
26 | Gtk.main();
27 |
--------------------------------------------------------------------------------
/img/hello-node-gtk.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WebReflection/node-gtk/1e5c91289b8d3a8486d9370d0de5ea009ae85b0c/img/hello-node-gtk.png
--------------------------------------------------------------------------------
/lib/index.js:
--------------------------------------------------------------------------------
1 |
2 | "use strict";
3 |
4 | var gi;
5 | try {
6 | gi = require('../build/Release/node-gtk');
7 | } catch(e) {
8 | gi = require('../build/Debug/node-gtk');
9 | }
10 |
11 | // The bootstrap from C here contains functions and methods for each object,
12 | // namespaced with underscores. See gi.cc for more information.
13 | var GIRepository = gi.Bootstrap();
14 |
15 | // The GIRepository API is fairly poor, and contains methods on classes,
16 | // methods on objects, and what should be methods interpreted as functions,
17 | // because the scanner does not interpret methods on typedefs correctly.
18 |
19 | // We extend this bootstrap'd repo to define all flags / enums, which
20 | // are all we need to start declaring objects.
21 | (function() {
22 | var repo = GIRepository.Repository_get_default();
23 | var ns = "GIRepository";
24 |
25 | // First, grab InfoType so we can find enums / flags.
26 | var InfoType = makeEnum(GIRepository.Repository_find_by_name.call(repo, ns, "InfoType"));
27 |
28 | // Now, define all enums / flags.
29 | var nInfos = GIRepository.Repository_get_n_infos.call(repo, ns);
30 | for (var i = 0; i < nInfos; i++) {
31 | var info = GIRepository.Repository_get_info.call(repo, ns, i);
32 | var name = GIRepository.BaseInfo_get_name.call(info);
33 | var type = GIRepository.BaseInfo_get_type.call(info);
34 | if (type === InfoType.ENUM || type === InfoType.FLAGS)
35 | GIRepository[name] = makeEnum(info);
36 | }
37 | })();
38 |
39 | function declareFunction(obj, info) {
40 | var name = GIRepository.BaseInfo_get_name.call(info);
41 | var flags = GIRepository.function_info_get_flags(info);
42 | var func = gi.MakeFunction(info);
43 | var target = flags & GIRepository.FunctionInfoFlags.IS_METHOD ? obj.prototype : obj;
44 | Object.defineProperty(target, name, {
45 | configurable: true,
46 | writable: true,
47 | value: func
48 | });
49 | }
50 |
51 | function makeEnum(info) {
52 | var obj = {};
53 | var nValues = GIRepository.enum_info_get_n_values(info);
54 |
55 | for (var i = 0; i < nValues; i++) {
56 | var valueInfo = GIRepository.enum_info_get_value(info, i);
57 | var valueName = GIRepository.BaseInfo_get_name.call(valueInfo);
58 | var valueValue = GIRepository.value_info_get_value(valueInfo);
59 | obj[valueName.toUpperCase()] = valueValue;
60 | }
61 |
62 | var nMethods = GIRepository.enum_info_get_n_methods(info);
63 | for (var i = 0; i < nMethods; i++) {
64 | var methodInfo = GIRepository.enum_info_get_method(info, i);
65 | declareFunction(constructor, methodInfo);
66 | }
67 |
68 | return obj;
69 | }
70 |
71 | function makeConstant(info) {
72 | return gi.GetConstantValue(info);
73 | }
74 |
75 | function makeFunction(info) {
76 | return gi.MakeFunction(info);
77 | }
78 |
79 | function makeStruct(info) {
80 | function fieldGetter(fieldInfo) {
81 | return function() {
82 | return gi.BoxedFieldGetter(this, fieldInfo);
83 | };
84 | }
85 | function fieldSetter(fieldInfo) {
86 | return function(value) {
87 | return gi.BoxedFieldSetter(this, fieldInfo, value);
88 | };
89 | }
90 |
91 | var constructor = gi.MakeBoxed(info);
92 |
93 | var nMethods = GIRepository.struct_info_get_n_methods(info);
94 | for (var i = 0; i < nMethods; i++) {
95 | var methodInfo = GIRepository.struct_info_get_method(info, i);
96 | declareFunction(constructor, methodInfo);
97 | }
98 |
99 | var nProperties = GIRepository.struct_info_get_n_fields(info);
100 | for (var i = 0; i < nProperties; i++) {
101 | var fieldInfo = GIRepository.struct_info_get_field(info, i);
102 | var fieldFlags = GIRepository.field_info_get_flags(fieldInfo);
103 |
104 | var fieldName = GIRepository.BaseInfo_get_name.call(fieldInfo);
105 | var jsFieldName = fieldName.replace(/-/g, '_');
106 |
107 | var desc = {};
108 |
109 | if (fieldFlags & GIRepository.FieldInfoFlags.READABLE)
110 | desc.get = fieldGetter(fieldInfo);
111 |
112 | if (fieldFlags & GIRepository.FieldInfoFlags.WRITABLE) {
113 | desc.set = fieldSetter(fieldInfo);
114 | desc.configurable = true;
115 | }
116 |
117 | Object.defineProperty(constructor.prototype, jsFieldName, desc);
118 | }
119 |
120 | return constructor;
121 | }
122 |
123 | function makeObject(info) {
124 | function propertyGetter(propertyName) {
125 | return function() {
126 | return gi.ObjectPropertyGetter(this, propertyName);
127 | };
128 | }
129 | function propertySetter(propertyName) {
130 | return function(value) {
131 | return gi.ObjectPropertySetter(this, propertyName, value);
132 | };
133 | }
134 |
135 | var constructor = gi.MakeClass(info);
136 |
137 | var nMethods = GIRepository.object_info_get_n_methods(info);
138 | for (var i = 0; i < nMethods; i++) {
139 | var methodInfo = GIRepository.object_info_get_method(info, i);
140 | declareFunction(constructor, methodInfo);
141 | }
142 |
143 | var nProperties = GIRepository.object_info_get_n_properties(info);
144 | for (var i = 0; i < nProperties; i++) {
145 | var propertyInfo = GIRepository.object_info_get_property(info, i);
146 |
147 | var propertyName = GIRepository.BaseInfo_get_name.call(propertyInfo);
148 | var jsPropertyName = propertyName.replace(/-/g, '_');
149 |
150 | Object.defineProperty(constructor.prototype, jsPropertyName, {
151 | configurable: true,
152 | get: propertyGetter(propertyName),
153 | set: propertySetter(propertyName),
154 | });
155 | }
156 |
157 | return constructor;
158 | }
159 |
160 | function makeInfo(info) {
161 | var type = GIRepository.BaseInfo_get_type.call(info);
162 |
163 | if (type === GIRepository.InfoType.ENUM)
164 | return makeEnum(info);
165 | if (type === GIRepository.InfoType.CONSTANT)
166 | return makeConstant(info);
167 | if (type === GIRepository.InfoType.FUNCTION)
168 | return makeFunction(info);
169 | if (type === GIRepository.InfoType.OBJECT)
170 | return makeObject(info);
171 | if (type === GIRepository.InfoType.STRUCT)
172 | return makeStruct(info);
173 | }
174 |
175 | function importNS(ns, version) {
176 | var module = {};
177 |
178 | var repo = GIRepository.Repository_get_default();
179 | GIRepository.Repository_require.call(repo, ns, version || null, 0);
180 |
181 | var nInfos = GIRepository.Repository_get_n_infos.call(repo, ns);
182 | for (var i = 0; i < nInfos; i++) {
183 | var info = GIRepository.Repository_get_info.call(repo, ns, i);
184 | var name = GIRepository.BaseInfo_get_name.call(info);
185 | module[name] = makeInfo(info);
186 | }
187 |
188 | var override;
189 | try {
190 | override = require('./overrides/' + ns);
191 | } catch (e) {
192 | // No override
193 | }
194 |
195 | if (override)
196 | override.apply(module);
197 |
198 | return module;
199 | }
200 |
201 | // Used to avoid exporting same module every time it's required
202 | var cache = Object.create(null);
203 | exports.importNS = function(ns, version) {
204 | var module = cache[ns] || (cache[ns] = {});
205 | var ver = version || '*';
206 | return module[ver] || (module[ver] = importNS(ns, version));
207 | };
208 |
209 | exports.startLoop = function() {
210 | gi.StartLoop();
211 | };
212 |
--------------------------------------------------------------------------------
/lib/overrides/GLib.js:
--------------------------------------------------------------------------------
1 |
2 | exports.apply = function(GLib) {
3 | GLib.test_override = function() {
4 | return 1 + 1;
5 | };
6 | };
7 |
--------------------------------------------------------------------------------
/node-gtk.doap:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 | node-gtk
9 | node-gtk
10 |
11 |
12 | GNOME Gtk+ bindings for NodeJS
13 | GNOME Gtk+ bindings for NodeJS
14 |
15 | C++, JS
16 |
17 |
18 |
19 | Jasper St. Pierre
20 |
21 | jstpierre
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "node-gtk",
3 | "version": "0.0.20",
4 | "description": "GNOME Gtk+ bindings for NodeJS",
5 | "main": "lib/index.js",
6 | "scripts": {
7 | "install": "if [ \"$(uname)\" = \"Darwin\" ] && [ \"$(which brew)\" != \"\" ]; then export PKG_CONFIG_PATH=$(brew --prefix libffi)/lib/pkgconfig; fi; node-pre-gyp install --fallback-to-build",
8 | "test": "echo \"Error: no test specified\" && exit 1"
9 | },
10 | "repository": {
11 | "type": "git",
12 | "url": "git+https://github.com/WebReflection/node-gtk.git"
13 | },
14 | "keywords": [
15 | "GNOME",
16 | "gobject-introspection",
17 | "GObject",
18 | "Gtk+",
19 | "Gtk",
20 | "bindings"
21 | ],
22 | "author": "Jasper St. Pierre",
23 | "license": "MIT",
24 | "bugs": {
25 | "url": "https://github.com/WebReflection/node-gtk/issues"
26 | },
27 | "homepage": "https://github.com/WebReflection/node-gtk#readme",
28 | "dependencies": {
29 | "node-gyp": "^3.2.1",
30 | "node-pre-gyp": "^0.6.x",
31 | "node-pre-gyp-github": "^1.x"
32 | },
33 | "binary": {
34 | "module_name": "node-gtk",
35 | "module_path": "./build/{configuration}/{node_abi}-{platform}-{arch}/",
36 | "package_name": "{node_abi}-{platform}-{arch}.tar.gz",
37 | "host": "https://github.com/WebReflection/node-gtk/releases/download/0.0.16"
38 | }
39 | }
--------------------------------------------------------------------------------
/src/boxed.cc:
--------------------------------------------------------------------------------
1 |
2 | #include "boxed.h"
3 | #include "function.h"
4 |
5 | using namespace v8;
6 |
7 | namespace GNodeJS {
8 |
9 | struct Boxed {
10 | GIBaseInfo *info;
11 | };
12 |
13 | static G_DEFINE_QUARK(gnode_js_template, gnode_js_template);
14 |
15 | static void BoxedClassDestroyed(const WeakCallbackData &data) {
16 | GIBaseInfo *info = data.GetParameter ();
17 | GType gtype = g_registered_type_info_get_g_type ((GIRegisteredTypeInfo *) info);
18 |
19 | void *type_data = g_type_get_qdata (gtype, gnode_js_template_quark ());
20 | assert (type_data != NULL);
21 | Persistent *persistent = (Persistent *) type_data;
22 | delete persistent;
23 |
24 | g_type_set_qdata (gtype, gnode_js_template_quark (), NULL);
25 | g_base_info_unref (info);
26 | }
27 |
28 | static void BoxedConstructor(const FunctionCallbackInfo &args) {
29 | Isolate *isolate = args.GetIsolate ();
30 |
31 | /* See gobject.cc for how this works */
32 |
33 | if (!args.IsConstructCall ()) {
34 | isolate->ThrowException (Exception::TypeError (String::NewFromUtf8 (isolate, "Not a construct call.")));
35 | return;
36 | }
37 |
38 | Local