From 136856c397ca0ca1fbfa99f03bfdf4346c239000 Mon Sep 17 00:00:00 2001 From: Niklas Mischkulnig Date: Sat, 28 Apr 2018 22:35:45 +0200 Subject: [PATCH 1/8] Use libui timer instead of own --- index.js | 40 +++++++++++++++++++++---------- src/Ui.cc | 11 +++++++++ src/arch/darwin/timer.mm | 40 ------------------------------- src/arch/unix/timer.cc | 39 ------------------------------ src/arch/win32/timer.cc | 52 ---------------------------------------- src/timer-common.cc | 27 --------------------- 6 files changed, 38 insertions(+), 171 deletions(-) mode change 100755 => 100644 index.js delete mode 100644 src/arch/darwin/timer.mm delete mode 100644 src/arch/unix/timer.cc delete mode 100644 src/arch/win32/timer.cc delete mode 100644 src/timer-common.cc diff --git a/index.js b/index.js old mode 100755 new mode 100644 index 481a6f8..ec09e24 --- a/index.js +++ b/index.js @@ -50,27 +50,41 @@ function startLoop() { asyncHook.enable(); setTimeoutNode = global.setTimeout; - - global.setTimeout = function(cb, t) { + global.setTimeout = function(cb, timeout) { + const timeoutHandle = {running: true}; const args = Array.prototype.slice.call(arguments, 2); - return binding.lib.setTimeout(function() { - cb.apply(null, args); - }, t); - }; + binding.lib.Ui.startTimer(timeout, function() { + if (timeoutHandle.running) { + cb.apply(null, args); + } + return false; + }); + return timeoutHandle; + } clearTimeoutNode = global.clearTimeout; - global.clearTimeout = binding.lib.clearTimeout; + global.clearTimeout = function(timeoutHandle) { + timeoutHandle.running = false; + } setIntervalNode = global.setInterval; - global.setInterval = function(cb, t) { + global.setInterval = function(cb, timeout) { + const timeoutHandle = {running: true}; const args = Array.prototype.slice.call(arguments, 2); - return binding.lib.setInterval(function() { - cb.apply(null, args); - }, t); - }; + binding.lib.Ui.startTimer(timeout, function() { + if (timeoutHandle.running) { + cb.apply(null, args); + return true; + } + return false; + }); + return timeoutHandle; + } clearIntervalNode = global.clearInterval; - global.clearInterval = binding.lib.clearInterval; + global.clearInterval = function(timeoutHandle) { + timeoutHandle.running = false; + } } // This is called when a new async handle diff --git a/src/Ui.cc b/src/Ui.cc index 5342ead..9563d57 100644 --- a/src/Ui.cc +++ b/src/Ui.cc @@ -10,6 +10,11 @@ static int onShouldQuit_cb(void *data) { return 0; } +static int uiTimer_cb(void *data) { + nbind::cbFunction *cb = (nbind::cbFunction *)data; + return cb->call(); +} + struct Ui { static void main() { uiMain(); @@ -40,6 +45,11 @@ struct Ui { uiFreeInitError(err); } } + + static void startTimer(int ms, nbind::cbFunction &cb) { + nbind::cbFunction *callbackJs = new nbind::cbFunction(cb); + uiTimer(ms, uiTimer_cb, callbackJs); + } }; NBIND_CLASS(Ui) { @@ -49,4 +59,5 @@ NBIND_CLASS(Ui) { method(mainStep); method(mainSteps); method(onShouldQuit); + method(startTimer); } diff --git a/src/arch/darwin/timer.mm b/src/arch/darwin/timer.mm deleted file mode 100644 index 43537b6..0000000 --- a/src/arch/darwin/timer.mm +++ /dev/null @@ -1,40 +0,0 @@ -#include "timer.h" -#include "nbind/api.h" -#import - -TimeoutHandle *setTimeout(nbind::cbFunction &cb, unsigned int timeout) { - nbind::cbFunction *callbackJs = new nbind::cbFunction(cb); - TimeoutHandle *timeoutHandle = new TimeoutHandle(callbackJs); - timeoutHandle->handle = - [NSTimer scheduledTimerWithTimeInterval:timeout / 1000.0 - repeats:NO - block:^(NSTimer *timer) { - CALL_JSCB(timeoutHandle); - timeoutHandle->destroy(); - }]; - return timeoutHandle; -} - -void clearTimeout(TimeoutHandle *timeoutHandle) { - NSTimer *handle = (NSTimer *)timeoutHandle->handle; - [handle invalidate]; - timeoutHandle->destroy(); -} - -TimeoutHandle *setInterval(nbind::cbFunction &cb, unsigned int timeout) { - nbind::cbFunction *callbackJs = new nbind::cbFunction(cb); - TimeoutHandle *timeoutHandle = new TimeoutHandle(callbackJs); - timeoutHandle->handle = - [NSTimer scheduledTimerWithTimeInterval:timeout / 1000.0 - repeats:YES - block:^(NSTimer *timer) { - CALL_JSCB(timeoutHandle); - }]; - return timeoutHandle; -} - -void clearInterval(TimeoutHandle *timeoutHandle) { - NSTimer *handle = (NSTimer *)timeoutHandle->handle; - [handle invalidate]; - timeoutHandle->destroy(); -} diff --git a/src/arch/unix/timer.cc b/src/arch/unix/timer.cc deleted file mode 100644 index 5b0347d..0000000 --- a/src/arch/unix/timer.cc +++ /dev/null @@ -1,39 +0,0 @@ -#include "timer.h" -#include "nbind/api.h" - -gboolean glib_timeout_cb(TimeoutHandle *timeoutHandle) { - CALL_JSCB(timeoutHandle); - timeoutHandle->destroy(); - return FALSE; -} - -gboolean glib_interval_cb(TimeoutHandle *timeoutHandle) { - CALL_JSCB(timeoutHandle); - return TRUE; -} - -TimeoutHandle *setTimeout(nbind::cbFunction &cb, unsigned int timeout) { - nbind::cbFunction *callbackJs = new nbind::cbFunction(cb); - TimeoutHandle *timeoutHandle = new TimeoutHandle(callbackJs); - timeoutHandle->handle = - g_timeout_add(timeout, (GSourceFunc)glib_timeout_cb, timeoutHandle); - return timeoutHandle; -} - -void clearTimeout(TimeoutHandle *timeoutHandle) { - g_source_remove(timeoutHandle->handle); - timeoutHandle->destroy(); -} - -TimeoutHandle *setInterval(nbind::cbFunction &cb, unsigned int timeout) { - nbind::cbFunction *callbackJs = new nbind::cbFunction(cb); - TimeoutHandle *timeoutHandle = new TimeoutHandle(callbackJs); - timeoutHandle->handle = - g_timeout_add(timeout, (GSourceFunc)glib_interval_cb, timeoutHandle); - return timeoutHandle; -} - -void clearInterval(TimeoutHandle *timeoutHandle) { - g_source_remove(timeoutHandle->handle); - timeoutHandle->destroy(); -} diff --git a/src/arch/win32/timer.cc b/src/arch/win32/timer.cc deleted file mode 100644 index af8fd90..0000000 --- a/src/arch/win32/timer.cc +++ /dev/null @@ -1,52 +0,0 @@ -#include -#include "nbind/api.h" -#include "timer.h" - -static std::map timersMap; - -void killTimer(TimeoutHandle *timeoutHandle) { - KillTimer(NULL, timeoutHandle->handle); - timersMap.erase(timeoutHandle->handle); - timeoutHandle->destroy(); -} - -void CALLBACK win_timeout_cb(HWND hwnd, UINT uMsg, UINT_PTR idEvent, - DWORD dwTime) { - TimeoutHandle *timeoutHandle = timersMap[idEvent]; - CALL_JSCB(timeoutHandle); - killTimer(timeoutHandle); -} - -void CALLBACK win_interval_cb(HWND hwnd, UINT uMsg, UINT_PTR idEvent, - DWORD dwTime) { - TimeoutHandle *timeoutHandle = timersMap[idEvent]; - CALL_JSCB(timeoutHandle); -} - -TimeoutHandle *setTimeout(nbind::cbFunction &cb, unsigned int timeout) { - nbind::cbFunction *callbackJs = new nbind::cbFunction(cb); - TimeoutHandle *timeoutHandle = new TimeoutHandle(callbackJs); - // SetTimer could work with a NULL window. - // https://stackoverflow.com/questions/7531650/can-i-use-a-settimer-api-in-a-console-c-application - timeoutHandle->handle = SetTimer(NULL, NULL, timeout, win_timeout_cb); - timersMap[timeoutHandle->handle] = timeoutHandle; - return timeoutHandle; -} - -void clearTimeout(TimeoutHandle *timeoutHandle) { - if (!timeoutHandle->destroyed) { - killTimer(timeoutHandle); - } -} - -TimeoutHandle *setInterval(nbind::cbFunction &cb, unsigned int timeout) { - nbind::cbFunction *callbackJs = new nbind::cbFunction(cb); - TimeoutHandle *timeoutHandle = new TimeoutHandle(callbackJs); - timeoutHandle->handle = SetTimer(NULL, NULL, timeout, win_interval_cb); - timersMap[timeoutHandle->handle] = timeoutHandle; - return timeoutHandle; -} - -void clearInterval(TimeoutHandle *timeoutHandle) { - killTimer(timeoutHandle); -} diff --git a/src/timer-common.cc b/src/timer-common.cc deleted file mode 100644 index 1bf5954..0000000 --- a/src/timer-common.cc +++ /dev/null @@ -1,27 +0,0 @@ -#include "nbind/api.h" -#include "timer.h" - -TimeoutHandle::TimeoutHandle(nbind::cbFunction *callbackJs) { - this->callbackJs = callbackJs; - this->destroyed = false; -} - -void TimeoutHandle::destroy() { - if (this->destroyed) { - return; - } - - delete this->callbackJs; - this->destroyed = true; -} - -#include "nbind/nbind.h" - -NBIND_GLOBAL() { - function(setTimeout); - function(clearTimeout); - function(setInterval); - function(clearInterval); -} - -NBIND_CLASS(TimeoutHandle) {} From 7dc80b4033d9b678ff0ff66f6bcc393f32c04e99 Mon Sep 17 00:00:00 2001 From: Andrea Parodi Date: Thu, 3 May 2018 20:35:07 +0200 Subject: [PATCH 2/8] Bump version to 0.3.0-rc1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c421ee1..5fdda57 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "libui-node", - "version": "0.2.0-rc1", + "version": "0.3.0-rc1", "description": "Node.js bindings for libui", "repository": "parro-it/libui-node", "license": "MIT", From 36027e8ec376f1eca5f50cde2f33bd3743e39663 Mon Sep 17 00:00:00 2001 From: Andrea Parodi Date: Thu, 3 May 2018 21:07:02 +0200 Subject: [PATCH 3/8] Add log to solve setTimeout problem --- examples/event-loop.js | 5 +++++ index.js | 8 +++++--- package-lock.json | 2 +- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/examples/event-loop.js b/examples/event-loop.js index 9494853..0271c88 100644 --- a/examples/event-loop.js +++ b/examples/event-loop.js @@ -116,7 +116,10 @@ function makeToolbar() { let timeoutHandle = null; btnCustom.onClicked(() => { if (timeoutHandle) { + console.log('before clearTimeout', timeoutHandle); clearTimeout(timeoutHandle); + console.log('after clearTimeout', timeoutHandle); + timeoutHandle = null; return; } @@ -126,6 +129,8 @@ function makeToolbar() { const elapsed = Date.now() - now; logAppend(`Custom setTimeout: ${now} - elapsed ${elapsed} ms. Args: ${a} ${b} ${c}`); }, 1000, 'custom', 'args', 2); + console.log('after timeoutHandle', timeoutHandle); + }); toolbar.append(btnCustom, false); diff --git a/index.js b/index.js index 01bc43d..5ac2e0a 100644 --- a/index.js +++ b/index.js @@ -55,6 +55,7 @@ function startLoop() { const args = Array.prototype.slice.call(arguments, 2); binding.lib.Ui.startTimer(timeout, function() { + console.log('timeoutHandle.running ', timeoutHandle.running); if (timeoutHandle.running) { cb.apply(null, args); } @@ -68,8 +69,9 @@ function startLoop() { global.clearTimeout = function(timeoutHandle) { if (timeoutHandle && typeof timeoutHandle.running === 'boolean') { timeoutHandle.running = false; + console.log('patched clearTimeout called'); } else { - // console.log('node clearTimeout called'); + console.log('node clearTimeout called'); // not created by us, use original // clearTimeoutNode clearTimeoutNode(timeoutHandle); @@ -95,9 +97,9 @@ function startLoop() { global.clearInterval = function(timeoutHandle) { if (timeoutHandle && typeof timeoutHandle.running === 'boolean') { timeoutHandle.running = false; - // console.log('patched clearInterval called'); + console.log('patched clearInterval called'); } else { - // console.log('node clearInterval called'); + console.log('node clearInterval called'); // not created by us, use original // clearTimeoutNode clearIntervalNode(timeoutHandle); diff --git a/package-lock.json b/package-lock.json index 75a7eaf..00207eb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "libui-node", - "version": "0.1.0", + "version": "0.3.0-rc1", "lockfileVersion": 1, "requires": true, "dependencies": { From c648a6209ba0175c7222365f84ca809b2da73d88 Mon Sep 17 00:00:00 2001 From: Niklas Mischkulnig Date: Thu, 3 May 2018 23:20:00 +0200 Subject: [PATCH 4/8] Fix eventloop example --- examples/event-loop.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/event-loop.js b/examples/event-loop.js index 0271c88..b9e9712 100644 --- a/examples/event-loop.js +++ b/examples/event-loop.js @@ -128,9 +128,9 @@ function makeToolbar() { timeoutHandle = setTimeout((a, b, c) => { const elapsed = Date.now() - now; logAppend(`Custom setTimeout: ${now} - elapsed ${elapsed} ms. Args: ${a} ${b} ${c}`); + timeoutHandle = null; }, 1000, 'custom', 'args', 2); console.log('after timeoutHandle', timeoutHandle); - }); toolbar.append(btnCustom, false); From 28cbaf6e9c9134ddcbb46a0ca7c0597330b2ae65 Mon Sep 17 00:00:00 2001 From: Andrea Parodi Date: Thu, 3 May 2018 23:26:55 +0200 Subject: [PATCH 5/8] Removed log --- index.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/index.js b/index.js index 5ac2e0a..db57153 100644 --- a/index.js +++ b/index.js @@ -55,7 +55,7 @@ function startLoop() { const args = Array.prototype.slice.call(arguments, 2); binding.lib.Ui.startTimer(timeout, function() { - console.log('timeoutHandle.running ', timeoutHandle.running); + // console.log('timeoutHandle.running ', timeoutHandle.running); if (timeoutHandle.running) { cb.apply(null, args); } @@ -69,9 +69,9 @@ function startLoop() { global.clearTimeout = function(timeoutHandle) { if (timeoutHandle && typeof timeoutHandle.running === 'boolean') { timeoutHandle.running = false; - console.log('patched clearTimeout called'); + // console.log('patched clearTimeout called'); } else { - console.log('node clearTimeout called'); + // console.log('node clearTimeout called'); // not created by us, use original // clearTimeoutNode clearTimeoutNode(timeoutHandle); @@ -97,9 +97,9 @@ function startLoop() { global.clearInterval = function(timeoutHandle) { if (timeoutHandle && typeof timeoutHandle.running === 'boolean') { timeoutHandle.running = false; - console.log('patched clearInterval called'); + // console.log('patched clearInterval called'); } else { - console.log('node clearInterval called'); + // console.log('node clearInterval called'); // not created by us, use original // clearTimeoutNode clearIntervalNode(timeoutHandle); From 9610f0c41609061ee8801e76561cb9b5e369fef1 Mon Sep 17 00:00:00 2001 From: Andrea Parodi Date: Fri, 4 May 2018 19:46:52 +0200 Subject: [PATCH 6/8] Added the same example used in libui-napi --- examples/example-from-napi.js | 85 +++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 examples/example-from-napi.js diff --git a/examples/example-from-napi.js b/examples/example-from-napi.js new file mode 100644 index 0000000..a34e7fc --- /dev/null +++ b/examples/example-from-napi.js @@ -0,0 +1,85 @@ +const libui = require('..'); + +const {UiWindow, UiHorizontalBox, UiMultilineEntry} = libui; + +libui.onShouldQuit(() => { + libui.stopLoop(); + global.gc(); +}); + +function createWindow() { + let win = new UiWindow('Test Window', 800, 600, false); + win.margined = true; + const logEntry = new UiMultilineEntry(); + + const entry = new UiMultilineEntry(); + entry.text = 'A test line\n'; + entry.append('A second test line\n'); + entry.onChanged(() => { + const msg = `Text changed to ${entry.text}`; + console.log(msg); + logEntry.append(msg + '\n'); + }); + + const box = new UiHorizontalBox(); + box.setPadded(true); + box.append(entry, true); + box.append(logEntry, true); + + win.setChild(box); + + win.onContentSizeChanged(() => { + const size = win.getContentSize(); + console.log(`size changed to ${size.width}x${size.height}`); + }); + let step = 0; + win.onClosing(() => { + if (win.getTitle() == 'Test Window') { + let interval = setInterval(() => { + if (step === 0) { + win.contentSize = {width: 400, height: 300}; + } + if (step === 1) { + win.margined = true; + } + if (step === 2) { + win.margined = false; + win.fullscreen = true; + } + if (step === 3) { + win.fullscreen = false; + win.borderless = true; + } + if (step === 4) { + win.borderless = false; + } + + if (step > 4) { + clearInterval(interval); + } + + step++; + + console.log({ + Margined: win.margined, + Fullscreen: win.fullscreen, + Borderless: win.borderless, + }); + }, 1000); + box.deleteAt(1); + return win.title = 'Wait some seconds please...'; + } + console.log('closing', win.title); + win.close(); + win = null; + libui.stopLoop(); + }); + + win.show(); +} + +createWindow(); +libui.startLoop(); +setInterval(() => { + global.gc(); +}, 10); From 82b060d4f4321851a8edfc3f3b5ed7a22949ba1b Mon Sep 17 00:00:00 2001 From: Niklas Mischkulnig Date: Fri, 1 Jun 2018 22:49:01 +0200 Subject: [PATCH 7/8] Fix file dialogs cancel crash --- index.js | 18 ++++++++++++++++++ src/UiWindow.cc | 28 ++++++++++++++++++---------- 2 files changed, 36 insertions(+), 10 deletions(-) diff --git a/index.js b/index.js index db57153..a8a4a04 100644 --- a/index.js +++ b/index.js @@ -359,6 +359,24 @@ binding.lib.FontAttribute.prototype.getOTFeatures = function() { return this.getOTFeaturesInternal(); }; +binding.lib.UiDialogs.openFile = function(parent) { + const v = binding.lib.UiDialogs.openFileInternal(parent); + if (v) { + return v; + } else { + return null; + } +}; + +binding.lib.UiDialogs.saveFile = function(parent) { + const v = binding.lib.UiDialogs.saveFileInternal(parent); + if (v) { + return v; + } else { + return null; + } +}; + const brushType = { solid: 0, linearGradient: 1, diff --git a/src/UiWindow.cc b/src/UiWindow.cc index d6ed484..deaf91a 100644 --- a/src/UiWindow.cc +++ b/src/UiWindow.cc @@ -163,18 +163,26 @@ NBIND_CLASS(UiWindow) { struct UiDialogs { - static std::string openFile(UiWindow *parent) { + static std::string openFileInternal(UiWindow *parent) { char *char_ptr = uiOpenFile(parent->getHandle()); - std::string s(char_ptr); - uiFreeText(char_ptr); - return s; + if (char_ptr == NULL) { + return std::string(""); + } else { + std::string s(char_ptr); + uiFreeText(char_ptr); + return s; + } } - static std::string saveFile(UiWindow *parent) { + static std::string saveFileInternal(UiWindow *parent) { char *char_ptr = uiSaveFile(parent->getHandle()); - std::string s(char_ptr); - uiFreeText(char_ptr); - return s; + if (char_ptr == NULL) { + return std::string(""); + } else { + std::string s(char_ptr); + uiFreeText(char_ptr); + return s; + } } static void msgBox(UiWindow *parent, std::string title, @@ -189,8 +197,8 @@ struct UiDialogs { }; NBIND_CLASS(UiDialogs) { - method(openFile); - method(saveFile); + method(openFileInternal); + method(saveFileInternal); method(msgBox); method(msgBoxError); } From 053769f969e00cc93f5f023e8117b81342f75392 Mon Sep 17 00:00:00 2001 From: Niklas Mischkulnig Date: Tue, 19 Jun 2018 11:23:29 +0200 Subject: [PATCH 8/8] textarea EOL is fixed upstream --- examples/event-loop.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/event-loop.js b/examples/event-loop.js index b9e9712..f9202fe 100644 --- a/examples/event-loop.js +++ b/examples/event-loop.js @@ -58,9 +58,9 @@ libui.startLoop(); function logAppend(line) { const lines = log.text.split('\n'); if (lines.length > 25) { - log.text = lines.slice(1).join(os.EOL); + log.text = lines.slice(1).join("\n"); } - log.append(line + os.EOL); + log.append(line + "\n"); } function setIntervalChanged() {