├── .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 self = args.This (); 39 | 40 | if (args[0]->IsExternal ()) { 41 | /* The External case. This is how WrapperFromBoxed is called. */ 42 | 43 | /* XXX: We might want to copy the boxed? */ 44 | void *boxed = External::Cast (*args[0])->Value (); 45 | 46 | self->SetAlignedPointerInInternalField (0, boxed); 47 | } else { 48 | /* TODO: Boxed construction not supported yet. */ 49 | g_assert_not_reached (); 50 | } 51 | } 52 | 53 | static Local GetBoxedTemplate(Isolate *isolate, GIBaseInfo *info, GType gtype) { 54 | void *data; 55 | 56 | if (gtype == G_TYPE_NONE) 57 | data = NULL; 58 | else 59 | data = g_type_get_qdata (gtype, gnode_js_template_quark ()); 60 | 61 | if (data) { 62 | Persistent *persistent = (Persistent *) data; 63 | Local tpl = Local::New (isolate, *persistent); 64 | return tpl; 65 | } else { 66 | Local tpl = FunctionTemplate::New (isolate, BoxedConstructor, External::New (isolate, info)); 67 | 68 | Persistent *persistent = new Persistent(isolate, tpl); 69 | persistent->SetWeak (g_base_info_ref (info), BoxedClassDestroyed); 70 | 71 | const char *class_name = g_base_info_get_name (info); 72 | tpl->SetClassName (String::NewFromUtf8 (isolate, class_name)); 73 | 74 | tpl->InstanceTemplate ()->SetInternalFieldCount (1); 75 | 76 | if (gtype != G_TYPE_NONE) 77 | g_type_set_qdata (gtype, gnode_js_template_quark (), persistent); 78 | 79 | return tpl; 80 | } 81 | } 82 | 83 | static Local GetBoxedTemplateFromGI(Isolate *isolate, GIBaseInfo *info) { 84 | GType gtype = g_registered_type_info_get_g_type ((GIRegisteredTypeInfo *) info); 85 | return GetBoxedTemplate (isolate, info, gtype); 86 | } 87 | 88 | Local MakeBoxed(Isolate *isolate, GIBaseInfo *info) { 89 | Local tpl = GetBoxedTemplateFromGI (isolate, info); 90 | return tpl->GetFunction (); 91 | } 92 | 93 | Local WrapperFromBoxed(Isolate *isolate, GIBaseInfo *info, void *data) { 94 | Local constructor = MakeBoxed (isolate, info); 95 | 96 | Local boxed_external = External::New (isolate, data); 97 | Local args[] = { boxed_external }; 98 | Local obj = constructor->NewInstance (1, args); 99 | return obj; 100 | } 101 | 102 | void * BoxedFromWrapper(Local value) { 103 | Local object = value->ToObject (); 104 | void *data = object->GetAlignedPointerFromInternalField (0); 105 | return data; 106 | } 107 | 108 | }; 109 | -------------------------------------------------------------------------------- /src/boxed.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include 5 | #include 6 | 7 | namespace GNodeJS { 8 | 9 | v8::Local MakeBoxed(v8::Isolate *isolate, GIBaseInfo *info); 10 | 11 | v8::Local WrapperFromBoxed(v8::Isolate *isolate, GIBaseInfo *info, void *data); 12 | void * BoxedFromWrapper(v8::Local); 13 | 14 | }; 15 | -------------------------------------------------------------------------------- /src/closure.cc: -------------------------------------------------------------------------------- 1 | 2 | #include "function.h" 3 | 4 | #include "value.h" 5 | 6 | using namespace v8; 7 | 8 | namespace GNodeJS { 9 | 10 | struct Closure { 11 | GClosure base; 12 | Persistent persistent; 13 | 14 | static void Marshal(GClosure *closure, 15 | GValue *g_return_value, 16 | uint argc, const GValue *g_argv, 17 | gpointer invocation_hint, 18 | gpointer marshal_data); 19 | 20 | static void Invalidated(gpointer data, GClosure *closure); 21 | }; 22 | 23 | void Closure::Marshal(GClosure *base, 24 | GValue *g_return_value, 25 | uint argc, const GValue *g_argv, 26 | gpointer invocation_hint, 27 | gpointer marshal_data) { 28 | /* XXX: Any other way to get this? */ 29 | Isolate *isolate = Isolate::GetCurrent (); 30 | HandleScope scope(isolate); 31 | 32 | Closure *closure = (Closure *) base; 33 | Local func = Local::New(isolate, closure->persistent); 34 | 35 | #ifndef __linux__ 36 | Local* argv = new Local[argc]; 37 | #else 38 | Local argv[argc]; 39 | #endif 40 | 41 | for (uint i = 0; i < argc; i++) 42 | argv[i] = GValueToV8 (isolate, &g_argv[i]); 43 | 44 | Local this_obj = func; 45 | Local return_value = func->Call (this_obj, argc, argv); 46 | 47 | #ifndef __linux__ 48 | delete[] argv; 49 | #endif 50 | 51 | if (g_return_value) 52 | V8ToGValue (g_return_value, return_value); 53 | } 54 | 55 | void Closure::Invalidated(gpointer data, GClosure *base) { 56 | Closure *closure = (Closure *) base; 57 | closure->~Closure(); 58 | } 59 | 60 | GClosure *MakeClosure(Isolate *isolate, Local function) { 61 | Closure *closure = (Closure *) g_closure_new_simple (sizeof (*closure), NULL); 62 | closure->persistent.Reset(isolate, function); 63 | GClosure *gclosure = &closure->base; 64 | g_closure_set_marshal (gclosure, Closure::Marshal); 65 | g_closure_add_invalidate_notifier (gclosure, NULL, Closure::Invalidated); 66 | return gclosure; 67 | } 68 | 69 | }; 70 | -------------------------------------------------------------------------------- /src/closure.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include 5 | #include 6 | 7 | namespace GNodeJS { 8 | 9 | GClosure *MakeClosure(v8::Isolate *isolate, v8::Local function); 10 | 11 | }; 12 | -------------------------------------------------------------------------------- /src/function.cc: -------------------------------------------------------------------------------- 1 | 2 | #include "function.h" 3 | #include "value.h" 4 | #include "gobject.h" 5 | 6 | #include 7 | 8 | using namespace v8; 9 | 10 | namespace GNodeJS { 11 | 12 | struct FunctionInfo { 13 | GIFunctionInfo *info; 14 | GIFunctionInvoker invoker; 15 | }; 16 | 17 | struct Parameter { 18 | enum { 19 | NORMAL, ARRAY, SKIP, 20 | } type; 21 | }; 22 | 23 | static void FunctionInvoker(const FunctionCallbackInfo &args) { 24 | Isolate *isolate = args.GetIsolate(); 25 | FunctionInfo *func = (FunctionInfo *) External::Cast (*args.Data ())->Value (); 26 | 27 | GIBaseInfo *info = func->info; 28 | GError *error = NULL; 29 | 30 | int n_callable_args = g_callable_info_get_n_args ((GICallableInfo *) info); 31 | int n_total_args = n_callable_args; 32 | int n_in_args = 0; 33 | 34 | Parameter call_parameters[n_callable_args]; 35 | 36 | for (int i = 0; i < n_callable_args; i++) { 37 | GIArgInfo arg_info; 38 | g_callable_info_load_arg ((GICallableInfo *) info, i, &arg_info); 39 | 40 | GITypeInfo type_info; 41 | g_arg_info_load_type (&arg_info, &type_info); 42 | 43 | int array_length_idx = g_type_info_get_array_length (&type_info); 44 | if (array_length_idx >= 0) { 45 | call_parameters[i].type = Parameter::ARRAY; 46 | call_parameters[array_length_idx].type = Parameter::SKIP; 47 | } 48 | } 49 | 50 | for (int i = 0; i < n_callable_args; i++) { 51 | GIArgInfo arg_info; 52 | 53 | if (call_parameters[i].type == Parameter::SKIP) 54 | continue; 55 | 56 | g_callable_info_load_arg ((GICallableInfo *) info, i, &arg_info); 57 | 58 | if (g_arg_info_get_direction (&arg_info) == GI_DIRECTION_IN || 59 | g_arg_info_get_direction (&arg_info) == GI_DIRECTION_INOUT) 60 | n_in_args++; 61 | } 62 | 63 | if (args.Length() < n_in_args) { 64 | isolate->ThrowException (Exception::TypeError (String::NewFromUtf8 (isolate, "Not enough arguments."))); 65 | return; 66 | } 67 | 68 | gboolean is_method = ((g_function_info_get_flags (info) & GI_FUNCTION_IS_METHOD) != 0 && 69 | (g_function_info_get_flags (info) & GI_FUNCTION_IS_CONSTRUCTOR) == 0); 70 | if (is_method) 71 | n_total_args++; 72 | 73 | gboolean can_throw = g_callable_info_can_throw_gerror (info); 74 | if (can_throw) 75 | n_total_args++; 76 | 77 | GIArgument total_arg_values[n_total_args]; 78 | GIArgument *callable_arg_values; 79 | 80 | if (is_method) { 81 | GIBaseInfo *container = g_base_info_get_container (func->info); 82 | V8ToGIArgument (isolate, container, &total_arg_values[0], args.This ()); 83 | callable_arg_values = &total_arg_values[1]; 84 | } else { 85 | callable_arg_values = &total_arg_values[0]; 86 | } 87 | 88 | int in_arg = 0, i = 0; 89 | for (; i < n_callable_args; i++) { 90 | if (call_parameters[i].type == Parameter::SKIP) 91 | continue; 92 | 93 | GIArgInfo arg_info = {}; 94 | g_callable_info_load_arg ((GICallableInfo *) info, i, &arg_info); 95 | GIDirection direction = g_arg_info_get_direction (&arg_info); 96 | 97 | if (direction == GI_DIRECTION_OUT) { 98 | if (g_arg_info_is_caller_allocates (&arg_info)) { 99 | assert (0); 100 | } else { 101 | callable_arg_values[i].v_pointer = NULL; 102 | } 103 | } else { 104 | if (call_parameters[i].type == Parameter::ARRAY) { 105 | GITypeInfo type_info; 106 | g_arg_info_load_type (&arg_info, &type_info); 107 | bool may_be_null = g_arg_info_may_be_null (&arg_info); 108 | size_t array_length; 109 | V8ToGIArgument (isolate, &type_info, &callable_arg_values[i], args[in_arg], may_be_null, &array_length); 110 | 111 | Local array_length_value = Integer::New (isolate, array_length); 112 | 113 | int array_length_pos = g_type_info_get_array_length (&type_info); 114 | GIArgInfo array_length_arg; 115 | g_callable_info_load_arg ((GICallableInfo *) info, array_length_pos, &array_length_arg); 116 | 117 | { 118 | GITypeInfo type_info; 119 | g_arg_info_load_type (&array_length_arg, &type_info); 120 | V8ToGIArgument (isolate, &type_info, &callable_arg_values[array_length_pos], array_length_value, false); 121 | } 122 | } else { 123 | GITypeInfo type_info; 124 | g_arg_info_load_type (&arg_info, &type_info); 125 | bool may_be_null = g_arg_info_may_be_null (&arg_info); 126 | V8ToGIArgument (isolate, &type_info, &callable_arg_values[i], args[in_arg], may_be_null); 127 | } 128 | 129 | in_arg++; 130 | } 131 | } 132 | 133 | if (can_throw) 134 | callable_arg_values[i].v_pointer = &error; 135 | 136 | void *ffi_arg_pointers[n_total_args]; 137 | for (int i = 0; i < n_total_args; i++) 138 | ffi_arg_pointers[i] = &total_arg_values[i]; 139 | 140 | GIArgument return_value; 141 | ffi_call (&func->invoker.cif, FFI_FN (func->invoker.native_address), 142 | &return_value, ffi_arg_pointers); 143 | 144 | for (int i = 0; i < n_callable_args; i++) { 145 | GIArgInfo arg_info; 146 | g_callable_info_load_arg ((GICallableInfo *) info, i, &arg_info); 147 | GIDirection direction = g_arg_info_get_direction (&arg_info); 148 | if (direction == GI_DIRECTION_OUT) { 149 | /* XXX: Process out value. */ 150 | } else { 151 | GITypeInfo type_info; 152 | g_arg_info_load_type (&arg_info, &type_info); 153 | FreeGIArgument (&type_info, &callable_arg_values[i]); 154 | } 155 | } 156 | 157 | if (error) { 158 | isolate->ThrowException (Exception::TypeError (String::NewFromUtf8 (isolate, error->message))); 159 | return; 160 | } 161 | 162 | GITypeInfo return_value_type; 163 | g_callable_info_load_return_type ((GICallableInfo *) info, &return_value_type); 164 | args.GetReturnValue ().Set (GIArgumentToV8 (isolate, &return_value_type, &return_value)); 165 | } 166 | 167 | static void FunctionDestroyed(const WeakCallbackData &data) { 168 | FunctionInfo *func = data.GetParameter (); 169 | g_base_info_unref (func->info); 170 | g_function_invoker_destroy (&func->invoker); 171 | g_free (func); 172 | } 173 | 174 | Local MakeFunction(Isolate *isolate, GIBaseInfo *info) { 175 | FunctionInfo *func = g_new0 (FunctionInfo, 1); 176 | func->info = g_base_info_ref (info); 177 | 178 | g_function_info_prep_invoker (func->info, &func->invoker, NULL); 179 | 180 | Local tpl = FunctionTemplate::New (isolate, FunctionInvoker, External::New (isolate, func)); 181 | Local fn = tpl->GetFunction (); 182 | 183 | Persistent persistent(isolate, tpl); 184 | persistent.SetWeak (func, FunctionDestroyed); 185 | 186 | const char *function_name = g_base_info_get_name (info); 187 | fn->SetName (String::NewFromUtf8 (isolate, function_name)); 188 | 189 | return fn; 190 | } 191 | 192 | #if 0 193 | class TrampolineInfo { 194 | ffi_cif cif; 195 | ffi_closure *closure; 196 | Persistent persistent; 197 | GICallableInfo *info; 198 | GIScopeType scope_type; 199 | 200 | TrampolineInfo(Local function, 201 | GICallableInfo *info, 202 | GIScopeType scope_type); 203 | 204 | void Dispose(); 205 | static void Call(ffi_cif *cif, void *result, void **args, void *data); 206 | void *GetClosure(); 207 | }; 208 | 209 | void TrampolineInfo::Dispose() { 210 | persistent = nullptr; 211 | g_base_info_unref (info); 212 | g_callable_info_free_closure (info, closure); 213 | }; 214 | 215 | void TrampolineInfo::Call(ffi_cif *cif, 216 | void *result, 217 | void **args, 218 | void *data) { 219 | TrampolineInfo *trampoline = (TrampolineInfo *) data; 220 | 221 | int argc = g_callable_info_get_n_args (trampoline->info); 222 | Local argv[argc]; 223 | 224 | for (int i = 0; i < argc; i++) { 225 | GIArgInfo arg_info; 226 | g_callable_info_load_arg (trampoline->info, i, &arg_info); 227 | GITypeInfo type_info; 228 | g_arg_info_load_type (&arg_info, &type_info); 229 | argv[i] = GIArgumentToV8 (&type_info, (GIArgument *) &args[i]); 230 | } 231 | 232 | Local func = trampoline->func; 233 | /* Provide a bogus "this" function. Any interested callers should 234 | * bind their callbacks to what they're intersted in... */ 235 | Local this_obj = func; 236 | Local return_value = func->Call (this_obj, argc, argv); 237 | GITypeInfo type_info; 238 | g_callable_info_load_return_type (trampoline->info, &type_info); 239 | V8ToGIArgument (&type_info, (GIArgument *) &result, return_value, 240 | g_callable_info_may_return_null (trampoline->info)); 241 | } 242 | 243 | TrampolineInfo::TrampolineInfo(Local function, 244 | GICallableInfo *info, 245 | GIScopeType scope_type) { 246 | this->closure = g_callable_info_prepare_closure (info, &cif, Call, this); 247 | this->func = Persistent::New (function); 248 | this->info = g_base_info_ref (info); 249 | this->scope_type = scope_type; 250 | } 251 | #endif 252 | 253 | }; 254 | -------------------------------------------------------------------------------- /src/function.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace GNodeJS { 9 | 10 | v8::Local MakeFunction(v8::Isolate *isolate, GIBaseInfo *base_info); 11 | 12 | }; 13 | -------------------------------------------------------------------------------- /src/gi.cc: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | #include "value.h" 6 | #include "boxed.h" 7 | #include "function.h" 8 | #include "gobject.h" 9 | #include "loop.h" 10 | 11 | #include 12 | 13 | using namespace v8; 14 | 15 | static void DefineFunction(Isolate *isolate, Local module_obj, GIBaseInfo *info) { 16 | const char *function_name = g_base_info_get_name ((GIBaseInfo *) info); 17 | module_obj->Set (String::NewFromUtf8 (isolate, function_name), GNodeJS::MakeFunction (isolate, info)); 18 | } 19 | 20 | static void DefineFunction(Isolate *isolate, Local module_obj, GIBaseInfo *info, const char *base_name) { 21 | char *function_name = g_strdup_printf ("%s_%s", base_name, g_base_info_get_name ((GIBaseInfo *) info)); 22 | module_obj->Set (String::NewFromUtf8 (isolate, function_name), GNodeJS::MakeFunction (isolate, info)); 23 | g_free (function_name); 24 | } 25 | 26 | static void DefineObjectFunctions(Isolate *isolate, Local module_obj, GIBaseInfo *info) { 27 | const char *object_name = g_base_info_get_name ((GIBaseInfo *) info); 28 | 29 | int n_methods = g_object_info_get_n_methods (info); 30 | for (int i = 0; i < n_methods; i++) { 31 | GIFunctionInfo *meth_info = g_object_info_get_method (info, i); 32 | DefineFunction (isolate, module_obj, meth_info, object_name); 33 | g_base_info_unref ((GIBaseInfo *) meth_info); 34 | } 35 | } 36 | 37 | static void DefineBoxedFunctions(Isolate *isolate, Local module_obj, GIBaseInfo *info) { 38 | const char *object_name = g_base_info_get_name ((GIBaseInfo *) info); 39 | 40 | int n_methods = g_struct_info_get_n_methods (info); 41 | for (int i = 0; i < n_methods; i++) { 42 | GIFunctionInfo *meth_info = g_struct_info_get_method (info, i); 43 | DefineFunction (isolate, module_obj, meth_info, object_name); 44 | g_base_info_unref ((GIBaseInfo *) meth_info); 45 | } 46 | } 47 | 48 | static void DefineBootstrapInfo(Isolate *isolate, Local module_obj, GIBaseInfo *info) { 49 | GIInfoType type = g_base_info_get_type (info); 50 | 51 | switch (type) { 52 | case GI_INFO_TYPE_FUNCTION: 53 | DefineFunction (isolate, module_obj, info); 54 | break; 55 | case GI_INFO_TYPE_OBJECT: 56 | DefineObjectFunctions (isolate, module_obj, info); 57 | break; 58 | case GI_INFO_TYPE_BOXED: 59 | case GI_INFO_TYPE_STRUCT: 60 | DefineBoxedFunctions (isolate, module_obj, info); 61 | break; 62 | default: 63 | break; 64 | } 65 | } 66 | 67 | static void Bootstrap(const FunctionCallbackInfo &args) { 68 | Isolate *isolate = args.GetIsolate (); 69 | 70 | GIRepository *repo = g_irepository_get_default (); 71 | GError *error = NULL; 72 | 73 | const char *ns = "GIRepository"; 74 | g_irepository_require (repo, ns, NULL, (GIRepositoryLoadFlags) 0, &error); 75 | if (error) { 76 | isolate->ThrowException (Exception::TypeError (String::NewFromUtf8 (isolate, error->message))); 77 | return; 78 | } 79 | 80 | Local module_obj = Object::New (isolate); 81 | 82 | int n = g_irepository_get_n_infos (repo, ns); 83 | for (int i = 0; i < n; i++) { 84 | GIBaseInfo *info = g_irepository_get_info (repo, ns, i); 85 | DefineBootstrapInfo (isolate, module_obj, info); 86 | g_base_info_unref (info); 87 | } 88 | 89 | args.GetReturnValue ().Set (module_obj); 90 | } 91 | 92 | static void GetConstantValue(const FunctionCallbackInfo &args) { 93 | Isolate *isolate = args.GetIsolate (); 94 | GIBaseInfo *info = (GIBaseInfo *) GNodeJS::BoxedFromWrapper (args[0]); 95 | GITypeInfo *type_info = g_constant_info_get_type ((GIConstantInfo *) info); 96 | GIArgument garg; 97 | g_constant_info_get_value ((GIConstantInfo *) info, &garg); 98 | args.GetReturnValue ().Set (GNodeJS::GIArgumentToV8 (isolate, type_info, &garg)); 99 | } 100 | 101 | static void MakeFunction(const FunctionCallbackInfo &args) { 102 | Isolate *isolate = args.GetIsolate (); 103 | GIBaseInfo *info = (GIBaseInfo *) GNodeJS::BoxedFromWrapper (args[0]); 104 | args.GetReturnValue ().Set (GNodeJS::MakeFunction (isolate, info)); 105 | } 106 | 107 | static void MakeClass(const FunctionCallbackInfo &args) { 108 | Isolate *isolate = args.GetIsolate (); 109 | GIBaseInfo *info = (GIBaseInfo *) GNodeJS::BoxedFromWrapper (args[0]); 110 | args.GetReturnValue ().Set (GNodeJS::MakeClass (isolate, info)); 111 | } 112 | 113 | static void MakeBoxed(const FunctionCallbackInfo &args) { 114 | Isolate *isolate = args.GetIsolate (); 115 | GIBaseInfo *info = (GIBaseInfo *) GNodeJS::BoxedFromWrapper (args[0]); 116 | args.GetReturnValue ().Set (GNodeJS::MakeBoxed (isolate, info)); 117 | } 118 | 119 | static void ObjectPropertyGetter(const FunctionCallbackInfo &args) { 120 | Isolate *isolate = args.GetIsolate (); 121 | GObject *gobject = GNodeJS::GObjectFromWrapper (args[0]); 122 | String::Utf8Value prop_name_v (args[1]->ToString ()); 123 | const char *prop_name = *prop_name_v; 124 | 125 | GParamSpec *pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (gobject), prop_name); 126 | GValue value = {}; 127 | g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec)); 128 | 129 | g_object_get_property (gobject, prop_name, &value); 130 | 131 | args.GetReturnValue ().Set (GNodeJS::GValueToV8 (isolate, &value)); 132 | } 133 | 134 | static void ObjectPropertySetter(const FunctionCallbackInfo &args) { 135 | GObject *gobject = GNodeJS::GObjectFromWrapper (args[0]); 136 | String::Utf8Value prop_name_v (args[1]->ToString ()); 137 | const char *prop_name = *prop_name_v; 138 | 139 | GParamSpec *pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (gobject), prop_name); 140 | GValue value = {}; 141 | g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec)); 142 | 143 | GNodeJS::V8ToGValue (&value, args[2]); 144 | 145 | g_object_set_property (gobject, prop_name, &value); 146 | } 147 | 148 | static void BoxedFieldGetter(const FunctionCallbackInfo &args) { 149 | Isolate *isolate = args.GetIsolate (); 150 | void *boxed = GNodeJS::BoxedFromWrapper (args[0]); 151 | GIFieldInfo *field_info = (GIFieldInfo *) GNodeJS::BoxedFromWrapper (args[1]); 152 | GIArgument argument; 153 | GITypeInfo *type_info = g_field_info_get_type (field_info); 154 | if (!g_field_info_get_field (field_info, boxed, &argument)) { 155 | isolate->ThrowException (Exception::Error (String::NewFromUtf8 (isolate, "Could not get boxed field"))); 156 | goto out; 157 | } 158 | args.GetReturnValue ().Set (GNodeJS::GIArgumentToV8 (isolate, type_info, &argument)); 159 | 160 | out: 161 | g_base_info_unref (type_info); 162 | } 163 | 164 | static void BoxedFieldSetter(const FunctionCallbackInfo &args) { 165 | Isolate *isolate = args.GetIsolate (); 166 | void *boxed = GNodeJS::BoxedFromWrapper (args[0]); 167 | GIFieldInfo *field_info = (GIFieldInfo *) GNodeJS::BoxedFromWrapper (args[1]); 168 | GIArgument argument; 169 | GITypeInfo *type_info = g_field_info_get_type (field_info); 170 | GNodeJS::V8ToGIArgument (isolate, type_info, &argument, args[2], true); 171 | if (!g_field_info_set_field (field_info, boxed, &argument)) 172 | isolate->ThrowException (Exception::Error (String::NewFromUtf8 (isolate, "Could not set boxed field"))); 173 | g_base_info_unref (type_info); 174 | } 175 | 176 | static void StartLoop(const FunctionCallbackInfo &args) { 177 | GNodeJS::StartLoop (); 178 | } 179 | 180 | void InitModule(Local exports, Local module, void *priv) { 181 | Isolate *isolate = Isolate::GetCurrent (); 182 | 183 | /* XXX: This is an ugly collection of random bits and pieces. We should organize 184 | * this functionality a lot better and clean it up. */ 185 | exports->Set (String::NewFromUtf8 (isolate, "Bootstrap"), FunctionTemplate::New (isolate, Bootstrap)->GetFunction ()); 186 | exports->Set (String::NewFromUtf8 (isolate, "GetConstantValue"), FunctionTemplate::New (isolate, GetConstantValue)->GetFunction ()); 187 | exports->Set (String::NewFromUtf8 (isolate, "MakeFunction"), FunctionTemplate::New (isolate, MakeFunction)->GetFunction ()); 188 | 189 | exports->Set (String::NewFromUtf8 (isolate, "MakeClass"), FunctionTemplate::New (isolate, MakeClass)->GetFunction ()); 190 | exports->Set (String::NewFromUtf8 (isolate, "ObjectPropertyGetter"), FunctionTemplate::New (isolate, ObjectPropertyGetter)->GetFunction ()); 191 | exports->Set (String::NewFromUtf8 (isolate, "ObjectPropertySetter"), FunctionTemplate::New (isolate, ObjectPropertySetter)->GetFunction ()); 192 | 193 | exports->Set (String::NewFromUtf8 (isolate, "MakeBoxed"), FunctionTemplate::New (isolate, MakeBoxed)->GetFunction ()); 194 | exports->Set (String::NewFromUtf8 (isolate, "BoxedFieldGetter"), FunctionTemplate::New (isolate, BoxedFieldGetter)->GetFunction ()); 195 | exports->Set (String::NewFromUtf8 (isolate, "BoxedFieldSetter"), FunctionTemplate::New (isolate, BoxedFieldSetter)->GetFunction ()); 196 | 197 | exports->Set (String::NewFromUtf8 (isolate, "StartLoop"), FunctionTemplate::New (isolate, StartLoop)->GetFunction ()); 198 | } 199 | 200 | NODE_MODULE(gi, InitModule) 201 | -------------------------------------------------------------------------------- /src/gobject.cc: -------------------------------------------------------------------------------- 1 | 2 | #include "gobject.h" 3 | 4 | #include "function.h" 5 | #include "value.h" 6 | #include "closure.h" 7 | 8 | using namespace v8; 9 | 10 | namespace GNodeJS { 11 | 12 | static bool InitGParameterFromProperty(GParameter *parameter, 13 | void *klass, 14 | Local name, 15 | Local value) { 16 | String::Utf8Value name_str (name); 17 | GParamSpec *pspec = g_object_class_find_property (G_OBJECT_CLASS (klass), *name_str); 18 | if (pspec == NULL) 19 | return false; 20 | 21 | parameter->name = pspec->name; 22 | g_value_init (¶meter->value, G_PARAM_SPEC_VALUE_TYPE (pspec)); 23 | V8ToGValue (¶meter->value, value); 24 | return true; 25 | } 26 | 27 | static bool InitGParametersFromProperty(GParameter **parameters_p, 28 | int *n_parameters_p, 29 | void *klass, 30 | Local property_hash) { 31 | Local properties = property_hash->GetOwnPropertyNames (); 32 | int n_parameters = properties->Length (); 33 | GParameter *parameters = g_new0 (GParameter, n_parameters); 34 | 35 | for (int i = 0; i < n_parameters; i++) { 36 | Local name = properties->Get (i); 37 | Local value = property_hash->Get (name); 38 | 39 | if (!InitGParameterFromProperty (¶meters[i], klass, name->ToString (), value)) 40 | return false; 41 | } 42 | 43 | *parameters_p = parameters; 44 | *n_parameters_p = n_parameters; 45 | return true; 46 | } 47 | 48 | static void ToggleNotify(gpointer user_data, GObject *gobject, gboolean toggle_down); 49 | 50 | static G_DEFINE_QUARK(gnode_js_object, gnode_js_object); 51 | 52 | static void AssociateGObject(Isolate *isolate, Local object, GObject *gobject) { 53 | object->SetAlignedPointerInInternalField (0, gobject); 54 | 55 | g_object_ref_sink (gobject); 56 | g_object_add_toggle_ref (gobject, ToggleNotify, NULL); 57 | 58 | Persistent *persistent = new Persistent(isolate, object); 59 | g_object_set_qdata (gobject, gnode_js_object_quark (), persistent); 60 | } 61 | 62 | static void GObjectConstructor(const FunctionCallbackInfo &args) { 63 | Isolate *isolate = args.GetIsolate (); 64 | 65 | /* The flow of this function is a bit twisty. 66 | 67 | * There's two cases for when this code is called: 68 | * user code doing `new Gtk.Widget({ ... })`, and 69 | * internal code as part of WrapperFromGObject, where 70 | * the constructor is called with one external. */ 71 | 72 | if (!args.IsConstructCall ()) { 73 | isolate->ThrowException (Exception::TypeError (String::NewFromUtf8 (isolate, "Not a construct call."))); 74 | return; 75 | } 76 | 77 | Local self = args.This (); 78 | 79 | if (args[0]->IsExternal ()) { 80 | /* The External case. This is how WrapperFromGObject is called. */ 81 | 82 | void *data = External::Cast (*args[0])->Value (); 83 | GObject *gobject = G_OBJECT (data); 84 | 85 | AssociateGObject (isolate, self, gobject); 86 | } else { 87 | /* User code calling `new Gtk.Widget({ ... })` */ 88 | 89 | GObject *gobject; 90 | GIBaseInfo *info = (GIBaseInfo *) External::Cast (*args.Data ())->Value (); 91 | GType gtype = g_registered_type_info_get_g_type ((GIRegisteredTypeInfo *) info); 92 | void *klass = g_type_class_ref (gtype); 93 | 94 | GParameter *parameters = NULL; 95 | int n_parameters = 0; 96 | 97 | if (args[0]->IsObject ()) { 98 | Local property_hash = args[0]->ToObject (); 99 | 100 | if (!InitGParametersFromProperty (¶meters, &n_parameters, klass, property_hash)) { 101 | isolate->ThrowException (Exception::TypeError (String::NewFromUtf8 (isolate, "Unable to make GParameters."))); 102 | goto out; 103 | } 104 | } 105 | 106 | gobject = (GObject *) g_object_newv (gtype, n_parameters, parameters); 107 | AssociateGObject (isolate, self, gobject); 108 | 109 | out: 110 | g_free (parameters); 111 | g_type_class_unref (klass); 112 | } 113 | } 114 | 115 | static G_DEFINE_QUARK(gnode_js_template, gnode_js_template); 116 | 117 | static void SignalConnectInternal(const FunctionCallbackInfo &args, bool after) { 118 | Isolate *isolate = args.GetIsolate (); 119 | GObject *gobject = GObjectFromWrapper (args.This ()); 120 | 121 | String::Utf8Value signal_name (args[0]->ToString ()); 122 | Local callback = Local::Cast (args[1]->ToObject ()); 123 | GClosure *gclosure = MakeClosure (isolate, callback); 124 | 125 | ulong handler_id = g_signal_connect_closure (gobject, *signal_name, gclosure, after); 126 | args.GetReturnValue ().Set(Integer::NewFromUnsigned (isolate, handler_id)); 127 | } 128 | 129 | static void SignalConnect(const FunctionCallbackInfo &args) { 130 | SignalConnectInternal (args, false); 131 | } 132 | 133 | static Local GetBaseClassTemplate(Isolate *isolate) { 134 | Local tpl = FunctionTemplate::New (isolate); 135 | Local proto = tpl->PrototypeTemplate (); 136 | proto->Set (String::NewFromUtf8 (isolate, "connect"), FunctionTemplate::New (isolate, SignalConnect)->GetFunction ()); 137 | return tpl; 138 | } 139 | 140 | static Local GetClassTemplateFromGI(Isolate *isolate, GIBaseInfo *info); 141 | 142 | static void ClassDestroyed(const WeakCallbackData &data) { 143 | GIBaseInfo *info = data.GetParameter (); 144 | GType gtype = g_registered_type_info_get_g_type ((GIRegisteredTypeInfo *) info); 145 | 146 | void *type_data = g_type_get_qdata (gtype, gnode_js_template_quark ()); 147 | assert (type_data != NULL); 148 | Persistent *persistent = (Persistent *) type_data; 149 | delete persistent; 150 | 151 | g_type_set_qdata (gtype, gnode_js_template_quark (), NULL); 152 | g_base_info_unref (info); 153 | } 154 | 155 | static Local GetClassTemplate(Isolate *isolate, GIBaseInfo *info, GType gtype) { 156 | void *data = g_type_get_qdata (gtype, gnode_js_template_quark ()); 157 | 158 | if (data) { 159 | Persistent *persistent = (Persistent *) data; 160 | Local tpl = Local::New (isolate, *persistent); 161 | return tpl; 162 | } else { 163 | Local tpl = FunctionTemplate::New (isolate, GObjectConstructor, External::New (isolate, info)); 164 | 165 | Persistent *persistent = new Persistent(isolate, tpl); 166 | persistent->SetWeak (g_base_info_ref (info), ClassDestroyed); 167 | g_type_set_qdata (gtype, gnode_js_template_quark (), persistent); 168 | 169 | const char *class_name = g_base_info_get_name (info); 170 | tpl->SetClassName (String::NewFromUtf8 (isolate, class_name)); 171 | 172 | tpl->InstanceTemplate ()->SetInternalFieldCount (1); 173 | 174 | GIObjectInfo *parent_info = g_object_info_get_parent (info); 175 | if (parent_info) { 176 | Local parent_tpl = GetClassTemplateFromGI (isolate, (GIBaseInfo *) parent_info); 177 | tpl->Inherit (parent_tpl); 178 | } else { 179 | tpl->Inherit (GetBaseClassTemplate (isolate)); 180 | } 181 | 182 | return tpl; 183 | } 184 | } 185 | 186 | static Local GetClassTemplateFromGI(Isolate *isolate, GIBaseInfo *info) { 187 | GType gtype = g_registered_type_info_get_g_type ((GIRegisteredTypeInfo *) info); 188 | return GetClassTemplate (isolate, info, gtype); 189 | } 190 | 191 | static Local GetClassTemplateFromGType(Isolate *isolate, GType gtype) { 192 | GIRepository *repo = g_irepository_get_default (); 193 | GIBaseInfo *info = g_irepository_find_by_gtype (repo, gtype); 194 | return GetClassTemplate (isolate, info, gtype); 195 | } 196 | 197 | Local MakeClass(Isolate *isolate, GIBaseInfo *info) { 198 | Local tpl = GetClassTemplateFromGI (isolate, info); 199 | return tpl->GetFunction (); 200 | } 201 | 202 | static void ObjectDestroyed(const WeakCallbackData &data) { 203 | GObject *gobject = data.GetParameter (); 204 | 205 | void *type_data = g_object_get_qdata (gobject, gnode_js_object_quark ()); 206 | assert (type_data != NULL); 207 | Persistent *persistent = (Persistent *) type_data; 208 | delete persistent; 209 | 210 | /* We're destroying the wrapper object, so make sure to clear out 211 | * the qdata that points back to us. */ 212 | g_object_set_qdata (gobject, gnode_js_object_quark (), NULL); 213 | 214 | g_object_unref (gobject); 215 | } 216 | 217 | static void ToggleNotify(gpointer user_data, GObject *gobject, gboolean toggle_down) { 218 | void *data = g_object_get_qdata (gobject, gnode_js_object_quark ()); 219 | assert (data != NULL); 220 | 221 | Persistent *persistent = (Persistent *) data; 222 | 223 | if (toggle_down) { 224 | /* We're dropping from 2 refs to 1 ref. We are the last holder. Make 225 | * sure that that our weak ref is installed. */ 226 | persistent->SetWeak (gobject, ObjectDestroyed); 227 | } else { 228 | /* We're going from 1 ref to 2 refs. We can't let our wrapper be 229 | * collected, so make sure that our reference is persistent */ 230 | persistent->ClearWeak (); 231 | } 232 | } 233 | 234 | Local WrapperFromGObject(Isolate *isolate, GObject *gobject) { 235 | void *data = g_object_get_qdata (gobject, gnode_js_object_quark ()); 236 | 237 | if (data) { 238 | /* Easy case: we already have an object. */ 239 | Persistent *persistent = (Persistent *) data; 240 | Local obj = Local::New (isolate, *persistent); 241 | return obj; 242 | } else { 243 | GType gtype = G_OBJECT_TYPE (gobject); 244 | 245 | Local tpl = GetClassTemplateFromGType (isolate, gtype); 246 | Local constructor = tpl->GetFunction (); 247 | 248 | Local gobject_external = External::New (isolate, gobject); 249 | Local args[] = { gobject_external }; 250 | Local obj = constructor->NewInstance (1, args); 251 | return obj; 252 | } 253 | } 254 | 255 | GObject * GObjectFromWrapper(Local value) { 256 | Local object = value->ToObject (); 257 | void *data = object->GetAlignedPointerFromInternalField (0); 258 | GObject *gobject = G_OBJECT (data); 259 | return gobject; 260 | } 261 | 262 | }; 263 | -------------------------------------------------------------------------------- /src/gobject.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace GNodeJS { 9 | 10 | v8::Local MakeClass(v8::Isolate *isolate, GIBaseInfo *info); 11 | 12 | v8::Local WrapperFromGObject(v8::Isolate *isolate, GObject *object); 13 | GObject * GObjectFromWrapper(v8::Local value); 14 | 15 | }; 16 | -------------------------------------------------------------------------------- /src/loop.cc: -------------------------------------------------------------------------------- 1 | 2 | #include "loop.h" 3 | 4 | #include 5 | #include 6 | 7 | /* Integration for the GLib main loop and uv's main loop */ 8 | 9 | /* The way that this works is that we take uv's loop and nest it inside GLib's 10 | * mainloop, since nesting GLib inside uv seems to be fairly impossible until 11 | * either uv allows external sources to drive prepare/check, or until GLib 12 | * exposes an epoll fd to wait on... */ 13 | 14 | namespace GNodeJS { 15 | 16 | struct uv_loop_source { 17 | GSource source; 18 | uv_loop_t *loop; 19 | }; 20 | 21 | static gboolean uv_loop_source_prepare (GSource *base, int *timeout) { 22 | struct uv_loop_source *source = (struct uv_loop_source *) base; 23 | uv_update_time (source->loop); 24 | 25 | bool loop_alive = uv_loop_alive (source->loop); 26 | 27 | /* If the loop is dead, we can simply sleep forever until a GTK+ source 28 | * (presumably) wakes us back up again. */ 29 | if (!loop_alive) 30 | return FALSE; 31 | 32 | /* Otherwise, check the timeout. If the timeout is 0, that means we're 33 | * ready to go. Otherwise, keep sleeping until the timeout happens again. */ 34 | int t = uv_backend_timeout (source->loop); 35 | *timeout = t; 36 | 37 | if (t == 0) 38 | return TRUE; 39 | else 40 | return FALSE; 41 | } 42 | 43 | static gboolean uv_loop_source_dispatch (GSource *base, GSourceFunc callback, gpointer user_data) { 44 | struct uv_loop_source *source = (struct uv_loop_source *) base; 45 | uv_run (source->loop, UV_RUN_NOWAIT); 46 | return G_SOURCE_CONTINUE; 47 | } 48 | 49 | static GSourceFuncs uv_loop_source_funcs = { 50 | uv_loop_source_prepare, 51 | NULL, 52 | uv_loop_source_dispatch, 53 | NULL, 54 | 55 | NULL, NULL, 56 | }; 57 | 58 | static GSource *uv_loop_source_new (uv_loop_t *loop) { 59 | struct uv_loop_source *source = (struct uv_loop_source *) g_source_new (&uv_loop_source_funcs, sizeof (*source)); 60 | source->loop = loop; 61 | g_source_add_unix_fd (&source->source, 62 | uv_backend_fd (loop), 63 | (GIOCondition) (G_IO_IN | G_IO_OUT | G_IO_ERR)); 64 | return &source->source; 65 | } 66 | 67 | void StartLoop() { 68 | GSource *source = uv_loop_source_new (uv_default_loop ()); 69 | g_source_attach (source, NULL); 70 | } 71 | 72 | }; 73 | -------------------------------------------------------------------------------- /src/loop.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | namespace GNodeJS { 5 | 6 | void StartLoop(); 7 | 8 | }; 9 | -------------------------------------------------------------------------------- /src/value.cc: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #include "value.h" 5 | #include "boxed.h" 6 | #include "gobject.h" 7 | 8 | using namespace v8; 9 | 10 | namespace GNodeJS { 11 | 12 | Local GIArgumentToV8(Isolate *isolate, GITypeInfo *type_info, GIArgument *arg) { 13 | GITypeTag type_tag = g_type_info_get_tag (type_info); 14 | 15 | switch (type_tag) { 16 | case GI_TYPE_TAG_VOID: 17 | return Undefined (isolate); 18 | 19 | case GI_TYPE_TAG_BOOLEAN: 20 | if (arg->v_boolean) 21 | return True (isolate); 22 | else 23 | return False (isolate); 24 | 25 | case GI_TYPE_TAG_INT32: 26 | return Integer::New (isolate, arg->v_int); 27 | case GI_TYPE_TAG_UINT32: 28 | return Integer::NewFromUnsigned (isolate, arg->v_uint); 29 | case GI_TYPE_TAG_INT16: 30 | return Integer::New (isolate, arg->v_int16); 31 | case GI_TYPE_TAG_UINT16: 32 | return Integer::NewFromUnsigned (isolate, arg->v_uint16); 33 | case GI_TYPE_TAG_INT8: 34 | return Integer::New (isolate, arg->v_int8); 35 | case GI_TYPE_TAG_UINT8: 36 | return Integer::NewFromUnsigned (isolate, arg->v_uint8); 37 | case GI_TYPE_TAG_FLOAT: 38 | return Number::New (isolate, arg->v_float); 39 | case GI_TYPE_TAG_DOUBLE: 40 | return Number::New (isolate, arg->v_double); 41 | 42 | /* For 64-bit integer types, use a float. When JS and V8 adopt 43 | * bigger sized integer types, start using those instead. */ 44 | case GI_TYPE_TAG_INT64: 45 | return Number::New (isolate, arg->v_int64); 46 | case GI_TYPE_TAG_UINT64: 47 | return Number::New (isolate, arg->v_uint64); 48 | 49 | case GI_TYPE_TAG_UNICHAR: 50 | { 51 | char data[7] = { 0 }; 52 | g_unichar_to_utf8 (arg->v_uint32, data); 53 | return String::NewFromUtf8 (isolate, data); 54 | } 55 | 56 | case GI_TYPE_TAG_UTF8: 57 | if (arg->v_pointer) 58 | return String::NewFromUtf8 (isolate, (char *) arg->v_pointer); 59 | else 60 | return Null (isolate); 61 | 62 | case GI_TYPE_TAG_INTERFACE: 63 | { 64 | GIBaseInfo *interface_info = g_type_info_get_interface (type_info); 65 | GIInfoType interface_type = g_base_info_get_type (interface_info); 66 | 67 | switch (interface_type) { 68 | case GI_INFO_TYPE_OBJECT: 69 | return WrapperFromGObject (isolate, (GObject *) arg->v_pointer); 70 | case GI_INFO_TYPE_BOXED: 71 | case GI_INFO_TYPE_STRUCT: 72 | return WrapperFromBoxed (isolate, interface_info, arg->v_pointer); 73 | case GI_INFO_TYPE_FLAGS: 74 | case GI_INFO_TYPE_ENUM: 75 | return Integer::New (isolate, arg->v_int); 76 | default: 77 | g_assert_not_reached (); 78 | } 79 | } 80 | break; 81 | 82 | default: 83 | g_assert_not_reached (); 84 | } 85 | } 86 | 87 | static GArray * V8ToGArray(Isolate *isolate, GITypeInfo *type_info, Local value) { 88 | if (!value->IsArray ()) { 89 | isolate->ThrowException (Exception::TypeError (String::NewFromUtf8 (isolate, "Not an array."))); 90 | return NULL; 91 | } 92 | 93 | Local array = Local::Cast (value->ToObject ()); 94 | GITypeInfo *elem_info = g_type_info_get_param_type (type_info, 0); 95 | 96 | int length = array->Length (); 97 | GArray *garray = g_array_sized_new (TRUE, FALSE, sizeof (GIArgument), length); 98 | for (int i = 0; i < length; i++) { 99 | Local value = array->Get (i); 100 | GIArgument arg; 101 | 102 | V8ToGIArgument (isolate, elem_info, &arg, value, false); 103 | g_array_append_val (garray, arg); 104 | } 105 | 106 | g_base_info_unref ((GIBaseInfo *) elem_info); 107 | return garray; 108 | } 109 | 110 | void V8ToGIArgument(Isolate *isolate, GIBaseInfo *base_info, GIArgument *arg, Local value) { 111 | GIInfoType type = g_base_info_get_type (base_info); 112 | 113 | switch (type) { 114 | case GI_INFO_TYPE_OBJECT: 115 | arg->v_pointer = GObjectFromWrapper (value); 116 | break; 117 | case GI_INFO_TYPE_BOXED: 118 | case GI_INFO_TYPE_STRUCT: 119 | arg->v_pointer = BoxedFromWrapper (value); 120 | break; 121 | case GI_INFO_TYPE_FLAGS: 122 | case GI_INFO_TYPE_ENUM: 123 | arg->v_int = value->Int32Value (); 124 | break; 125 | default: 126 | g_assert_not_reached (); 127 | } 128 | } 129 | 130 | void V8ToGIArgument(Isolate *isolate, GITypeInfo *type_info, GIArgument *arg, Local value, 131 | bool may_be_null, size_t *length_p) { 132 | GITypeTag type_tag = g_type_info_get_tag (type_info); 133 | 134 | if (value->IsNull ()) { 135 | arg->v_pointer = NULL; 136 | 137 | if (!may_be_null) 138 | isolate->ThrowException (Exception::TypeError (String::NewFromUtf8 (isolate, "Argument may not be null."))); 139 | 140 | return; 141 | } 142 | 143 | if (value->IsUndefined ()) { 144 | isolate->ThrowException (Exception::TypeError (String::NewFromUtf8 (isolate, "Argument may not be undefined."))); 145 | return; 146 | } 147 | 148 | switch (type_tag) { 149 | case GI_TYPE_TAG_VOID: 150 | arg->v_pointer = NULL; 151 | break; 152 | case GI_TYPE_TAG_BOOLEAN: 153 | arg->v_boolean = value->BooleanValue (); 154 | break; 155 | case GI_TYPE_TAG_INT32: 156 | arg->v_int = value->Int32Value (); 157 | break; 158 | case GI_TYPE_TAG_UINT32: 159 | arg->v_uint = value->Uint32Value (); 160 | break; 161 | case GI_TYPE_TAG_INT64: 162 | arg->v_int64 = value->NumberValue (); 163 | break; 164 | case GI_TYPE_TAG_UINT64: 165 | arg->v_uint64 = value->NumberValue (); 166 | break; 167 | case GI_TYPE_TAG_FLOAT: 168 | arg->v_float = value->NumberValue (); 169 | break; 170 | case GI_TYPE_TAG_DOUBLE: 171 | arg->v_double = value->NumberValue (); 172 | break; 173 | 174 | case GI_TYPE_TAG_FILENAME: 175 | { 176 | String::Utf8Value str (value); 177 | const char *utf8_data = *str; 178 | arg->v_pointer = g_filename_from_utf8 (utf8_data, -1, NULL, length_p, NULL); 179 | } 180 | break; 181 | 182 | case GI_TYPE_TAG_UTF8: 183 | { 184 | String::Utf8Value str (value); 185 | const char *data = *str; 186 | arg->v_pointer = g_strdup (data); 187 | if (length_p) 188 | *length_p = strlen (data); 189 | } 190 | break; 191 | 192 | case GI_TYPE_TAG_INTERFACE: 193 | { 194 | GIBaseInfo *interface_info = g_type_info_get_interface (type_info); 195 | V8ToGIArgument (isolate, interface_info, arg, value); 196 | g_base_info_unref (interface_info); 197 | } 198 | break; 199 | 200 | case GI_TYPE_TAG_ARRAY: 201 | { 202 | GIArrayType array_type = g_type_info_get_array_type (type_info); 203 | GArray *garray = V8ToGArray (isolate, type_info, value); 204 | 205 | if (length_p) 206 | *length_p = garray->len; 207 | 208 | switch (array_type) { 209 | case GI_ARRAY_TYPE_C: 210 | arg->v_pointer = g_array_free (garray, FALSE); 211 | break; 212 | case GI_ARRAY_TYPE_ARRAY: 213 | arg->v_pointer = garray; 214 | break; 215 | default: 216 | g_assert_not_reached (); 217 | } 218 | } 219 | break; 220 | 221 | default: 222 | g_assert_not_reached (); 223 | } 224 | } 225 | 226 | void FreeGIArgument(GITypeInfo *type_info, GIArgument *arg) { 227 | GITypeTag type_tag = g_type_info_get_tag (type_info); 228 | 229 | switch (type_tag) { 230 | case GI_TYPE_TAG_FILENAME: 231 | case GI_TYPE_TAG_UTF8: 232 | g_free (arg->v_pointer); 233 | break; 234 | 235 | case GI_TYPE_TAG_ARRAY: 236 | { 237 | GIArrayType array_type = g_type_info_get_array_type (type_info); 238 | 239 | switch (array_type) { 240 | case GI_ARRAY_TYPE_C: 241 | g_free (arg->v_pointer); 242 | break; 243 | case GI_ARRAY_TYPE_ARRAY: 244 | g_array_free ((GArray *) arg->v_pointer, TRUE); 245 | break; 246 | default: 247 | g_assert_not_reached (); 248 | } 249 | } 250 | break; 251 | default: 252 | break; 253 | } 254 | } 255 | 256 | void V8ToGValue(GValue *gvalue, Local value) { 257 | if (G_VALUE_HOLDS_BOOLEAN (gvalue)) { 258 | g_value_set_boolean (gvalue, value->BooleanValue ()); 259 | } else if (G_VALUE_HOLDS_INT (gvalue)) { 260 | g_value_set_int (gvalue, value->Int32Value ()); 261 | } else if (G_VALUE_HOLDS_UINT (gvalue)) { 262 | g_value_set_uint (gvalue, value->Uint32Value ()); 263 | } else if (G_VALUE_HOLDS_FLOAT (gvalue)) { 264 | g_value_set_float (gvalue, value->NumberValue ()); 265 | } else if (G_VALUE_HOLDS_DOUBLE (gvalue)) { 266 | g_value_set_double (gvalue, value->NumberValue ()); 267 | } else if (G_VALUE_HOLDS_STRING (gvalue)) { 268 | String::Utf8Value str (value); 269 | const char *data = *str; 270 | g_value_set_string (gvalue, data); 271 | } else if (G_VALUE_HOLDS_ENUM (gvalue)) { 272 | g_value_set_enum (gvalue, value->Int32Value ()); 273 | } else if (G_VALUE_HOLDS_OBJECT (gvalue)) { 274 | g_value_set_object (gvalue, GObjectFromWrapper (value)); 275 | } else { 276 | g_assert_not_reached (); 277 | } 278 | } 279 | 280 | Local GValueToV8(Isolate *isolate, const GValue *gvalue) { 281 | if (G_VALUE_HOLDS_BOOLEAN (gvalue)) { 282 | if (g_value_get_boolean (gvalue)) 283 | return True (isolate); 284 | else 285 | return False (isolate); 286 | } else if (G_VALUE_HOLDS_INT (gvalue)) { 287 | return Integer::New (isolate, g_value_get_int (gvalue)); 288 | } else if (G_VALUE_HOLDS_UINT (gvalue)) { 289 | return Integer::NewFromUnsigned (isolate, g_value_get_uint (gvalue)); 290 | } else if (G_VALUE_HOLDS_FLOAT (gvalue)) { 291 | return Number::New (isolate, g_value_get_float (gvalue)); 292 | } else if (G_VALUE_HOLDS_DOUBLE (gvalue)) { 293 | return Number::New (isolate, g_value_get_double (gvalue)); 294 | } else if (G_VALUE_HOLDS_STRING (gvalue)) { 295 | return String::NewFromUtf8 (isolate, g_value_get_string (gvalue)); 296 | } else if (G_VALUE_HOLDS_ENUM (gvalue)) { 297 | return Integer::New (isolate, g_value_get_enum (gvalue)); 298 | } else if (G_VALUE_HOLDS_OBJECT (gvalue)) { 299 | return WrapperFromGObject (isolate, G_OBJECT (g_value_get_object (gvalue))); 300 | } else { 301 | g_assert_not_reached (); 302 | } 303 | } 304 | 305 | }; 306 | -------------------------------------------------------------------------------- /src/value.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include 5 | #include 6 | 7 | namespace GNodeJS { 8 | 9 | v8::Local GIArgumentToV8(v8::Isolate *isolate, GITypeInfo *type_info, GIArgument *argument); 10 | void V8ToGIArgument(v8::Isolate *isolate, GIBaseInfo *base_info, GIArgument *arg, v8::Local value); 11 | void V8ToGIArgument(v8::Isolate *isolate, GITypeInfo *type_info, GIArgument *argument, v8::Local value, 12 | bool may_be_null, size_t *length_p = NULL); 13 | void FreeGIArgument(GITypeInfo *type_info, GIArgument *argument); 14 | 15 | void V8ToGValue(GValue *gvalue, v8::Local value); 16 | v8::Local GValueToV8(v8::Isolate *isolate, const GValue *gvalue); 17 | 18 | }; 19 | --------------------------------------------------------------------------------