diff --git a/.gitignore b/.gitignore index b82470b..8e2822c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ build/ node_modules/ -npm-debug.log .DS_Store +generated.h -TODO.md +*.log +spec.js diff --git a/.npmignore b/.npmignore index f50596f..a7a73d1 100644 --- a/.npmignore +++ b/.npmignore @@ -1,10 +1,10 @@ build/ node_modules/ -npm-debug.log .DS_Store +generated.h -TODO.md - +*.log +spec.js .gitignore .travis.yml appveyor.yml diff --git a/.travis.yml b/.travis.yml index e3445c9..ab30b72 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,12 +2,12 @@ language: node_js os: - linux - osx +env: TTYU_NCURSES_REBUILD=true TTYU_BUILD_DEBUG=true TTYU_CODE_DEBUG=true node_js: - - "0.8" - "0.10" - "0.12" - - "iojs-v1.6.4" + - "iojs-v2.3.0" install: - - npm install + - npm install --verbose script: - npm test diff --git a/CHANGELOG.md b/CHANGELOG.md index e115f13..69069b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,13 +6,11 @@ - fullscreen detection - improve examples - add pixel rendering (e.g. for pngs) functions - - rework goto/write functionality - add X11 implementations to remove curses where possible + - add 256 color support to windows (using pixel rendering) ## 0.2.0 (WIP) - - added `.pause()` function - - renamed `.stop()` to `.destroy()` - all functions will throw now when the object was not started or already destroyed - added `.emit()` function @@ -21,10 +19,6 @@ - added `.type` property to event objects - iojs support -### TODO - - - fix segfault on unix - ## 0.1.2 - fixed infinite loop in helper method `util_rgb2term()` diff --git a/appveyor.yml b/appveyor.yml index 8e42596..fd6ffbf 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -4,9 +4,10 @@ os: Windows Server 2012 R2 environment: matrix: # node.js - # - nodejs_version: "0.8" mocha does not work on windows with v0.8 - nodejs_version: "0.10" - nodejs_version: "0.12" + TTYU_NCURSES_REBUILD: true # to make sure ncurses does not install + TTYU_BUILD_DEBUG: true platform: - x86 diff --git a/binding.gyp b/binding.gyp old mode 100755 new mode 100644 index 09f0ccd..040bc09 --- a/binding.gyp +++ b/binding.gyp @@ -1,37 +1,72 @@ { - "targets": [ + "targets": [ + { + "target_name": "ttyu", + "include_dirs" : [ + "(\"" + v + "\")"; - } else { - return v; - } -} - -// TODO node-gyp rebuild diff --git a/deps/.gitkeep b/deps/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/deps/ee/ee.c b/deps/ee/ee.c deleted file mode 100644 index 3cd3d33..0000000 --- a/deps/ee/ee.c +++ /dev/null @@ -1,199 +0,0 @@ -/* ee - eventemitter for c - ee.c - * https://github.com/clidejs/ee.c - * - * Copyright Bernhard Bücherl - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#ifdef __cplusplus -extern "C" { -#endif - -// header file -#include "ee.h" - -// for malloc, free -#include - -// initialize empty emitter -void ee_init(ee_emitter_t *emitter, - int (*emit)(ee__listener_t *, EE_DATA_TYPE), - int (*compare)(EE_CB_ARG(cb1), EE_CB_ARG(cb2))) { - emitter->root = 0; - emitter->emit = emit; - emitter->compare = compare; -} - -void ee_on(ee_emitter_t *emitter, int event, EE_CB_ARG(cb)) { - ee__event_t *ev; - if(emitter->root == 0) { - ev = ee__event_new(event); - emitter->root = ev; - } else if(emitter->root->id == event) { - ev = emitter->root; - } else { - ev = ee__event_find(emitter->root, event); - } - if(ev->root == 0) { - ev->root = ee__listener_new(cb); - } else { - ee__listener_add(ev->root, cb); - } -} - -void ee_off(ee_emitter_t *emitter, int event, EE_CB_ARG(cb)) { - ee__event_t *ev; - if(emitter->root == 0) { - return; - } else if(emitter->root->id == event) { - ev = emitter->root; - } else { - ev = ee__event_find(emitter->root, event); - } - if(ev->root == 0) { - return; - } else if((emitter->compare && emitter->compare(ev->root->cb, cb)) || - ev->root->cb == cb) { - ee__listener_t *old = ev->root; - ev->root = old->next; - ee__listener_destroy(old); - } else { - ee__listener_remove(emitter, ev->root, cb); - } -} - -int ee_emit(ee_emitter_t *emitter, int event, EE_DATA_ARG(data)) { - ee__listener_t *l; - int count = 0; - if(emitter->root == 0) { - return count; - } else if(emitter->root->id == event) { - l = emitter->root->root; - } else { - l = ee__event_find(emitter->root, event)->root; - } - if(l != 0) { - if(emitter->emit) { - return emitter->emit(l, data); - } else { - do { - if(l->cb) { - l->cb(data); - } - ++count; - } while((l = l->next)); - } - } - return count; -} - -int ee_count(ee_emitter_t *emitter, int event) { - ee__listener_t *l; - int count = 0; - if(emitter->root == 0) { - return count; - } else if(emitter->root->id == event) { - l = emitter->root->root; - } else { - l = ee__event_find(emitter->root, event)->root; - } - if(l != 0) { - do { - ++count; - } while((l = l->next)); - } - return count; -} - -void ee_destroy(ee_emitter_t *emitter) { - ee__event_t *tmp; - while((tmp = emitter->root)) { - emitter->root = tmp->next; - ee__event_destroy(tmp); - } -} - -ee__listener_t *ee__listener_new(EE_CB_ARG(cb)) { - ee__listener_t *new_listener = - (ee__listener_t *)malloc(sizeof(ee__listener_t)); - new_listener->next = 0; - new_listener->cb = cb; - return new_listener; -} - -ee__listener_t *ee__listener_add(ee__listener_t *listener, EE_CB_ARG(cb)) { - if(listener->next == 0) { - return (listener->next = ee__listener_new(cb)); - } else { - return ee__listener_add(listener->next, cb); - } -} - -void ee__listener_remove(ee_emitter_t *emitter, ee__listener_t *listener, - EE_CB_ARG(cb)) { - if(listener->next != 0) { - if((emitter->compare && emitter->compare(listener->next->cb, cb)) || - listener->next->cb == cb) { - ee__listener_t *old = listener->next; - listener->next = old->next; - ee__listener_destroy(old); - } else { - ee__listener_remove(emitter, listener->next, cb); - } - } -} - -void ee__listener_destroy(ee__listener_t *listener) { - listener->next = 0; - listener->cb = 0; - free(listener); -} - -ee__event_t *ee__event_new(int event) { - ee__event_t *new_event = (ee__event_t *)malloc(sizeof(ee__event_t)); - new_event->id = event; - new_event->next = 0; - new_event->root = 0; - return new_event; -} - -ee__event_t *ee__event_find(ee__event_t *element, int event) { - if(element->next == 0) { - return (element->next = ee__event_new(event)); - } else if(element->next->id == event) { - return element->next; - } else { - return ee__event_find(element->next, event); - } -} - -void ee__event_destroy(ee__event_t *element) { - ee__listener_t *tmp; - element->next = 0; - while((tmp = element->root)) { - element->root = tmp->next; - ee__listener_destroy(tmp); - } - free(element); -} - -#ifdef __cplusplus -} -#endif diff --git a/deps/ee/ee.h b/deps/ee/ee.h deleted file mode 100644 index 642cd8a..0000000 --- a/deps/ee/ee.h +++ /dev/null @@ -1,92 +0,0 @@ -/* ee - eventemitter for c - ee.h - * https://github.com/clidejs/ee.c - * - * Copyright Bernhard Bücherl - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#ifndef EE_H_ -#define EE_H_ - -#ifdef __cplusplus -extern "C" { -#endif - -#ifndef EE_DATA_TYPE -#define EE_DATA_TYPE void * -#endif - -#ifndef EE_DATA_ARG -#define EE_DATA_ARG(name) EE_DATA_TYPE name -#endif - -#ifndef EE_CB_TYPE -#define EE_CB_TYPE void(*) (EE_DATA_TYPE) -#endif - -#ifndef EE_CB_ARG -#define EE_CB_ARG(name) void(*name)(EE_DATA_TYPE) -#endif - -typedef struct ee__event_s ee__event_t; -typedef struct ee__listener_s ee__listener_t; -typedef struct ee_emitter_s ee_emitter_t; - -struct ee__event_s { - int id; // event identifier - ee__event_t *next; - ee__listener_t *root; -}; - -struct ee__listener_s { - EE_CB_ARG(cb); - ee__listener_t *next; -}; - -struct ee_emitter_s { - ee__event_t *root; - int (*emit)(ee__listener_t *, EE_DATA_TYPE); - int (*compare)(EE_CB_TYPE, EE_CB_TYPE); -}; - -void ee_init(ee_emitter_t *emitter, - int (*emit)(ee__listener_t *, EE_DATA_TYPE), - int (*compare)(EE_CB_TYPE, EE_CB_TYPE)); -void ee_on(ee_emitter_t *emitter, int event, EE_CB_ARG(cb)); -void ee_off(ee_emitter_t *emitter, int event, EE_CB_ARG(cb)); -int ee_emit(ee_emitter_t *emitter, int event, EE_DATA_ARG(data)); -int ee_count(ee_emitter_t *emitter, int event); -void ee_destroy(ee_emitter_t *emitter); - -ee__listener_t *ee__listener_new(EE_CB_ARG(cb)); -ee__listener_t *ee__listener_add(ee__listener_t *listener, EE_CB_ARG(cb)); -void ee__listener_remove(ee_emitter_t *emitter, ee__listener_t *listener, - EE_CB_ARG(cb)); -void ee__listener_destroy(ee__listener_t *listener); - -ee__event_t *ee__event_new(int event); -ee__event_t *ee__event_find(ee__event_t *element, int event); -void ee__event_destroy(ee__event_t *element); - -#ifdef __cplusplus -} -#endif - -#endif // EE_H_ diff --git a/examples/colored-listener.js b/examples/colored-listener.js new file mode 100644 index 0000000..ceae874 --- /dev/null +++ b/examples/colored-listener.js @@ -0,0 +1,17 @@ +var ttyu = require("../index"); + +ttyu.start().bold().italic().underline() + .on(ttyu.EVENT.KEY, out) + .on(ttyu.EVENT.MOUSEDOWN, out); + +function col() { + return Math.round(Math.random()*255); +} + +function out(ev) { + ttyu.fg(col()).bg(col()) + .write(JSON.stringify(ev) + "\r\n"); + if(ev.which == ttyu.WHICH.CHARX) { + ttyu.stop(); + } +}; diff --git a/examples/emit.js b/examples/emit.js deleted file mode 100644 index d4bb3b4..0000000 --- a/examples/emit.js +++ /dev/null @@ -1,13 +0,0 @@ -var TTYUtil = require("../index"); - -var ttyu = new TTYUtil(); - -ttyu.on(TTYUtil.EVENT.KEY, function(ev) { - ttyu.write(JSON.stringify(ev) + "\r\n"); -}); - -ttyu.start(); - -setTimeout(function() { - ttyu.emit(TTYUtil.EVENT.KEY, 65, 4); -}, 10); diff --git a/examples/input.js b/examples/input.js deleted file mode 100644 index ef8eee2..0000000 --- a/examples/input.js +++ /dev/null @@ -1,26 +0,0 @@ -var TTYUtil = require("../index"); - -var ttyu = new TTYUtil(); - -for(var event in TTYUtil.EVENT) { - ttyu.on(TTYUtil.EVENT[event], listener(TTYUtil.EVENT[event])); -} - -var l = function(ev) { - ttyu.write("asdf"); -} - -ttyu.on(TTYUtil.EVENT.KEY, l); - -function listener(name) { - return function(ev) { - ttyu.write(name + ": " + JSON.stringify(ev) + "\r\n", name === "error" ? - "#F00" : "#0F0", name === "error" ? "#400" : "#040"); - if(name === "signal") { - ttyu.destroy(); - } - }; -} - -ttyu.start(); -ttyu.off(TTYUtil.EVENT.KEY, l); diff --git a/examples/output.js b/examples/output.js deleted file mode 100644 index 2c3e0d5..0000000 --- a/examples/output.js +++ /dev/null @@ -1,7 +0,0 @@ -var TTYUtil = require("../index"); - -var ttyu = new TTYUtil(); - -ttyu.start(); - -ttyu.write("Hello World", "#F00", "#365cff"); diff --git a/export.js b/export.js deleted file mode 100644 index 9dd8134..0000000 --- a/export.js +++ /dev/null @@ -1,81 +0,0 @@ -var Const = require("./const"); -var signal = require("./signal"); - -module.exports = function(ttyu) { - ttyu.TTYUtil.prototype.on = function(ev, listener) { - if(ev === ttyu.TTYUtil.EVENT.SIGNAL) { - signal.on(ttyu, this, listener); - } else if(ev in Const.Event && listener instanceof Function) { - this.__on__(Const.Event[ev], listener); - } - return this; - }; - ttyu.TTYUtil.prototype.off = - ttyu.TTYUtil.prototype.removeListener = function(ev, listener) { - if(ev === ttyu.TTYUtil.EVENT.SIGNAL) { - signal.off(this, listener); - } else if(ev in Const.Event && listener instanceof Function) { - this.__off__(Const.Event[ev], listener); - } - return this; - }; - ttyu.TTYUtil.prototype.emit = function(ev) { - switch(ev.type) { - case ttyu.TTYUtil.EVENT.SIGNAL: - signal.emit(ev.signal); - break; - case ttyu.TTYUtil.EVENT.KEY: - this.__emit__(Const.Event[ev.type], ev.which, ev.ctrl); - break; - case ttyu.TTYUtil.EVENT.MOUSEDOWN: - case ttyu.TTYUtil.EVENT.MOUSEUP: - case ttyu.TTYUtil.EVENT.MOUSEMOVE: - case ttyu.TTYUtil.EVENT.MOUSEWHEEL: - case ttyu.TTYUtil.EVENT.MOUSEHWHEEL: - this.__emit__(Const.Event[ev.type], ev.button, ev.x, ev.y, - ev.ctrl); - break; - default: - // ERROR, RESIZE - break; - } - }; - - ttyu.TTYUtil.SIGNAL = { - SIGINT: "SIGINT", - SIGTERM: "SIGTERM", - SIGPIPE: "SIGPIPE", - SIGHUP: "SIGHUP" - }; - ttyu.TTYUtil.EVENT = Const.EventString; - ttyu.TTYUtil.MOUSE = Const.Mouse; - ttyu.TTYUtil.WHICH = Const.Which; - ttyu.TTYUtil.CTRL = Const.Ctrl; - ttyu.TTYUtil.MODE = Const.Mode; - - // event constructors - ttyu.TTYUtil.KeyEvent = function(which, ctrl) { - return { - type: ttyu.TTYUtil.EVENT.KEY, - which: which, - ctrl: ctrl - }; - }; - ttyu.TTYUtil.MouseEvent = function(type, button, x, y, ctrl) { - return { - type: type, - button: button, - x: x, - y: y, - ctrl: ctrl - }; - }; - ttyu.TTYUtil.SignalEvent = function(signal) { - return { - type: ttyu.TTYUtil.EVENT.SIGNAL, - signal: signal - }; - }; - - return ttyu.TTYUtil; -}; diff --git a/include/cdebug.h b/include/cdebug.h new file mode 100644 index 0000000..c2d5527 --- /dev/null +++ b/include/cdebug.h @@ -0,0 +1,47 @@ +/* ttyutil - cdebug.h - header file defining debugging macros + * https://github.com/clidejs/ttyutil + * + * Copyright Bernhard Bücherl + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#ifndef INCLUDE_CDEBUG_H_ +#define INCLUDE_CDEBUG_H_ +#include +#include + +#ifdef CDEBUG +# ifdef CDEBUG_FILE +// TODO(@bbuecherl) write debugging log +# else // ifndef CDEBUG_FILE +#define DBG(msg) printf("%s\r\n", msg) +#define SDBG(f, ...) do { \ + std::string str = f; \ + str.append("\r\n"); \ + printf(str.c_str(), __VA_ARGS__); \ +} while (0) + +# endif // CDEBUG_FILE +#else // ifndef CDEBUG +#define DBG(msg) +#define SDBG(f, ...) + +#endif // CDEBUG + +#endif // INCLUDE_CDEBUG_H_ diff --git a/include/generated.h b/include/generated.h deleted file mode 100644 index 065e5b1..0000000 --- a/include/generated.h +++ /dev/null @@ -1,267 +0,0 @@ -/** - * do not change, this file is autogenerated by 'node build', - * these constants are set in ./const.js - * Build: 0.2.0.1428403644944 - */ -#ifndef TTYU_GENERATED_H_ -#define TTYU_GENERATED_H_ - -#define EVENT_ERROR 0 -#define EVENT_SIGNAL 1 -#define EVENT_RESIZE 2 -#define EVENT_KEY 3 -#define EVENT_MOUSEUP 4 -#define EVENT_MOUSEDOWN 5 -#define EVENT_MOUSEMOVE 6 -#define EVENT_MOUSEWHEEL 7 -#define EVENT_MOUSEHWHEEL 8 - -#define EVENTSTRING_ERROR NanNew("ERROR") -#define EVENTSTRING_SIGNAL NanNew("SIGNAL") -#define EVENTSTRING_RESIZE NanNew("RESIZE") -#define EVENTSTRING_KEY NanNew("KEY") -#define EVENTSTRING_MOUSEUP NanNew("MOUSEUP") -#define EVENTSTRING_MOUSEDOWN NanNew("MOUSEDOWN") -#define EVENTSTRING_MOUSEMOVE NanNew("MOUSEMOVE") -#define EVENTSTRING_MOUSEWHEEL NanNew("MOUSEWHEEL") -#define EVENTSTRING_MOUSEHWHEEL NanNew("MOUSEHWHEEL") - -#define MOUSE_LEFT 1 -#define MOUSE_LEFT2 4 -#define MOUSE_LEFT3 8 -#define MOUSE_LEFT4 16 -#define MOUSE_RIGHT 2 - -#define WHICH_UNKNOWN -1 -#define WHICH_BACK 8 -#define WHICH_BACKSPACE 8 -#define WHICH_TAB 9 -#define WHICH_CLEAR 12 -#define WHICH_RETURN 13 -#define WHICH_ENTER 13 -#define WHICH_SHIFT 16 -#define WHICH_CONTROL 17 -#define WHICH_CTRL 17 -#define WHICH_MENU 18 -#define WHICH_ALT 18 -#define WHICH_PAUSE 19 -#define WHICH_CAPITAL 20 -#define WHICH_CAPSLOCK 20 -#define WHICH_KANA 21 -#define WHICH_HANGUEL 21 -#define WHICH_HANGUL 21 -#define WHICH_JUNJA 23 -#define WHICH_FINAL 24 -#define WHICH_HANJA 25 -#define WHICH_KANJI 25 -#define WHICH_ESCAPE 27 -#define WHICH_ESC 27 -#define WHICH_CONVERT 28 -#define WHICH_NONCONVERT 29 -#define WHICH_ACCEPT 30 -#define WHICH_MODECHANGE 31 -#define WHICH_SPACE 32 -#define WHICH_SPACEBAR 32 -#define WHICH_PRIOR 33 -#define WHICH_PAGE_UP 33 -#define WHICH_PAGEUP 33 -#define WHICH_NEXT 34 -#define WHICH_PAGE_DOWN 34 -#define WHICH_PAGEDOWN 34 -#define WHICH_END 35 -#define WHICH_HOME 36 -#define WHICH_POS1 36 -#define WHICH_LEFT 37 -#define WHICH_UP 38 -#define WHICH_RIGHT 39 -#define WHICH_DOWN 40 -#define WHICH_SELECT 41 -#define WHICH_PRINT 42 -#define WHICH_EXECUTE 43 -#define WHICH_SNAPSHOT 44 -#define WHICH_PRINT_SCREEN 44 -#define WHICH_PRINTSCREEN 44 -#define WHICH_INSERT 45 -#define WHICH_INS 45 -#define WHICH_DELETE 46 -#define WHICH_DEL 46 -#define WHICH_HELP 47 -#define WHICH_CHAR0 48 -#define WHICH_CHAR1 49 -#define WHICH_CHAR2 50 -#define WHICH_CHAR3 51 -#define WHICH_CHAR4 52 -#define WHICH_CHAR5 53 -#define WHICH_CHAR6 54 -#define WHICH_CHAR7 55 -#define WHICH_CHAR8 56 -#define WHICH_CHAR9 57 -#define WHICH_CHARA 65 -#define WHICH_CHARB 66 -#define WHICH_CHARC 67 -#define WHICH_CHARD 68 -#define WHICH_CHARE 69 -#define WHICH_CHARF 70 -#define WHICH_CHARG 71 -#define WHICH_CHARH 72 -#define WHICH_CHARI 73 -#define WHICH_CHARJ 74 -#define WHICH_CHARK 75 -#define WHICH_CHARL 76 -#define WHICH_CHARM 77 -#define WHICH_CHARN 78 -#define WHICH_CHARO 79 -#define WHICH_CHARP 80 -#define WHICH_CHARQ 81 -#define WHICH_CHARR 82 -#define WHICH_CHARS 83 -#define WHICH_CHART 84 -#define WHICH_CHARU 85 -#define WHICH_CHARV 86 -#define WHICH_CHARW 87 -#define WHICH_CHARX 88 -#define WHICH_CHARY 89 -#define WHICH_CHARZ 90 -#define WHICH_LWIN 91 -#define WHICH_LEFT_WIN 91 -#define WHICH_RWIN 92 -#define WHICH_RIGHT_WIN 92 -#define WHICH_APPS 93 -#define WHICH_SLEEP 95 -#define WHICH_NUMPAD0 96 -#define WHICH_NUM0 96 -#define WHICH_NUMPAD1 97 -#define WHICH_NUM1 97 -#define WHICH_NUMPAD2 98 -#define WHICH_NUM2 98 -#define WHICH_NUMPAD3 99 -#define WHICH_NUM3 99 -#define WHICH_NUMPAD4 100 -#define WHICH_NUM4 100 -#define WHICH_NUMPAD5 101 -#define WHICH_NUM5 101 -#define WHICH_NUMPAD6 102 -#define WHICH_NUM6 102 -#define WHICH_NUMPAD7 103 -#define WHICH_NUM7 103 -#define WHICH_NUMPAD8 104 -#define WHICH_NUM8 104 -#define WHICH_NUMPAD9 105 -#define WHICH_NUM9 105 -#define WHICH_MULTIPLY 106 -#define WHICH_MUL 106 -#define WHICH_ADD 107 -#define WHICH_SEPERATOR 108 -#define WHICH_SEP 108 -#define WHICH_SUBSTRACT 109 -#define WHICH_SUB 109 -#define WHICH_DECIMAL 110 -#define WHICH_DIVIDE 111 -#define WHICH_DIV 111 -#define WHICH_F1 112 -#define WHICH_F2 113 -#define WHICH_F3 114 -#define WHICH_F4 115 -#define WHICH_F5 116 -#define WHICH_F6 117 -#define WHICH_F7 118 -#define WHICH_F8 119 -#define WHICH_F9 120 -#define WHICH_F10 121 -#define WHICH_F11 122 -#define WHICH_F12 123 -#define WHICH_F13 124 -#define WHICH_F14 125 -#define WHICH_F15 126 -#define WHICH_F16 127 -#define WHICH_F17 128 -#define WHICH_F18 129 -#define WHICH_F19 130 -#define WHICH_F20 131 -#define WHICH_F21 132 -#define WHICH_F22 133 -#define WHICH_F23 134 -#define WHICH_F24 135 -#define WHICH_NUMLOCK 144 -#define WHICH_SCROLL 145 -#define WHICH_SCROLL_LOCK 145 -#define WHICH_SCROLLLOCK 145 -#define WHICH_LSHIFT 160 -#define WHICH_LEFT_SHIFT 160 -#define WHICH_RSHIFT 161 -#define WHICH_RIGHT_SHIFT 161 -#define WHICH_LCONTROL 162 -#define WHICH_LCTRL 162 -#define WHICH_LEFT_CONTROL 162 -#define WHICH_LEFT_CTRL 162 -#define WHICH_RCONTROL 163 -#define WHICH_RCTRL 163 -#define WHICH_RIGHT_CONTROL 163 -#define WHICH_RIGHT_CTRL 163 -#define WHICH_LMENU 164 -#define WHICH_LEFT_MENU 164 -#define WHICH_RMENU 165 -#define WHICH_RIGHT_MENU 165 -#define WHICH_BROWSER_BACK 166 -#define WHICH_BROWSER_FORWARD 167 -#define WHICH_BROWSER_REFRESH 168 -#define WHICH_BROWSER_STOP 169 -#define WHICH_BROWSER_SEARCH 170 -#define WHICH_BROWSER_FAVORITES 171 -#define WHICH_BROWSER_HOME 172 -#define WHICH_VOLUME_MUTE 173 -#define WHICH_VOLUME_DOWN 174 -#define WHICH_VOLUME_UP 175 -#define WHICH_MEDIA_NEXT_TRACK 176 -#define WHICH_MEDIA_PREV_TRACK 177 -#define WHICH_MEDIA_STOP 178 -#define WHICH_MEDIA_PLAY_PAUSE 179 -#define WHICH_LAUNCH_MAIL 180 -#define WHICH_LAUNCH_MEDIA_SELECT 181 -#define WHICH_LAUNCH_APP1 182 -#define WHICH_LAUNCH_APP2 183 -#define WHICH_OEM_1 186 -#define WHICH_OEM_PLUS 187 -#define WHICH_PLUS 187 -#define WHICH_OEM_COMMA 188 -#define WHICH_COMMA 188 -#define WHICH_OEM_MINUS 189 -#define WHICH_MINUS 189 -#define WHICH_OEM_PERIOD 190 -#define WHICH_PERIOD 190 -#define WHICH_OEM_2 191 -#define WHICH_OEM_3 192 -#define WHICH_OEM_4 219 -#define WHICH_OEM_5 220 -#define WHICH_OEM_6 221 -#define WHICH_OEM_7 222 -#define WHICH_OEM_8 223 -#define WHICH_OEM_102 226 -#define WHICH_PROCESSKEY 229 -#define WHICH_PROCESS 229 -#define WHICH_PACKET 231 -#define WHICH_ATTN 246 -#define WHICH_CRSEL 247 -#define WHICH_EXSEL 248 -#define WHICH_EREOF 249 -#define WHICH_PLAY 250 -#define WHICH_ZOOM 251 -#define WHICH_NONAME 252 -#define WHICH_PA1 253 -#define WHICH_OEM_CLEAR 254 - -#define CTRL_NULL 0 -#define CTRL_ALT 1 -#define CTRL_CTRL 2 -#define CTRL_SHIFT 4 -#define CTRL_ENHANCED 8 -#define CTRL_CMD 16 -#define CTRL_NUMLOCK 32 -#define CTRL_SCROLLLOCK 64 -#define CTRL_CAPSLOCK 128 - -#define MODE_CMD 0 -#define MODE_VT102 1 -#define MODE_VT100 2 - -#endif // TTYU_GENERATED_H_ diff --git a/include/ttyu.h b/include/ttyu.h index 9789916..9e56815 100644 --- a/include/ttyu.h +++ b/include/ttyu.h @@ -21,25 +21,16 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ +#ifndef INCLUDE_TTYU_H_ +#define INCLUDE_TTYU_H_ -#ifndef TTYU_H_ -#define TTYU_H_ +#include -#include -#include - -#include -#include #include #include -#include "util.h" - -// predefine event data and callbacks for ee.c -#define EE_DATA_TYPE v8::Local -#define EE_DATA_ARG(name) v8::Local name -#define EE_CB_TYPE NanCallback * -#define EE_CB_ARG(name) NanCallback *name -#include +#include +#include +#include // define TRUE & FALSE #ifndef TRUE @@ -49,20 +40,39 @@ # define FALSE 0 #endif -// callback call function for the event emitter -int ttyu_ee_cb_call(ee__listener_t *l, EE_DATA_ARG(data)); -// callback compare function for the event emitter -int ttyu_ee_compare(EE_CB_ARG(cb1), EE_CB_ARG(cb2)); - -// predefine classes -class ttyu_js_c; -class ttyu_worker_c; - -// data structure for caching platform-dependent terminal handles -// these are defined in platform-dependent source and header files -typedef struct ttyu_data_s ttyu_data_t; -void ttyu_data_init(ttyu_data_t *data); -void ttyu_data_destroy(ttyu_data_t *data); +// more defines (extending ) +#define EVENT_NONE -1 +#define EMIT_INTERVAL 20 + +// error messages +typedef struct ttyu_error_s { + int id; + const char *msg; +} ttyu_error_t; + +#define __ERRMSG(XX) \ + XX(0x00, "critical unknown error"); \ + XX(0x01, "invalid console handle value"); \ + XX(0x02, "could not get console output buffer information"); \ + XX(0x03, "could not set console output buffer information"); \ + XX(0x04, "could not fill console output buffer") + +TTYU_INLINE ttyu_error_t _ERRMSG(int id) { + ttyu_error_t err; + err.id = id; + #define XX(i, name) if (i == id) { \ + err.msg = name; \ + return err; \ + } + __ERRMSG(XX); + #undef XX + return _ERRMSG(0); +} +#define ERRMSG(id) &_ERRMSG(id) + +#define THROW_IF_STOPPED(obj) if (!obj->running) { \ + return NanThrowError("Function requires ttyutil to be running"); \ +} // key event structure typedef struct ttyu_key_s { @@ -81,11 +91,10 @@ typedef struct ttyu_mouse_s { // event data structure typedef struct ttyu_event_s { int type; - const char *err; ttyu_key_t *key; ttyu_mouse_t *mouse; } ttyu_event_t; -void ttyu_event_create_error(ttyu_event_t *event, const char *err); +void ttyu_event_create_error(ttyu_event_t *event); void ttyu_event_create_resize(ttyu_event_t *event); void ttyu_event_create_key(ttyu_event_t *event, int ctrl, char *c, int code, int which); @@ -93,123 +102,8 @@ void ttyu_event_create_mouse(ttyu_event_t *event, int type, int button, int x, int y, int ctrl); void ttyu_event_destroy(ttyu_event_t *event); -// helper functions for exception throwing -#define TTYU_THROW_IF_DESTROYED(obj) do { \ - if(obj->throw_ && obj->destroyed_) { \ - NanThrowError("TTYUtil object was already destroyed"); \ - NanReturnUndefined(); \ - } \ -} while(0) -#define TTYU_THROW_IF_NOT_RUNNING(obj) do { \ - TTYU_THROW_IF_DESTROYED(obj); \ - if(obj->throw_ && !obj->running) { \ - NanThrowError("TTYUtil object was not started"); \ - NanReturnUndefined(); \ - } \ -} while(0) -#define TTYU_THROW_IF_NOT_RUNNING_VOID(obj) do { \ - if(obj->throw_ && obj->destroyed_) { \ - NanThrowError("TTYUtil object was already destroyed"); \ - return; \ - } \ - if(obj->throw_ && !obj->running) { \ - NanThrowError("TTYUtil object was not started"); \ - return; \ - } \ -} while(0) - -// definition of the node module class -class ttyu_js_c : public node::ObjectWrap { -public: - static void init(v8::Handle target); - explicit ttyu_js_c(); - void destroy(); - - ttyu_data_t *data; - ee_emitter_t *emitter; - bool running; - bool paused; -#ifndef PLATFORM_WINDOWS - uv_barrier_t barrier; -#endif -private: - ~ttyu_js_c(); - - static NAN_METHOD(new_instance); - static NAN_METHOD(start); - static NAN_METHOD(pause); - static NAN_METHOD(destroy); - static NAN_METHOD(on); - static NAN_METHOD(off); - static NAN_METHOD(emit); - static NAN_GETTER(is_running); - static NAN_GETTER(get_width); - static NAN_GETTER(get_height); - static NAN_GETTER(get_mode); - static NAN_GETTER(get_colors); - static NAN_GETTER(getx); - static NAN_SETTER(setx); - static NAN_GETTER(gety); - static NAN_SETTER(sety); - static NAN_METHOD(gotoxy); - static NAN_METHOD(write); - static NAN_METHOD(beep); - static NAN_METHOD(clear); - static NAN_METHOD(prepare); - static NAN_METHOD(color); - - static v8::Persistent constructor; - - bool throw_; - bool destroyed_; - ttyu_worker_c *worker_; -}; - -// definition of the terminal input listener class -class ttyu_worker_c : public NanAsyncWorker { -public: - class ttyu_progress_c { - friend class ttyu_worker_c; - public: - void send(const ttyu_event_t *event) const; - private: - explicit ttyu_progress_c(ttyu_worker_c *that); - // prevent movement - ttyu_progress_c(const ttyu_progress_c&); - void operator=(const ttyu_progress_c&); -#if __cplusplus >= 201103L - ttyu_progress_c(const ttyu_progress_c&&) V8_DELETE; - void operator=(const ttyu_progress_c&&) V8_DELETE; -#endif - ttyu_worker_c *const that_; - }; - - ttyu_worker_c(ttyu_js_c *obj); - virtual ~ttyu_worker_c(); - - void progress(); - - bool execute(const ttyu_progress_c& progress, ttyu_data_t *data); - void handle(ttyu_event_t *event); - - virtual void Destroy(); - - void Execute(); - virtual void WorkComplete(); -private: - void send_(const ttyu_event_t *event); - - static NAUV_WORK_CB(async_progress_); - static void async_close_(uv_handle_t *handle); - - uv_async_t *async; - uv_mutex_t *async_lock; - ttyu_event_t *asyncdata_; - ttyu_js_c *obj_; -}; - -template class ttyu_queue_c; -template struct ttyu_queue_data_s; +// predefine data class +class ttyu_js_c; // include platform dependent headers #ifdef PLATFORM_WINDOWS @@ -218,4 +112,40 @@ template struct ttyu_queue_data_s; # include #endif -#endif // TTYU_H_ +class ttyu_js_c : public node::ObjectWrap { + public: + ttyu_js_c(); + ~ttyu_js_c(); + + static void init(DATATYPE exports, DATATYPE module); + static NAN_METHOD(js_new); + static NAN_METHOD(js_start); + static NAN_METHOD(js_stop); + static NAN_METHOD(js_emit); + static NAN_METHOD(js_running); + static NAN_METHOD(js_getwidth); + static NAN_METHOD(js_getheight); + static NAN_METHOD(js_setwidth); + static NAN_METHOD(js_setheight); + static NAN_METHOD(js_resize); + static NAN_METHOD(js_mode); + static NAN_METHOD(js_colors); + static NAN_METHOD(js_setx); + static NAN_METHOD(js_getx); + static NAN_METHOD(js_sety); + static NAN_METHOD(js_gety); + static NAN_METHOD(js_goto); + static NAN_METHOD(js_beep); + static NAN_METHOD(js_clear); + static NAN_METHOD(js_write); + static NAN_METHOD(js_hide); + static NAN_METHOD(js_show); + + ttyu_error_t *err; + bool running; + bool stop; + NanCallback *emitter; + PLATFORM_DEPENDENT_FIELDS; +}; + +#endif // INCLUDE_TTYU_H_ diff --git a/include/unix.h b/include/unix.h index 9c24013..28eba1c 100644 --- a/include/unix.h +++ b/include/unix.h @@ -1,5 +1,5 @@ /* ttyutil - unix.h - additional header file for unixy systems, - * impementation in src/unix.cc + * impementation in src/unix/. * https://github.com/clidejs/ttyutil * * Copyright Bernhard Bücherl @@ -22,20 +22,17 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -#ifndef TTYU_UNIX_H_ -#define TTYU_UNIX_H_ +#ifndef INCLUDE_UNIX_H_ +#define INCLUDE_UNIX_H_ #define NCURSES_OPAQUE FALSE +#include #include +#include +#include +#include #include -#define ERROR_UNIX_UNDEF "unknown error occured while reading input" -#define ERROR_UNIX_MOUSEBAD "skipping unreadable mouse event" -#define ERROR_UNIX_MOUSEUNCAUGHT "skipping unknown mouse event" - -#define TTYU_EXIT 2 -#define TTYU_UNKNOWN 1 - #define TTYU_UNIX_KW(XX) \ XX(WHICH_DOWN, KEY_DOWN, FALSE); \ XX(WHICH_DOWN, KEY_SF, FALSE); \ @@ -103,78 +100,44 @@ \ XX(WHICH_SHIFT, 0, TRUE) -// thread save queue -template struct ttyu_queue_data_s; -template class ttyu_queue_c { -public: - ttyu_queue_c() : size_(0) { - uv_mutex_init(&mutex_); - } - - virtual ~ttyu_queue_c() { - uv_mutex_destroy(&mutex_); - } - - void push(T val) { - DBG("push has started"); - uv_mutex_lock(&mutex_); - queue_.push(&val); - uv_mutex_unlock(&mutex_); - DBG("push has finished"); - } - - T *pop() { - DBG("pop acquiring rdlock"); - uv_mutex_lock(&mutex_); - DBG("pop acquired rdlock"); - T *front = queue_.front(); - queue_.pop(); - uv_mutex_unlock(&mutex_); - DBG("pop released rdlock"); - return front; - } +#define REFRESH_POSITION(obj) wmove(obj->win, obj->x, obj->y); \ + wrefresh(obj->win) - int size() { - DBG("size has sarted"); - if(uv_mutex_trylock(&mutex_) == 0) { - DBG("size acquired rdlock"); - size_ = queue_.size(); - uv_mutex_unlock(&mutex_); - DBG("size released rdlock"); - } - return size_; - } - - bool empty() { - DBG("empty has sarted"); - if(uv_mutex_trylock(&mutex_) == 0) { - DBG("empty acquired rdlock"); - size_ = queue_.size(); - uv_mutex_unlock(&mutex_); - DBG("empty released rdlock"); - } - return (size_ == 0); - } +int ttyu_unix_which(int c); +int ttyu_unix_key(int which); +TTYU_INLINE int ttyu_get_colors(); -private: - std::queue queue_; - uv_mutex_t mutex_; - int size_; -}; +class ttyu_worker_c : public NanAsyncWorker { + public: + explicit ttyu_worker_c(ttyu_js_c *obj) : NanAsyncWorker(NULL), obj(obj) { } + ~ttyu_worker_c() { } -// unixy data structure -struct ttyu_data_s { - WINDOW *win; - mmask_t old_mouse_mask; - int mode; - bool closing; + void Execute(); + void HandleOKCallback(); - ttyu_queue_c ungetch_stack; - ttyu_queue_c ungetmouse_stack; + private: + ttyu_js_c *obj; + std::vector emit_stack; }; -void ttyu_unix_clrscr(ttyu_data_t *data, int x, int y, int width, int height); -int ttyu_unix_key(int which); -int ttyu_unix_which(int key); +#define PLATFORM_DEPENDENT_FIELDS \ + void check_queue(); \ + static void curses_thread_func(void *that); \ + static int curses_threaded_func_thread(WINDOW *win, void *that); \ + static int curses_threaded_func(WINDOW *win, ttyu_js_c *obj); \ + \ + uv_thread_t *curses_thread; \ + WINDOW *win; \ + uv_barrier_t *barrier; \ + uv_mutex_t *emitstacklock; \ + uv_mutex_t *ungetlock; \ + uv_cond_t *condition; \ + int mode; \ + int x; \ + int y; \ + int colors; \ + bool worker_run; \ + std::queue unget_stack; \ + std::vector emit_stack -#endif // TTYU_UNIX_H_ +#endif // INCLUDE_UNIX_H_# diff --git a/include/util.h b/include/util.h deleted file mode 100644 index a0cdb3f..0000000 --- a/include/util.h +++ /dev/null @@ -1,64 +0,0 @@ -/* ttyutil - util.h - header file to define utility methods, - * implementation in src/util.cc - * https://github.com/clidejs/ttyutil - * - * Copyright Bernhard Bücherl - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ -#ifndef TTYU_UTIL_H_ -#define TTYU_UTIL_H_ - -#include -#include - -#define EXPORT_PROTOTYPE_METHOD NODE_SET_PROTOTYPE_METHOD -#define EXPORT_PROTOTYPE_METHOD_HIDDEN(tpl, name, cb) do { \ - v8::Local t = NanNew(cb); \ - tpl->InstanceTemplate()->Set( \ - NanNew(name), t->GetFunction(), v8::ReadOnly); \ -} while(0) - -#define EXPORT_PROTOTYPE_GET(tpl, name, fn) \ - tpl->InstanceTemplate()->SetAccessor(NanNew(name), (fn)) - -#define EXPORT_PROTOTYPE_GETSET(tpl, name, get, set) \ - tpl->InstanceTemplate()->SetAccessor(NanNew(name), (get), (set)) - -#define WIN_COLORS 16 - -short util_rgbi2term(short r, short g, short b); -short util_rgbi2win(short r, short g, short b); -char *util_render(const char *ch, short fg, short bg); -short util_parse_dec(char d); -short util_parse_hex(char h); -unsigned long util_term2argb(short t); -short util_rgb2term(const char *rgb); -short util_hex2term(const char *hex); -short util_color(const char *c); -int util_max(int a, int b); -int util_min(int a, int b); -int util_abs(int a); -char *util_error(char *name, int id); - -#undef ERROR -#define ERROR(name, id) util_error(name, id) -#define DBG(a) std::cout << (a) << "\r\n" - -#endif // TTYUTIL_UTIL_H_ diff --git a/include/utils.h b/include/utils.h new file mode 100644 index 0000000..7294b4d --- /dev/null +++ b/include/utils.h @@ -0,0 +1,132 @@ +/* ttyutil - utils.h - header file to define utility macros + * https://github.com/clidejs/ttyutil + * + * Copyright Bernhard Bücherl + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#ifndef INCLUDE_UTILS_H_ +#define INCLUDE_UTILS_H_ + +#define DATATYPE v8::Local + +#define EXPORT_METHOD(tpl, name, cb) do { \ + v8::Local t = NanNew(cb); \ + tpl->InstanceTemplate()->Set(NanNew(name), \ + t->GetFunction(), v8::ReadOnly); \ + ++_exports; \ +} while (0) + +#define TTYU_TOSTRING(handle) \ + (new v8::String::Utf8Value(handle->ToString()))->operator*() + +#define MUTEX_LOCK(mutex, action) do { \ + uv_mutex_lock(mutex); \ + action; \ + uv_mutex_unlock(mutex); \ +} while (0) + +#ifdef PLATFORM_WINDOWS +# define JSFUNCTION(_0, ...) JSFUNCTION_CALL(JSFUNCTION_((__VA_ARGS__, \ + JSFUNCTION_PROTO, JSFUNCTION_STATIC, 0)), (_0, __VA_ARGS__)) +# define JSFUNCTION_CALL(fun, args) fun args +# define JSFUNCTION_(args) JSFUNCTION__ args +#else +# define JSFUNCTION(_0, ...) JSFUNCTION__(__VA_ARGS__, JSFUNCTION_PROTO, \ + JSFUNCTION_STATIC, 0)(_0, __VA_ARGS__) +#endif +#define JSFUNCTION__(_1, _2, _3, ...) _3 +#define JSFUNCTION_STATIC(name, body) NAN_METHOD(name) { \ + NanScope(); \ + if (1) body \ + NanReturnUndefined(); \ +} +#define JSFUNCTION_PROTO(clas, name, body) NAN_METHOD(clas::name) { \ + NanScope(); \ + clas *that = ObjectWrap::Unwrap(args.This()); \ + if (1) body \ + NanReturnUndefined(); \ +} + +#define ALLOC(c, n) reinterpret_cast(std::malloc(sizeof(c) * (n))); + +#define EMIT_EVENT_OBJECT(event, cb) do { \ + v8::Local __obj = NanNew(); \ + switch (event->type) { \ + case EVENT_RESIZE: \ + __obj->Set(NanNew("type"), EVENTSTRING_RESIZE); \ + break; \ + case EVENT_KEY: \ + __obj->Set(NanNew("type"), EVENTSTRING_KEY); \ + __obj->Set(NanNew("ctrl"), \ + NanNew(event->key->ctrl)); \ + __obj->Set(NanNew("char"), \ + NanNew(event->key->c)); \ + __obj->Set(NanNew("code"), \ + NanNew(event->key->code)); \ + __obj->Set(NanNew("which"), \ + NanNew(event->key->which)); \ + break; \ + case EVENT_MOUSEDOWN: \ + case EVENT_MOUSEUP: \ + case EVENT_MOUSEMOVE: \ + case EVENT_MOUSEWHEEL: \ + case EVENT_MOUSEHWHEEL: \ + if (event->type == EVENT_MOUSEDOWN) { \ + __obj->Set(NanNew("type"), EVENTSTRING_MOUSEDOWN); \ + } else if (event->type == EVENT_MOUSEUP) { \ + __obj->Set(NanNew("type"), EVENTSTRING_MOUSEUP); \ + } else if (event->type == EVENT_MOUSEMOVE) { \ + __obj->Set(NanNew("type"), EVENTSTRING_MOUSEMOVE); \ + } else if (event->type == EVENT_MOUSEWHEEL) { \ + __obj->Set(NanNew("type"), EVENTSTRING_MOUSEWHEEL); \ + } else if (event->type == EVENT_MOUSEHWHEEL) { \ + __obj->Set(NanNew("type"), EVENTSTRING_MOUSEHWHEEL); \ + } \ + __obj->Set(NanNew("button"), \ + NanNew(event->mouse->button)); \ + __obj->Set(NanNew("x"), \ + NanNew(event->mouse->x)); \ + __obj->Set(NanNew("y"), \ + NanNew(event->mouse->y)); \ + __obj->Set(NanNew("ctrl"), \ + NanNew(event->mouse->ctrl)); \ + break; \ + default: \ + __obj->Set(NanNew("type"), EVENTSTRING_ERROR); \ + __obj->Set(NanNew("error"), NanError("TODO")); \ + event->type = EVENT_ERROR; \ + break; \ + } \ + \ + v8::Local __args[] = { __obj }; \ + DBG(" emit_event_object calling"); \ + cb->Call(1, __args); \ + DBG(" emit_event_object called"); \ +} while (0) + +#if defined(__GNUC__) && !(defined(DEBUG) && DEBUG) +# define TTYU_INLINE inline __attribute__((always_inline)) +#elif defined(_MSC_VER) && !(defined(DEBUG) && DEBUG) +# define TTYU_INLINE __forceinline +#else +# define TTYU_INLINE inline +#endif + +#endif // INCLUDE_UTILS_H_ diff --git a/include/win.h b/include/win.h index a61647f..b795a3d 100644 --- a/include/win.h +++ b/include/win.h @@ -1,5 +1,5 @@ /* ttyutil - win.h - additional header file for windows systems, - * implementation in src/win.cc + * implementation in src/win/. * https://github.com/clidejs/ttyutil * * Copyright Bernhard Bücherl @@ -22,43 +22,143 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -#ifndef TTYU_WIN_H_ -#define TTYU_WIN_H_ +#ifndef INCLUDE_WIN_H_ +#define INCLUDE_WIN_H_ #include #define WIN_BUFFER_SIZE 128 -typedef struct ttyu_error_s { - char *msg; - bool kill; -} ttyu_error_t; - -#define ERROR_WIN_INIT "could not get console handles - %d" -#define ERROR_WIN_GET "could not get console output buffer information - %d" -#define ERROR_WIN_SET "could not set console output buffer information - %d" -#define ERROR_WIN_FILL "could not fill console output buffer - %d" - -// windows data structure -struct ttyu_data_s { - HANDLE hin; - HANDLE hout; - DWORD old_mode; - ttyu_error_t *err; - int width; - int height; - int top; - int curx; - int cury; - short base_color; - bool closing; +#ifndef MOUSE_HWHEELED +#define MOUSE_HWHEELED 0x0008 +#endif + +// worker class, heavily inspired by nan's NanAsyncProgressWorker +class ttyu_worker_c : public NanAsyncWorker { + public: + class ttyu_progress_c { + friend class ttyu_worker_c; + public: + void send(const ttyu_event_t *event) const { + DBG("::send"); + that_->send_(event); + } + private: + explicit ttyu_progress_c(ttyu_worker_c *that) : that_(that) {} + // prevent movement + ttyu_progress_c(const ttyu_progress_c&); + void operator=(const ttyu_progress_c&); +#if __cplusplus >= 201103L + ttyu_progress_c(const ttyu_progress_c&&) V8_DELETE; // NOLINT(build/c++11) + void operator=(const ttyu_progress_c&&) V8_DELETE; // NOLINT(build/c++11) +#endif + ttyu_worker_c *const that_; + }; + + explicit ttyu_worker_c(ttyu_js_c *obj) : NanAsyncWorker(NULL), + asyncdata_(NULL), obj_(obj) { + async = new uv_async_t; + async_lock = new uv_mutex_t; + uv_mutex_init(async_lock); + uv_async_init(uv_default_loop(), async, async_progress_); + async->data = this; + } + ~ttyu_worker_c() { + DBG("::~ttyu_worker_c"); + uv_mutex_destroy(async_lock); + ttyu_event_destroy(asyncdata_); + if (asyncdata_) + free(asyncdata_); + DBG("::~ttyu_worker_c freed"); + } + + void progress() { + DBG("::process aquire lock"); + uv_mutex_lock(async_lock); + DBG("::process aquired lock"); + ttyu_event_t *event = asyncdata_; + asyncdata_ = NULL; + uv_mutex_unlock(async_lock); + DBG("::process released lock"); + + if (event) { + handle(event); + DBG("destroying event"); + ttyu_event_destroy(event); + DBG("freeing event"); + free(event); + DBG("freed event"); + } + } + + bool execute(const ttyu_progress_c& progress, ttyu_js_c *obj); + void handle(ttyu_event_t *event); + + virtual void Destroy() { + uv_close(reinterpret_cast(async), async_close_); + } + + void Execute(); + + void WorkComplete() { /* do nothing */ } + + private: + void send_(const ttyu_event_t *event) { + DBG("::send_"); + size_t size = sizeof(*event); + ttyu_event_t *new_event = reinterpret_cast(malloc(size)); + memcpy(new_event, event, size); + DBG("::send_ aquire lock"); + uv_mutex_lock(async_lock); + DBG("::send_ aquired lock"); + ttyu_event_t *old_event = asyncdata_; + asyncdata_ = new_event; + uv_mutex_unlock(async_lock); + DBG("::send_ released lock"); + + ttyu_event_destroy(old_event); + if (old_event != NULL) + free(old_event); + DBG(" uv_async_send"); + uv_async_send(async); + } + + static NAUV_WORK_CB(async_progress_) { + ttyu_worker_c *worker = static_cast(async->data); + worker->progress(); + } + static void async_close_(uv_handle_t *handle) { + ttyu_worker_c *worker = static_cast(handle->data); + delete reinterpret_cast(handle); + delete worker; + } + + uv_async_t *async; + uv_mutex_t *async_lock; + ttyu_event_t *asyncdata_; + ttyu_js_c *obj_; }; +#define PLATFORM_DEPENDENT_FIELDS \ + HANDLE hin; \ + HANDLE hout; \ + DWORD old_mode; \ + DWORD top; \ + DWORD width; \ + DWORD height; \ + DWORD curx; \ + DWORD cury; \ + uv_barrier_t *barrier; \ + ttyu_worker_c *worker + +#define BARRIER_WAITKILL(b) if (uv_barrier_wait(b) > 0) { \ + uv_barrier_destroy(b); \ + free(b); \ + } + +bool ttyu_win_scr_update(ttyu_js_c *obj, bool initial); int ttyu_win_which(DWORD code); int ttyu_win_ctrl(DWORD state); DWORD ttyu_win_state(int ctrl); -bool ttyu_win_scr_update(ttyu_data_t *data, bool initial = FALSE); -void ttyu_win_render(char *c, ttyu_data_t *data); -bool ttyu_win_clrscr(ttyu_data_t *data, int x, int y, int width, int height); -#endif // TTYU_WIN_H_ +#endif // INCLUDE_WIN_H_ diff --git a/index.js b/index.js index 2d4834f..c47085c 100644 --- a/index.js +++ b/index.js @@ -1 +1 @@ -module.exports = require("./export")(require("./build/Release/ttyu")); +module.exports = require("./lib/export")(require("./build/Release/ttyu")); diff --git a/installer/generate.js b/installer/generate.js new file mode 100644 index 0000000..3629d64 --- /dev/null +++ b/installer/generate.js @@ -0,0 +1,47 @@ +var path = require("path"); +var fs = require("fs"); + +module.exports = function(ENV) { + return function(cb) { + var generated = path.join(__dirname, "..", "include", "generated.h"); + var cont = "/** ttyutil - generated.h - generated header\n" + + " * https://github.com/clidejs/ttyutil\n" + + " *\n" + + " * Copyright Bernhard Bücherl \n" + + " *\n" + + " * do not change this file,\n" + + " * it is autogenerated by 'npm install',\n" + + " * these constants are set in /const.js\n" + + " */\n" + + "#ifndef INCLUDE_GENERATED_H_\n" + + "#define INCLUDE_GENERATED_H_\n" + + "\n"; + for(var name in ENV.CONST) { + for(var sub in ENV.CONST[name]) { + cont += "#define " + name.toUpperCase() + "_" + sub.toUpperCase() + + " " + toCpp(ENV.CONST[name][sub]) + "\n"; + } + cont += "\n"; + } + if(ENV.TTYU_CODE_DEBUG) { + cont += "#define CDEBUG\n"; + if(ENV.TTYU_CODE_DEBUG === "file") { + cont += "#define CDEBUG_FILE\n"; + } + cont += "\n"; + } + cont += "#endif // INCLUDE_GENERATED_H_\n"; + console.log(" [preinstall] write constants to 'generated.h'"); + fs.writeFile(generated, cont, function(err) { + cb(err, 1); + }); + } +}; + +function toCpp(v) { + if(typeof v === "string") { + return "NanNew(\"" + v + "\")"; + } else { + return v; + } +} diff --git a/installer/index.js b/installer/index.js new file mode 100644 index 0000000..3a9a438 --- /dev/null +++ b/installer/index.js @@ -0,0 +1,39 @@ +var fs = require("fs"); +var path = require("path"); + +var util = require("./util"); + +var ENV = { + TTYU_NCURSES_REBUILD: process.env.TTYU_NCURSES_REBUILD || false, + TTYU_BUILD_DEBUG: process.env.TTYU_BUILD_DEBUG || false, + TTYU_CODE_DEBUG: process.env.TTYU_CODE_DEBUG || false, + WIN_OS: process.platform === "win32", + PKG: require("../package.json"), + CONST: require("../lib/const"), + LIB_PATH: path.join(__dirname, "..", "deps", "ncurses", "lib") +}; + +util.waterfall([ + // startup + function(cb) { + console.log(" [preinstall] ENV: [ TTYU_NCURSES_REBUILD='" + + ENV.TTYU_NCURSES_REBUILD + "', TTYU_BUILD_DEBUG='" + + ENV.TTYU_BUILD_DEBUG + "', TTYU_CODE_DEBUG='" + ENV.TTYU_CODE_DEBUG + + "', WIN_OS='" + ENV.WIN_OS + "' ]"); + cb(); + }, + // generate header file + require("./generate")(ENV), + // prepare ncurses + require("./ncurses")(ENV), + // conclusion + function(cb) { + console.log(" [preinstall] finishing..."); + if(!fs.existsSync(ENV.LIB_PATH)) // to prevent ld warning in build + util.mkdirRecursiveSync(ENV.LIB_PATH); + cb(); + } +], function(err) { + if(err) throw err; + console.log(" [preinstall] finished with code -0"); +}); diff --git a/installer/ncurses/build.js b/installer/ncurses/build.js new file mode 100644 index 0000000..eda3571 --- /dev/null +++ b/installer/ncurses/build.js @@ -0,0 +1,28 @@ +var cp = require("child_process"); + +module.exports = function(ENV, cb) { + console.log(" [preinstall/ncurses] './configure'"); + var cfg = cp.spawn("./configure", ["--with-shared", "--enable-pc-files", + "--enable-widec", "--without-normal"], { + cwd: ENV.NCURSES_PATH + }); + if(ENV.TTYU_BUILD_DEBUG) { + cfg.stdout.pipe(process.stdout); + cfg.stderr.pipe(process.stderr); + } + cfg.on("exit", function(code) { + if(code !== 0) throw "ncurses './configure' exited with code -" + code; + console.log(" [preinstall/ncurses] 'make'"); + var make = cp.spawn("make", { + cwd: ENV.NCURSES_PATH + }); + if(ENV.TTYU_BUILD_DEBUG) { + make.stdout.pipe(process.stdout); + make.stderr.pipe(process.stderr); + } + make.on("exit", function(code) { + if(code !== 0) throw "ncurses 'make' exited with code -" + code; + cb(); + }); + }); +}; diff --git a/installer/ncurses/download.js b/installer/ncurses/download.js new file mode 100644 index 0000000..7c4fd7a --- /dev/null +++ b/installer/ncurses/download.js @@ -0,0 +1,17 @@ +var gethub = require("gethub"); + +var build = require("./build"); +var util = require("../util"); + +module.exports = function(ENV, download, cb) { + if(download) { + util.rmRecursiveSync(ENV.NCURSES_PATH); + console.log(" [preinstall/ncurses] cloning from " + ENV.NCURSES_URL); + gethub("mirror", "ncurses", "master", ENV.NCURSES_PATH, function(err) { + if(err) throw err; + build(ENV, cb); + }); + } else { + build(ENV, cb); + } +}; diff --git a/installer/ncurses/index.js b/installer/ncurses/index.js new file mode 100644 index 0000000..863adfe --- /dev/null +++ b/installer/ncurses/index.js @@ -0,0 +1,45 @@ +var cp = require("child_process"); +var fs = require("fs"); +var path = require("path"); +var https = require("https"); + +var util = require("../util"); +var execute = require("./download"); + +module.exports = function(ENV) { + ENV.NCURSES_PATH = path.join(__dirname, "..", "..", "deps", "ncurses"); + ENV.NCURSES_URL = "https://github.com/mirror/ncurses"; + var rebuild = function(cb) { + console.log(" [preinstall/ncurses] rebuilding..."); + cb(); + }; + + return function(cb) { + if(ENV.WIN_OS) { + console.log(" [preinstall/ncurses] step skipped for windows"); + cb(); + } else { + console.log(" [preinstall/ncurses] checking..."); + var rebuild = ENV.TTYU_NCURSES_REBUILD; + var download = !fs.existsSync(ENV.NCURSES_PATH); + + if(rebuild) return execute(ENV, true, cb); + + util.spawn("gcc", ["-lncurses"], { }, + function(code, stdout, stderr) { + if(stderr.search(/\-lncurses/) === -1) { + console.log(" [preinstall/ncurses] library found"); + cb(); + } else { + if(fs.existsSync(path.join(ENV.NCURSES_PATH, "lib", + "libncurses++.a"))) { + console.log(" [preinstall/ncurses] using latest private build"); + cb(); + } else { + execute(ENV, download, cb); + } + } + }); + } + } +}; diff --git a/installer/util.js b/installer/util.js new file mode 100644 index 0000000..642fcd2 --- /dev/null +++ b/installer/util.js @@ -0,0 +1,56 @@ +var path = require("path"); +var fs = require("fs"); +var cp = require("child_process"); + +var util = module.exports = { + // waterfall function (inspired by async.waterfall) + waterfall: function(fns, end) { + (function h2o(index) { + fns[index](function(err, id) { + if(err) return end(err, id); + if(++index < fns.length) h2o(index); else end(); + }); + })(0); + }, + + // spawn a new process (child_process.spawn) which will callback when finished + // with stdout and stderr output as string + spawn: function(cmd, args, options, cb) { + var proc = cp.spawn(cmd, args, options); + var stdout = ""; + var stderr = ""; + + proc.stdout.on("data", function(chunk) { + stdout += chunk; + }); + proc.stderr.on("data", function(chunk) { + stderr += chunk; + }); + proc.on("close", function(code) { + cb(code, stdout, stderr); + }); + }, + + rmRecursiveSync: function(p) { + var files = []; + if(fs.existsSync(p)) { + files = fs.readdirSync(p); + for(var index = 0, file; index < files.length; ++index) { + file = path.join(p, files[index]); + if(fs.lstatSync(file).isDirectory()) { // recurse + util.rmRecursiveSync(file); + } else { // delete file + fs.unlinkSync(file); + } + } + fs.rmdirSync(p); + } + }, + + mkdirRecursiveSync: function(p) { + if(!fs.existsSync(p)) { + util.mkdirRecursiveSync(path.join(p, "..")); + fs.mkdirSync(p); + } + } +}; diff --git a/const.js b/lib/const.js similarity index 99% rename from const.js rename to lib/const.js index 7b116a8..f69682a 100644 --- a/const.js +++ b/lib/const.js @@ -213,7 +213,8 @@ module.exports = { ZOOM: 0xFB, NONAME: 0xFC, PA1: 0xFD, - OEM_CLEAR: 0xFE + OEM_CLEAR: 0xFE, + FN: 0xFF }, Ctrl: { diff --git a/lib/export.js b/lib/export.js new file mode 100644 index 0000000..2c81750 --- /dev/null +++ b/lib/export.js @@ -0,0 +1,270 @@ +var events = require("events"); +var Const = require("./const"); +var signal = require("./signal"); +var utils = require("./utils"); +var DIRECT = [ + "running", + "width", + "height", + "resize", + "mode", + "colors", + "x", + "y", + "goto", + "beep", + "clear", + "start", + "stop" +]; +var NOP = function() {}; + +module.exports = function(ttyu_js_c) { + var emitter = new events.EventEmitter(); + var fg, bg, bold, italic, underline, reverse; + var ttyu = new ttyu_js_c(function(ev) { + emitter.emit(ev.type, ev); + }); + emitter.setMaxListeners(100); + + var ttyutil = createObjectWithSealedMethods({ + on: function(ev, listener) { + if(ev === ttyutil.EVENT.SIGNAL) { + signal.on(ttyu, ttyu, listener); + } else if(ev in Const.Event && listener instanceof Function) { + emitter.on(ev, listener); + } + return ttyutil; + }, + removeListener: off, + off: off, + emit: function(ev) { + switch(ev.type) { + case ttyutil.EVENT.SIGNAL: + signal.emit(ev.signal); + break; + case ttyutil.EVENT.KEY: + ttyu.emit(Const.Event[ev.type], ev.which, ev.ctrl); + break; + case ttyutil.EVENT.MOUSEDOWN: + case ttyutil.EVENT.MOUSEUP: + case ttyutil.EVENT.MOUSEMOVE: + case ttyutil.EVENT.MOUSEWHEEL: + case ttyutil.EVENT.MOUSEHWHEEL: + ttyu.emit(Const.Event[ev.type], ev.button, ev.x, ev.y, + ev.ctrl); + break; + default: + // ERROR, RESIZE + break; + } + }, + reset: reset, + fg: function(r,g,b) { + if(arguments.length == 3) { + fg = utils.rgb(r,g,b); + } else if(arguments.length == 1) { + if(typeof r === "number") { + fg = r; + } else { + fg = utils.hex(r); + } + } else { + fg = -1; + } + return ttyutil; + }, + bg: function(r,g,b) { + if(arguments.length == 3) { + bg = utils.rgb(r,g,b); + } else if(arguments.length == 1) { + if(typeof r === "number") { + bg = r; + } else { + bg = utils.hex(r); + } + } else { + bg = -1; + } + return ttyutil; + }, + bold: function(flag) { + if(typeof flag === "boolean") { + bold = flag; + } else if(typeof flag === "undefined") { + bold = true; + } + return ttyutil; + }, + italic: function(flag) { + if(typeof flag === "boolean") { + italic = flag; + } else if(typeof flag === "undefined") { + italic = true; + } + return ttyutil; + }, + underline: function(flag) { + if(typeof flag === "boolean") { + underline = flag; + } else if(typeof flag === "undefined") { + underline = true; + } + return ttyutil; + }, + reverse: function(flag) { + if(typeof flag === "boolean") { + reverse = flag; + } else if(typeof flag === "undefined") { + reverse = true; + } + return ttyutil; + }, + write: function(chunk) { + format = "%s"; + if(fg != -1) + format = "\x1b[38;5;" + fg + "m" + format + "\x1b[39m"; + if(bg != -1) + format = "\x1b[48;5;" + bg + "m" + format + "\x1b[49m"; + if(bold) + format = "\x1b[1m" + format; + else + format = "\x1b[22m" + format; + if(italic) + format = "\x1b[3m" + format; + else + format = "\x1b[23m" + format; + if(underline) + format = "\x1b[4m" + format; + else + format = "\x1b[24m" + format; + ttyu.write(reverse ? rev(chunk) : chunk, format); + return ttyutil; + } + }); + + DIRECT.forEach(function(d) { + if(d in ttyu) { + Object.defineProperty(ttyutil, d, { + __proto__: null, + value: function() { + var ret = ttyu[d].apply(ttyu, arguments); + if(ret) + return ret; + return ttyutil; + }, + configurable: false, + enumerable: false, + writeable: false + }); + } else if("set" + d in ttyu) { + Object.defineProperty(ttyutil, d, { + __proto__: null, + get: ttyu["get" + d], + set: ttyu["set" + d], + configurable: false, + enumerable: true, + writeable: false + }); + } else { + Object.defineProperty(ttyutil, d, { + __proto__: null, + get: ttyu["get" + d].bind(ttyu), + set: NOP, + configurable: false, + enumerable: true, + writeable: false + }); + } + }); + + function createObjectWithSealedMethods(methods) { + var obj = {}; + var props = {}; + + for(var key in methods) { + props[key] = { + __proto__: null, + value: methods[key], + configurable: false, + enumerable: false, + writeable: false + }; + } + + Object.defineProperties(obj, props); + return obj; + } + + function rev(str) { + for(var i = str.length - 1, o = ""; i >= 0; o += str[i--]); + return o; + } + + function reset() { + fg = -1; + bg = -1; + bold = false; + italic = false; + underline = false; + reverse = false; + return ttyutil; + } + + function off(ev, listener) { + if(ev === ttyutil.EVENT.SIGNAL) { + signal.off(ttyu, listener); + } else if(ev in Const.Event && listener instanceof Function) { + emitter.removeListener(ev, listener); + } + return ttyutil; + }; + + ttyutil.SIGNAL = { + SIGINT: "SIGINT", + SIGTERM: "SIGTERM", + SIGPIPE: "SIGPIPE", + SIGHUP: "SIGHUP" + }; + ttyutil.EVENT = Const.EventString; + ttyutil.MOUSE = Const.Mouse; + ttyutil.WHICH = Const.Which; + ttyutil.CTRL = Const.Ctrl; + ttyutil.MODE = Const.Mode; + + // event constructors + ttyutil.KeyEvent = function(which, ctrl) { + return { + type: ttyutil.EVENT.KEY, + which: which, + ctrl: ctrl + }; + }; + ttyutil.MouseEvent = function(type, button, x, y, ctrl) { + if(!(utils.startsWith(type, "MOUSE") && type in ttyutil.EVENT)) + throw new TypeError("type is no valid mouse event type"); + return { + type: type, + button: button, + x: x, + y: y, + ctrl: ctrl + }; + }; + ttyutil.SignalEvent = function(signal) { + return { + type: ttyutil.EVENT.SIGNAL, + signal: signal + }; + }; + + // handle exits to clean up terminal + // TODO(@bbuecherl): only handle exits when no other listener is registered + /*process.on("exit", ttyutil.stop); + process.on("uncaughtException", ttyutil.stop); + Object.keys(ttyutil.SIGNAL).forEach(function(sig) { + process.on(sig, ttyutil.stop); + });*/ + + return reset(); // initial reset +}; diff --git a/lib/signal.js b/lib/signal.js new file mode 100644 index 0000000..83d52e7 --- /dev/null +++ b/lib/signal.js @@ -0,0 +1,49 @@ +var signal = module.exports = { + on: function(ttyu, listener) { + if(!ttyu.__signals__) { + ttyu.__signals__ = {}; + ttyu.__siglisten__ = []; + + // add signal listeners + for(var sig in ttyu.SIGNAL) { + process.on(sig, (ttyu.__siglisten__[sig] = + signal.listen(ttyu, sig))); + } + } + ttyu.__siglisten__.push(listener); + }, + + off: function(ttyu, listener) { + if(!ttyu.__signals__) return; + var i = ttyu.__siglisten__.indexOf(listener); + if(i !== -1) { + ttyu.__siglisten__.splice(i,1); + } + + if(ttyu.__siglisten__.length === 0) { + // remove signal listeners + for(var sig in ttyu.__siglisten__) { + process.removeListener(sig, ttyu.__siglisten__[sig]); + } + + ttyu.__signals__ = undefined; + ttyu.__siglisten__ = undefined; + } + }, + + emit: function(sig) { + process.emit(sig); + }, + + listen: function(ttyu, sig) { + return function() { + var ev = { + type: ttyu.EVENT.SIGNAL, + signal: sig + }; + for(var i = 0; i < ttyu.__siglisten__.length; ++i) { + ttyu.__siglisten__[i].call(ttyu, ev); + } + }; + } +}; diff --git a/lib/utils.js b/lib/utils.js new file mode 100644 index 0000000..cac60aa --- /dev/null +++ b/lib/utils.js @@ -0,0 +1,20 @@ +var utils = module.exports = { + rgb: function(r, g, b) { + return utils.rgb5(r/51, g/51, b/51); + }, + + rgb5: function(r, g, b) { + return 16 + Math.round(r)*36 + Math.round(g)*6 + Math.round(b); + }, + + hex: function(c) { + c = c[0] === "#" ? c.substring(1) : c; + return utils.rgb(parseInt(c.substring(0, 2), 16), + parseInt(c.substring(2, 4), 16), parseInt(c.substring(4, 6), 16)); + }, + + startsWith: function(str, searchString, position) { + position = position || 0; + return str.indexOf(searchString, position) === position; + } +}; diff --git a/package.json b/package.json index 64a687d..66d539b 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "cross-platform terminal utilities", "main": "index.js", "scripts": { - "test": "./node_modules/.bin/mocha tests/run" + "test": "node tests/index.js" }, "repository": { "type": "git", @@ -25,15 +25,8 @@ }, "homepage": "https://github.com/clidejs/ttyutil", "dependencies": { - "nan": "1.7.0" - }, - "devDependencies": { - "node-gyp": "1.0.2", - "pangyp": "2.1.0", - "chai": "2.1.2", - "mocha": "2.2.1", - "node-is": "0.5.2", - "it-each": "0.3.1" + "gethub": "^2.0.1", + "nan": "1.8.4" }, "engines": { "node": ">=0.8.0" diff --git a/signal.js b/signal.js deleted file mode 100644 index 1a26a13..0000000 --- a/signal.js +++ /dev/null @@ -1,49 +0,0 @@ -var signal = module.exports = { - on: function(ttyu, that, listener) { - if(!that.__signals__) { - that.__signals__ = {}; - that.__siglisten__ = []; - - // add signal listeners - for(var sig in ttyu.TTYUtil.SIGNAL) { - process.on(sig, (that.__siglisten__[sig] = - signal.listen(ttyu, sig, that))); - } - } - that.__siglisten__.push(listener); - }, - - off: function(that, listener) { - if(!that.__signals__) return; - var i = that.__siglisten__.indexOf(listener); - if(i !== -1) { - that.__siglisten__.splice(i,1); - } - - if(that.__siglisten__.length === 0) { - // remove signal listeners - for(var sig in that.__siglisten__) { - process.removeListener(sig, that.__siglisten__[sig]); - } - - that.__signals__ = undefined; - that.__siglisten__ = undefined; - } - }, - - emit: function(sig) { - process.emit(sig); - }, - - listen: function(ttyu, sig, that) { - return function() { - var ev = { - type: ttyu.TTYUtil.EVENT.SIGNAL, - signal: sig - }; - for(var i = 0; i < that.__siglisten__.length; ++i) { - that.__siglisten__[i].call(that, ev); - } - }; - } -}; diff --git a/src/core/ttyu.cc b/src/core/ttyu.cc new file mode 100644 index 0000000..f939da2 --- /dev/null +++ b/src/core/ttyu.cc @@ -0,0 +1,68 @@ +/* ttyutil - ttyu.cc - implements additional functions + * https://github.com/clidejs/ttyutil + * + * Copyright Bernhard Bücherl + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include + +JSFUNCTION(ttyu_js_c::js_new, { + ttyu_js_c *obj = new ttyu_js_c(); + obj->Wrap(args.This()); + obj->emitter = new NanCallback(v8::Local::Cast(args[0])); + NanReturnThis(); +}) + +JSFUNCTION(ttyu_js_c, js_running, { + NanReturnValue(that->running ? NanTrue() : NanFalse()); +}) + +// initialize node module +void ttyu_js_c::init(DATATYPE ex, DATATYPE module) { + int _exports = 0; + DATATYPE tpl = NanNew(js_new); + tpl->SetClassName(NanNew("ttyu_js_c")); + + EXPORT_METHOD(tpl, "start", js_start); + EXPORT_METHOD(tpl, "stop", js_stop); + EXPORT_METHOD(tpl, "emit", js_emit); + EXPORT_METHOD(tpl, "write", js_write); + EXPORT_METHOD(tpl, "getrunning", js_running); + EXPORT_METHOD(tpl, "getwidth", js_getwidth); + EXPORT_METHOD(tpl, "getheight", js_getheight); + EXPORT_METHOD(tpl, "setwidth", js_setwidth); + EXPORT_METHOD(tpl, "setheight", js_setheight); + EXPORT_METHOD(tpl, "resize", js_resize); + EXPORT_METHOD(tpl, "getmode", js_mode); + EXPORT_METHOD(tpl, "getcolors", js_colors); + EXPORT_METHOD(tpl, "setx", js_setx); + EXPORT_METHOD(tpl, "getx", js_getx); + EXPORT_METHOD(tpl, "sety", js_sety); + EXPORT_METHOD(tpl, "gety", js_gety); + EXPORT_METHOD(tpl, "goto", js_goto); + EXPORT_METHOD(tpl, "beep", js_beep); + EXPORT_METHOD(tpl, "clear", js_clear); + EXPORT_METHOD(tpl, "hide", js_hide); + EXPORT_METHOD(tpl, "show", js_show); + + tpl->InstanceTemplate()->SetInternalFieldCount(_exports); + module->Set(NanNew("exports"), tpl->GetFunction()); +} +NODE_MODULE(ttyu, ttyu_js_c::init); diff --git a/src/ttyu_event.cc b/src/core/ttyu_event.cc similarity index 79% rename from src/ttyu_event.cc rename to src/core/ttyu_event.cc index 3c56c50..eb61982 100644 --- a/src/ttyu_event.cc +++ b/src/core/ttyu_event.cc @@ -23,29 +23,27 @@ */ #include -void ttyu_event_create_error(ttyu_event_t *event, const char *err) { +void ttyu_event_create_error(ttyu_event_t *event) { event->type = EVENT_ERROR; - event->err = err; event->key = NULL; event->mouse = NULL; } void ttyu_event_create_resize(ttyu_event_t *event) { event->type = EVENT_RESIZE; - event->err = NULL; event->key = NULL; event->mouse = NULL; } void ttyu_event_create_key(ttyu_event_t *event, int ctrl, char *c, int code, int which) { - char *ch = (char *)std::malloc(sizeof(char) * (strlen(c) + 1)); - memcpy(ch, c, sizeof(char) * strlen(c)); - ch[strlen(c)] = '\0'; + size_t len = strlen(c); + char *ch = ALLOC(char, len + 1); + memcpy(ch, c, sizeof(char) * len); + ch[len] = '\0'; event->type = EVENT_KEY; - event->err = NULL; - event->key = (ttyu_key_t *)std::malloc(sizeof(ttyu_key_t)); + event->key = ALLOC(ttyu_key_t, 1); event->mouse = NULL; event->key->ctrl = ctrl; @@ -54,12 +52,11 @@ void ttyu_event_create_key(ttyu_event_t *event, int ctrl, char *c, event->key->which = which; } -void ttyu_event_create_mouse(ttyu_event_t *event, int type, int button, - int x, int y, int ctrl) { +void ttyu_event_create_mouse(ttyu_event_t *event, int type, int button, int x, + int y, int ctrl) { event->type = type; - event->err = NULL; event->key = NULL; - event->mouse = (ttyu_mouse_t *)std::malloc(sizeof(ttyu_mouse_t)); + event->mouse = ALLOC(ttyu_mouse_t, 1); event->mouse->button = button; event->mouse->x = x; @@ -68,12 +65,12 @@ void ttyu_event_create_mouse(ttyu_event_t *event, int type, int button, } void ttyu_event_destroy(ttyu_event_t *event) { - if(event) { - if(event->mouse) { + if (event) { + if (event->mouse) { std::free(event->mouse); } - if(event->key) { - if(event->key->c) { + if (event->key) { + if (event->key->c) { std::free(event->key->c); } std::free(event->mouse); diff --git a/src/ttyu_js.cc b/src/ttyu_js.cc deleted file mode 100644 index f4e1187..0000000 --- a/src/ttyu_js.cc +++ /dev/null @@ -1,161 +0,0 @@ -/* ttyutil - ttyu_js.cc - implements the javascript module - * https://github.com/clidejs/ttyutil - * - * Copyright Bernhard Bücherl - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ -#include - -v8::Persistent ttyu_js_c::constructor; - -void ttyu_js_c::init(v8::Handle target) { - v8::Local tpl = - NanNew(new_instance); - tpl->SetClassName(NanNew("TTYUtil")); - tpl->InstanceTemplate()->SetInternalFieldCount(19); - - EXPORT_PROTOTYPE_METHOD(tpl, "pause", pause); - EXPORT_PROTOTYPE_METHOD(tpl, "destroy", destroy); - EXPORT_PROTOTYPE_METHOD(tpl, "start", start); - EXPORT_PROTOTYPE_METHOD_HIDDEN(tpl, "__on__", on); - EXPORT_PROTOTYPE_METHOD_HIDDEN(tpl, "__off__", off); - EXPORT_PROTOTYPE_METHOD_HIDDEN(tpl, "__emit__", emit); - EXPORT_PROTOTYPE_GET(tpl, "running", is_running); - - EXPORT_PROTOTYPE_GET(tpl, "width", get_width); - EXPORT_PROTOTYPE_GET(tpl, "height", get_height); - EXPORT_PROTOTYPE_GET(tpl, "mode", get_mode); - EXPORT_PROTOTYPE_GET(tpl, "colors", get_colors); - EXPORT_PROTOTYPE_GETSET(tpl, "x", getx, setx); - EXPORT_PROTOTYPE_GETSET(tpl, "y", gety, sety); - EXPORT_PROTOTYPE_METHOD(tpl, "goto", gotoxy); - EXPORT_PROTOTYPE_METHOD(tpl, "color", color); - EXPORT_PROTOTYPE_METHOD(tpl, "beep", beep); - EXPORT_PROTOTYPE_METHOD(tpl, "clear", clear); - EXPORT_PROTOTYPE_METHOD(tpl, "prepare", prepare); - EXPORT_PROTOTYPE_METHOD(tpl, "write", write); - - NanAssignPersistent(constructor, tpl->GetFunction()); - target->Set(NanNew("TTYUtil"), tpl->GetFunction()); -} - -ttyu_js_c::ttyu_js_c() { - running = FALSE; - paused = FALSE; - destroyed_ = FALSE; - - data = (ttyu_data_t *)malloc(sizeof(ttyu_data_t)); - ttyu_data_init(data); - - emitter = (ee_emitter_t *)malloc(sizeof(ee_emitter_t)); - ee_init(emitter, ttyu_ee_cb_call, ttyu_ee_compare); -} - -ttyu_js_c::~ttyu_js_c() { - destroy(); -} - -void ttyu_js_c::destroy() { - running = FALSE; - paused = FALSE; - destroyed_ = TRUE; - if(data) { - ttyu_data_destroy(data); - free(data); - } -} - -NAN_METHOD(ttyu_js_c::new_instance) { - NanScope(); - ttyu_js_c *obj = new ttyu_js_c(); - obj->throw_ = args[0]->IsBoolean() ? args[1]->BooleanValue() : TRUE; - obj->Wrap(args.This()); - NanReturnThis(); -} - -NAN_METHOD(ttyu_js_c::start) { - NanScope(); - ttyu_js_c *obj = ObjectWrap::Unwrap(args.This()); - TTYU_THROW_IF_DESTROYED(obj); - if(!obj->running) { - obj->running = TRUE; -#ifndef PLATFORM_WINDOWS -// uv_barrier_init(&(obj->barrier), 2); -#endif - //ttyu_worker_c *w = new ttyu_worker_c(obj); - //NanAsyncQueueWorker(w); - //obj->worker_ = w; - -#ifndef PLATFORM_WINDOWS -// uv_barrier_wait(&(obj->barrier)); -// uv_barrier_destroy(&(obj->barrier)); -#endif - } else if(obj->paused) { - obj->paused = FALSE; - } - NanReturnThis(); -} - -NAN_METHOD(ttyu_js_c::pause) { - NanScope(); - ttyu_js_c *obj = ObjectWrap::Unwrap(args.This()); - TTYU_THROW_IF_DESTROYED(obj); - if(obj->running && !obj->paused) { - obj->paused = TRUE; - } - NanReturnThis(); -} - -NAN_METHOD(ttyu_js_c::destroy) { - NanScope(); - ttyu_js_c *obj = ObjectWrap::Unwrap(args.This()); - TTYU_THROW_IF_DESTROYED(obj); - if(obj->running) { - obj->destroy(); - } - NanReturnThis(); -} - -NAN_GETTER(ttyu_js_c::is_running) { - NanScope(); - ttyu_js_c *obj = ObjectWrap::Unwrap(args.This()); - NanReturnValue(NanNew(obj->running && !obj->paused)); -} - -NAN_METHOD(ttyu_js_c::on) { - NanScope(); - ttyu_js_c *obj = ObjectWrap::Unwrap(args.This()); - TTYU_THROW_IF_DESTROYED(obj); - ee_on(obj->emitter, args[0]->Int32Value(), - new NanCallback(v8::Local::Cast(args[1]))); - NanReturnThis(); -} - -NAN_METHOD(ttyu_js_c::off) { - NanScope(); - ttyu_js_c *obj = ObjectWrap::Unwrap(args.This()); - TTYU_THROW_IF_DESTROYED(obj); - ee_off(obj->emitter, args[0]->Int32Value(), - new NanCallback(v8::Local::Cast(args[1]))); - NanReturnThis(); -} - -// export -NODE_MODULE(ttyu, ttyu_js_c::init); diff --git a/src/ttyu_worker.cc b/src/ttyu_worker.cc deleted file mode 100644 index d20a2e6..0000000 --- a/src/ttyu_worker.cc +++ /dev/null @@ -1,159 +0,0 @@ -/* ttyutil - ttyu_worker.cc - implements the background (async) worker - * https://github.com/clidejs/ttyutil - * - * Copyright Bernhard Bücherl - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ -#include - -void ttyu_worker_c::ttyu_progress_c::send(const ttyu_event_t *event) const { - that_->send_(event); -} - -ttyu_worker_c::ttyu_progress_c::ttyu_progress_c(ttyu_worker_c *that) : - that_(that) {} - -ttyu_worker_c::ttyu_worker_c(ttyu_js_c *obj) : - NanAsyncWorker(NULL), asyncdata_(NULL), obj_(obj) { - async = new uv_async_t; - async_lock = new uv_mutex_t; - uv_mutex_init(async_lock); - uv_async_init(uv_default_loop(), async, async_progress_); - async->data = this; -} - -ttyu_worker_c::~ttyu_worker_c() { - uv_mutex_destroy(async_lock); - ttyu_event_destroy(asyncdata_); - free(asyncdata_); -} - -void ttyu_worker_c::progress() { - uv_mutex_lock(async_lock); - ttyu_event_t *event = asyncdata_; - asyncdata_ = NULL; - uv_mutex_unlock(async_lock); - - if(event) { - handle(event); - } - ttyu_event_destroy(event); - free(event); -} - -void ttyu_worker_c::handle(ttyu_event_t *event) { - if(ee_count(obj_->emitter, event->type) == 0 || obj_->paused || - !obj_->running) { - return; - } - - v8::Local obj = NanNew(); - switch(event->type) { - case EVENT_RESIZE: - obj->Set(NanNew("type"), EVENTSTRING_RESIZE); - break; - case EVENT_KEY: - obj->Set(NanNew("type"), EVENTSTRING_KEY); - obj->Set(NanNew("ctrl"), - NanNew(event->key->ctrl)); - obj->Set(NanNew("char"), NanNew(event->key->c)); - obj->Set(NanNew("code"), - NanNew(event->key->code)); - obj->Set(NanNew("which"), - NanNew(event->key->which)); - break; - case EVENT_MOUSEDOWN: - case EVENT_MOUSEUP: - case EVENT_MOUSEMOVE: - case EVENT_MOUSEWHEEL: - case EVENT_MOUSEHWHEEL: - if(event->type == EVENT_MOUSEDOWN) { - obj->Set(NanNew("type"), EVENTSTRING_MOUSEDOWN); - } else if(event->type == EVENT_MOUSEUP) { - obj->Set(NanNew("type"), EVENTSTRING_MOUSEUP); - } else if(event->type == EVENT_MOUSEMOVE) { - obj->Set(NanNew("type"), EVENTSTRING_MOUSEMOVE); - } else if(event->type == EVENT_MOUSEWHEEL) { - obj->Set(NanNew("type"), EVENTSTRING_MOUSEWHEEL); - } else if(event->type == EVENT_MOUSEHWHEEL) { - obj->Set(NanNew("type"), EVENTSTRING_MOUSEHWHEEL); - } - obj->Set(NanNew("button"), - NanNew(event->mouse->button)); - obj->Set(NanNew("x"), NanNew(event->mouse->x)); - obj->Set(NanNew("y"), NanNew(event->mouse->y)); - obj->Set(NanNew("ctrl"), - NanNew(event->mouse->ctrl)); - break; - default: // EVENT_ERROR, EVENT_SIGNAL - obj->Set(NanNew("type"), EVENTSTRING_ERROR); - obj->Set(NanNew("error"), NanError(event->err)); - event->type = EVENT_ERROR; - break; - } - ee_emit(obj_->emitter, event->type, obj); -} - -void ttyu_worker_c::Destroy() { - uv_close(reinterpret_cast(async), async_close_); -} - -void ttyu_worker_c::Execute() { - ttyu_progress_c progress(this); -#ifndef PLATFORM_WINDOWS - uv_barrier_wait(&(obj_->barrier)); -#endif - // loop execute until it returns false (error) - while(execute(progress, obj_->data)); -} - -void ttyu_worker_c::send_(const ttyu_event_t *event) { - ttyu_event_t *new_event = (ttyu_event_t *)malloc(sizeof(event)); - memcpy(&new_event, &event, sizeof(event)); - uv_mutex_lock(async_lock); - ttyu_event_t *old_event = asyncdata_; - asyncdata_ = new_event; - uv_mutex_unlock(async_lock); - - ttyu_event_destroy(old_event); - free(old_event); - uv_async_send(async); -} - -NAUV_WORK_CB(ttyu_worker_c::async_progress_) { - ttyu_worker_c *worker = static_cast(async->data); - worker->progress(); -} - -void ttyu_worker_c::async_close_(uv_handle_t *handle) { - ttyu_worker_c *worker = static_cast(handle->data); - delete reinterpret_cast(handle); - - if(worker->obj_->emitter) { - ee_destroy(worker->obj_->emitter); - delete worker->obj_->emitter; - } - - delete worker; -} - -void ttyu_worker_c::WorkComplete() { - // do nothing -} diff --git a/src/unix.cc b/src/unix.cc deleted file mode 100644 index fd44755..0000000 --- a/src/unix.cc +++ /dev/null @@ -1,427 +0,0 @@ -/* ttyutil - unix.cc - implements methods for unixy systems (see include/unix.h) - * https://github.com/clidejs/ttyutil - * - * Copyright Bernhard Bücherl - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ -#include - -void ttyu_data_init(ttyu_data_t *data) { - data->win = initscr(); - data->closing = FALSE; - data->mode = MODE_VT100; - -// data->ungetch_stack = new std::queue< int, std::list >; -// data->ungetmouse_stack = new std::queue< MEVENT, std::list >; - - noecho(); - cbreak(); - keypad(data->win, TRUE); - mousemask(ALL_MOUSE_EVENTS | REPORT_MOUSE_POSITION, &(data->old_mouse_mask)); - mouseinterval(0); - nodelay(data->win, TRUE); -} - -void ttyu_data_destroy(ttyu_data_t *data) { - data->closing = TRUE; - echo(); - while(!data->ungetch_stack.empty()) data->ungetch_stack.pop(); - while(!data->ungetmouse_stack.empty()) data->ungetmouse_stack.pop(); - data->ungetch_stack.push(TTYU_EXIT); - endwin(); -} - -bool ttyu_worker_c::execute(const ttyu_worker_c::ttyu_progress_c& progress, - ttyu_data_t *data) { - int c = getch(); - - if(c == TTYU_EXIT) { return FALSE; } - if(data->closing) { return FALSE; } - - if(c == ERR) { - if(data->ungetch_stack.empty()) { - if(!(data->ungetmouse_stack.empty())) { - // work on mouse stack - //ungetmouse(&(data->ungetmouse_stack->pop())); - } - } else { - // work on the char stack - c = *data->ungetch_stack.pop(); - ungetch(c == -1 ? TTYU_UNKNOWN : c); - } - return TRUE; - } - - MEVENT mev; - ttyu_event_t *event = (ttyu_event_t *)malloc(sizeof(ttyu_event_t)); - - if(c == KEY_RESIZE) { - ttyu_event_create_resize(event); - progress.send(const_cast(event)); - } else if(c == KEY_MOUSE) { - if(getmouse(&mev) == OK) { - ttyu_event_create_mouse(event, EVENT_ERROR, 0, mev.x, mev.y, 0); - - // add button control key sequences if possible - if(mev.bstate & BUTTON_SHIFT) { event->mouse->ctrl |= CTRL_SHIFT; } - if(mev.bstate & BUTTON_CTRL) { event->mouse->ctrl |= CTRL_CTRL; } - if(mev.bstate & BUTTON_ALT) { event->mouse->ctrl |= CTRL_ALT; } - - // convert button codes and mev type - if(mev.bstate & REPORT_MOUSE_POSITION) { - event->type = EVENT_MOUSEMOVE; - - } else if(mev.bstate & BUTTON1_RELEASED) { - event->mouse->button = MOUSE_LEFT; - event->type = EVENT_MOUSEUP; - } else if(mev.bstate & BUTTON1_PRESSED) { - event->mouse->button = MOUSE_LEFT; - event->type = EVENT_MOUSEDOWN; - - } else if(mev.bstate & BUTTON2_RELEASED) { - event->mouse->button = MOUSE_LEFT2; - event->type = EVENT_MOUSEUP; - } else if(mev.bstate & BUTTON2_PRESSED) { - event->mouse->button = MOUSE_LEFT2; - event->type = EVENT_MOUSEDOWN; - - } else if(mev.bstate & BUTTON3_RELEASED) { - event->mouse->button = MOUSE_LEFT3; - event->type = EVENT_MOUSEUP; - } else if(mev.bstate & BUTTON3_PRESSED) { - event->mouse->button = MOUSE_LEFT3; - event->type = EVENT_MOUSEDOWN; - } else -#if NCURSES_MOUSE_VERSION > 1 - if(mev.bstate & BUTTON4_RELEASED) { - event->mouse->button = MOUSE_LEFT4; - event->type = EVENT_MOUSEUP; - } else if(mev.bstate & BUTTON4_PRESSED) { - event->mouse->button = MOUSE_LEFT4; - event->type = EVENT_MOUSEDOWN; - - } else if(mev.bstate & BUTTON5_RELEASED) { - event->mouse->button = MOUSE_RIGHT; - event->type = EVENT_MOUSEUP; - } else if(mev.bstate & BUTTON5_PRESSED) { - event->mouse->button = MOUSE_RIGHT; - event->type = EVENT_MOUSEDOWN; - } -#else - if(mev.bstate & BUTTON4_RELEASED) { - event->mouse->button = MOUSE_RIGHT; - event->type = EVENT_MOUSEUP; - } else if(mev.bstate & BUTTON4_PRESSED) { - event->mouse->button = MOUSE_RIGHT; - event->type = EVENT_MOUSEDOWN; - } -#endif - if(event->type == EVENT_ERROR) { - // uncaught mouse event - ttyu_event_destroy(event); - ttyu_event_create_error(event, ERROR_UNIX_MOUSEUNCAUGHT); - } else { - // its VT102 - data->mode = MODE_VT102; - } - } else { - // bad mouse event - ttyu_event_create_error(event, ERROR_UNIX_MOUSEBAD); - } - progress.send(const_cast(event)); - } else { - char *ch = const_cast(keyname(c)); - int ctrl = CTRL_NULL; - int which = WHICH_UNKNOWN; - - if(c >= 48 && c <= 57) { - which = c; // WHICH_CHAR0 to WHICH_CHAR9 - } else if(c >= 65 && c <= 90) { - ctrl |= CTRL_SHIFT; - which = c; // WHICH_CHARA to WHICH_CHARZ - } else if(c >= 97 && c <= 122) { - which = c - 32; // WHICH_CHARA to WHICH_CHARZ - } else if(c == KEY_COMMAND) { - ctrl |= CTRL_CMD; - } else if(c == KEY_SCOMMAND) { - ctrl |= CTRL_CMD | CTRL_SHIFT; - } else { - which = ttyu_unix_which(c); - //if(ch[0] == '^') { - // ctrl |= CTRL_CTRL; - //} - } - - ttyu_event_create_key(event, ctrl, ch, c, which); - progress.send(const_cast(event)); - } - return TRUE; -} - -void ttyu_unix_clrscr(ttyu_data_t *data, int x, int y, int width, int height) { - if(x == 0 && y == 0 && width == COLS && height == LINES) { - wclear(data->win); - } else if(width != 0 || height != 0 || x < COLS || y < LINES) { - int ox = data->win->_curx; - int oy = data->win->_cury; - wmove(data->win, x, y); - - for(int j = 0; j < height; ++j) { - for(int i = 0; i < width; ++i) { - std::cout << " "; - } - std::cout << "\r\n"; - } - wmove(data->win, ox, oy); - } -} - -int ttyu_unix_key(int which) { - #define XXKEY(w, key, shift) if(w == which) { \ - return key; \ - } - TTYU_UNIX_KW(XXKEY); - return WHICH_UNKNOWN; -} - -int ttyu_unix_which(int key) { - #define XXWHICH(which, k, shift) if(k == key) { \ - return which; \ - } - TTYU_UNIX_KW(XXWHICH); - return WHICH_UNKNOWN; -} - -NAN_METHOD(ttyu_js_c::emit) { - NanScope(); - ttyu_js_c *obj = ObjectWrap::Unwrap(args.This()); - TTYU_THROW_IF_NOT_RUNNING(obj); - if(obj->running) { - int ev = args[0]->Int32Value(); - - switch(ev) { - case EVENT_KEY: { - int c = -1; - int which = args[1]->Int32Value(); - int ctrl = args[2]->Int32Value(); - if(ctrl & CTRL_CMD) { - c = KEY_COMMAND; - } else if(which >= 65 && which <= 90) { - if(ctrl & CTRL_SHIFT) { - c = which; // A-Z - } else { - c = which + 32; // a-z - } - } else if(which >= 48 && which <= 57) { - c = which; // 0 - 9 - } else { - c = ttyu_unix_key(which); - } - obj->data->ungetch_stack.push(c); - } break; - case EVENT_MOUSEDOWN: - case EVENT_MOUSEUP: - case EVENT_MOUSEMOVE: { - MEVENT mev; - int b = args[1]->Int32Value(); - int ctrl = args[4]->Int32Value(); - - mev.x = (short)args[2]->Int32Value(); - mev.y = (short)args[3]->Int32Value(); - mev.bstate = 0; - - if(ctrl & CTRL_ALT) { mev.bstate |= BUTTON_ALT; } - if(ctrl & CTRL_CTRL) { mev.bstate |= BUTTON_CTRL; } - if(ctrl & CTRL_SHIFT) { mev.bstate |= BUTTON_SHIFT; } - - if(ev == EVENT_MOUSEMOVE) { - mev.bstate |= REPORT_MOUSE_POSITION; - } else if(ev == EVENT_MOUSEUP) { - if(b == MOUSE_LEFT) { - mev.bstate |= BUTTON1_RELEASED; - } else if(b == MOUSE_LEFT2) { - mev.bstate |= BUTTON2_RELEASED; - } else if(b == MOUSE_LEFT3) { - mev.bstate |= BUTTON3_RELEASED; - } else if(b == MOUSE_LEFT4) { - mev.bstate |= BUTTON4_RELEASED; - } else if(b == MOUSE_RIGHT) { -#if NCURSES_MOUSE_VERSION > 1 - mev.bstate |= BUTTON5_RELEASED; -#else - mev.bstate |= BUTTON4_RELEASED; -#endif - } - } else if(ev == EVENT_MOUSEDOWN) { - if(b == MOUSE_LEFT) { - mev.bstate |= BUTTON1_PRESSED; - } else if(b == MOUSE_LEFT2) { - mev.bstate |= BUTTON2_PRESSED; - } else if(b == MOUSE_LEFT3) { - mev.bstate |= BUTTON3_PRESSED; - } else if(b == MOUSE_LEFT4) { - mev.bstate |= BUTTON4_PRESSED; - } else if(b == MOUSE_RIGHT) { -#if NCURSES_MOUSE_VERSION > 1 - mev.bstate |= BUTTON5_PRESSED; -#else - mev.bstate |= BUTTON4_PRESSED; -#endif - } - } - - obj->data->ungetmouse_stack.push(mev); - } break; - default: // EVENT_ERROR, EVENT_SIGNAL, EVENT_RESIZE, EVENT_MOUSEWHEEL, - // EVENT_MOUSEHWHEEL - // do nothing - break; - } - } - NanReturnUndefined(); -} - -NAN_GETTER(ttyu_js_c::get_width) { - NanScope(); - ttyu_js_c *obj = ObjectWrap::Unwrap(args.This()); - TTYU_THROW_IF_NOT_RUNNING(obj); - NanReturnValue(NanNew(COLS)); -} - -NAN_GETTER(ttyu_js_c::get_height) { - NanScope(); - ttyu_js_c *obj = ObjectWrap::Unwrap(args.This()); - TTYU_THROW_IF_NOT_RUNNING(obj); - NanReturnValue(NanNew(LINES)); -} - -NAN_GETTER(ttyu_js_c::get_mode) { - NanScope(); - ttyu_js_c *obj = ObjectWrap::Unwrap(args.This()); - TTYU_THROW_IF_NOT_RUNNING(obj); - NanReturnValue(NanNew(obj->data->mode)); -} - -NAN_GETTER(ttyu_js_c::get_colors) { - NanScope(); - ttyu_js_c *obj = ObjectWrap::Unwrap(args.This()); - TTYU_THROW_IF_NOT_RUNNING(obj); - NanReturnValue(NanNew(COLORS)); -} - -NAN_GETTER(ttyu_js_c::getx) { - NanScope(); - ttyu_js_c *obj = ObjectWrap::Unwrap(args.This()); - TTYU_THROW_IF_NOT_RUNNING(obj); - NanReturnValue(NanNew(obj->data->win->_curx)); -} - -NAN_SETTER(ttyu_js_c::setx) { - NanScope(); - ttyu_js_c *obj = ObjectWrap::Unwrap(args.This()); - TTYU_THROW_IF_NOT_RUNNING_VOID(obj); - obj->data->win->_curx = args.Data()->Int32Value(); -} - -NAN_GETTER(ttyu_js_c::gety) { - NanScope(); - ttyu_js_c *obj = ObjectWrap::Unwrap(args.This()); - TTYU_THROW_IF_NOT_RUNNING(obj); - NanReturnValue(NanNew(obj->data->win->_cury)); -} - -NAN_SETTER(ttyu_js_c::sety) { - NanScope(); - ttyu_js_c *obj = ObjectWrap::Unwrap(args.This()); - TTYU_THROW_IF_NOT_RUNNING_VOID(obj); - obj->data->win->_cury = args.Data()->Int32Value(); -} - -NAN_METHOD(ttyu_js_c::gotoxy) { - NanScope(); - ttyu_js_c *obj = ObjectWrap::Unwrap(args.This()); - TTYU_THROW_IF_NOT_RUNNING(obj); - wmove(obj->data->win, args[1]->Int32Value(), args[0]->Int32Value()); - NanReturnThis(); -} - -NAN_METHOD(ttyu_js_c::write) { - NanScope(); - ttyu_js_c *obj = ObjectWrap::Unwrap(args.This()); - TTYU_THROW_IF_NOT_RUNNING(obj); - v8::String::Utf8Value *ch = new v8::String::Utf8Value(args[0]->ToString()); - int fg = args[1]->IsNumber() ? args[1]->Int32Value() : ( - args[1]->IsString() ? util_color( - (new v8::String::Utf8Value(args[1]->ToString()))->operator*()) : - 1); - int bg = args[2]->IsNumber() ? args[2]->Int32Value() : ( - args[2]->IsString() ? util_color( - (new v8::String::Utf8Value(args[2]->ToString()))->operator*()) : - 1); - printf("%s", util_render(ch->operator*(), fg, bg)); - refresh(); - NanReturnThis(); -} - -NAN_METHOD(ttyu_js_c::beep) { - NanScope(); - ttyu_js_c *obj = ObjectWrap::Unwrap(args.This()); - TTYU_THROW_IF_NOT_RUNNING(obj); - ::beep(); - NanReturnThis(); -} - -NAN_METHOD(ttyu_js_c::clear) { - NanScope(); - ttyu_js_c *obj = ObjectWrap::Unwrap(args.This()); - TTYU_THROW_IF_NOT_RUNNING(obj); - - if(args[0]->IsNumber() && args[1]->IsNumber() && args[2]->IsNumber() && - args[3]->IsNumber()) { - ttyu_unix_clrscr(obj->data, - util_max(args[0]->Int32Value(), 0), // x - util_max(args[1]->Int32Value(), 0), // y - util_min(args[2]->Int32Value(), COLS), // width - util_min(args[3]->Int32Value(), LINES));// height - } else { - ttyu_unix_clrscr(obj->data, 0, 0, COLS, LINES); - } - NanReturnThis(); -} - -NAN_METHOD(ttyu_js_c::prepare) { - NanScope(); - ttyu_js_c *obj = ObjectWrap::Unwrap(args.This()); - TTYU_THROW_IF_NOT_RUNNING(obj); - v8::String::Utf8Value *ch = new v8::String::Utf8Value(args[0]->ToString()); - int fg = args[1]->IsNumber() ? args[1]->Int32Value() : ( - args[1]->IsString() ? util_color( - (new v8::String::Utf8Value(args[1]->ToString()))->operator*()) : - 1); - int bg = args[2]->IsNumber() ? args[2]->Int32Value() : ( - args[2]->IsString() ? util_color( - (new v8::String::Utf8Value(args[2]->ToString()))->operator*()) : - 1); - NanReturnValue(NanNew(util_render(ch->operator*(), fg, bg))); -} - -NAN_METHOD(ttyu_js_c::color) { - NanScope(); - ttyu_js_c *obj = ObjectWrap::Unwrap(args.This()); - TTYU_THROW_IF_NOT_RUNNING(obj); - v8::String::Utf8Value *ch = new v8::String::Utf8Value(args[0]->ToString()); - NanReturnValue(NanNew(util_color(ch->operator*()))); -} diff --git a/src/unix/beep.cc b/src/unix/beep.cc new file mode 100644 index 0000000..e0b577c --- /dev/null +++ b/src/unix/beep.cc @@ -0,0 +1,29 @@ +/* ttyutil - unix/beep.cc + * https://github.com/clidejs/ttyutil + * + * Copyright Bernhard Bücherl + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include + +JSFUNCTION(ttyu_js_c, js_beep, { + THROW_IF_STOPPED(that); + beep(); +}) diff --git a/src/unix/clear.cc b/src/unix/clear.cc new file mode 100644 index 0000000..9f8dc3b --- /dev/null +++ b/src/unix/clear.cc @@ -0,0 +1,45 @@ +/* ttyutil - unix/clear.cc + * https://github.com/clidejs/ttyutil + * + * Copyright Bernhard Bücherl + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include + +JSFUNCTION(ttyu_js_c, js_clear, { + THROW_IF_STOPPED(that); + if (args.Length() == 4) { + int x = args[0]->Int32Value(); + int y = args[1]->Int32Value(); + int w = args[2]->Int32Value(); + int h = args[3]->Int32Value(); + wmove(that->win, x, y); + for (int j = 0; j < h; ++j) { + for (int i = 0; i < w; ++i) { + std::cout << " "; + } + } + std::cout << "\r\n"; + } else { + werase(that->win); + } + wmove(that->win, that->x, that->y); + wrefresh(that->win); +}); diff --git a/src/unix/colors.cc b/src/unix/colors.cc new file mode 100644 index 0000000..7f3de57 --- /dev/null +++ b/src/unix/colors.cc @@ -0,0 +1,28 @@ +/* ttyutil - unix/colors.cc + * https://github.com/clidejs/ttyutil + * + * Copyright Bernhard Bücherl + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include + +JSFUNCTION(ttyu_js_c, js_colors, { + NanReturnValue(NanNew(that->colors)); +}) diff --git a/src/unix/curses.cc b/src/unix/curses.cc new file mode 100644 index 0000000..dc016be --- /dev/null +++ b/src/unix/curses.cc @@ -0,0 +1,221 @@ +/* ttyutil - unix/curses.cc + * https://github.com/clidejs/ttyutil + * + * Copyright Bernhard Bücherl + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include + +int ttyu_js_c::curses_threaded_func(WINDOW *win, ttyu_js_c *obj) { + int c = wgetch(win); + MEVENT mev; + ttyu_event_t event; + event.type = EVENT_NONE; + + if (c == ERR) { + uv_mutex_lock(obj->ungetlock); + if (!obj->unget_stack.empty()) { + ttyu_event_t ev = obj->unget_stack.front(); + obj->unget_stack.pop(); + uv_mutex_unlock(obj->ungetlock); + switch (ev.type) { + case EVENT_KEY: + ungetch(ev.key->code); + break; + case EVENT_MOUSEUP: + case EVENT_MOUSEDOWN: + case EVENT_MOUSEMOVE: + mev.x = ev.mouse->x; + mev.y = ev.mouse->y; + mev.bstate = 0; + + if (ev.mouse->ctrl & CTRL_ALT) { mev.bstate |= BUTTON_ALT; } + if (ev.mouse->ctrl & CTRL_CTRL) { mev.bstate |= BUTTON_CTRL; } + if (ev.mouse->ctrl & CTRL_SHIFT) { mev.bstate |= BUTTON_SHIFT; } + + if (ev.type == EVENT_MOUSEMOVE) { + mev.bstate |= REPORT_MOUSE_POSITION; + } else if (ev.type == EVENT_MOUSEUP) { + if (ev.mouse->button == MOUSE_LEFT) { + mev.bstate |= BUTTON1_RELEASED; + } else if (ev.mouse->button == MOUSE_LEFT2) { + mev.bstate |= BUTTON2_RELEASED; + } else if (ev.mouse->button == MOUSE_LEFT3) { + mev.bstate |= BUTTON3_RELEASED; + } else if (ev.mouse->button == MOUSE_LEFT4) { + mev.bstate |= BUTTON4_RELEASED; + } else if (ev.mouse->button == MOUSE_RIGHT) { +#if NCURSES_MOUSE_VERSION > 1 + mev.bstate |= BUTTON5_RELEASED; +#else + mev.bstate |= BUTTON4_RELEASED; +#endif + } + } else if (ev.type == EVENT_MOUSEDOWN) { + if (ev.mouse->button == MOUSE_LEFT) { + mev.bstate |= BUTTON1_PRESSED; + } else if (ev.mouse->button == MOUSE_LEFT2) { + mev.bstate |= BUTTON2_PRESSED; + } else if (ev.mouse->button == MOUSE_LEFT3) { + mev.bstate |= BUTTON3_PRESSED; + } else if (ev.mouse->button == MOUSE_LEFT4) { + mev.bstate |= BUTTON4_PRESSED; + } else if (ev.mouse->button == MOUSE_RIGHT) { +#if NCURSES_MOUSE_VERSION > 1 + mev.bstate |= BUTTON5_PRESSED; +#else + mev.bstate |= BUTTON4_PRESSED; +#endif + } + } + ungetmouse(&mev); + break; + case EVENT_MOUSEWHEEL: + case EVENT_MOUSEHWHEEL: + // TODO(@bbuecherl) + break; + default: + // EVENT_NONE, EVENT_ERROR, EVENT_SIGNAL, EVENT_RESIZE + break; + } + } else { + uv_mutex_unlock(obj->ungetlock); + } + return 0; + } else if (c == KEY_RESIZE) { + ttyu_event_create_resize(&event); + } else if (c == KEY_MOUSE) { + if (getmouse(&mev) == OK) { + ttyu_event_create_mouse(&event, EVENT_ERROR, 0, mev.x, mev.y, 0); + + // add button control key sequences if possible + if (mev.bstate & BUTTON_SHIFT) { event.mouse->ctrl |= CTRL_SHIFT; } + if (mev.bstate & BUTTON_CTRL) { event.mouse->ctrl |= CTRL_CTRL; } + if (mev.bstate & BUTTON_ALT) { event.mouse->ctrl |= CTRL_ALT; } + + // convert button codes and mev type + if (mev.bstate & REPORT_MOUSE_POSITION) { + event.type = EVENT_MOUSEMOVE; + } else if (mev.bstate & BUTTON1_RELEASED) { + event.mouse->button = MOUSE_LEFT; + event.type = EVENT_MOUSEUP; + } else if (mev.bstate & BUTTON1_PRESSED) { + event.mouse->button = MOUSE_LEFT; + event.type = EVENT_MOUSEDOWN; + } else if (mev.bstate & BUTTON2_RELEASED) { + event.mouse->button = MOUSE_LEFT2; + event.type = EVENT_MOUSEUP; + } else if (mev.bstate & BUTTON2_PRESSED) { + event.mouse->button = MOUSE_LEFT2; + event.type = EVENT_MOUSEDOWN; + } else if (mev.bstate & BUTTON3_RELEASED) { + event.mouse->button = MOUSE_LEFT3; + event.type = EVENT_MOUSEUP; + } else if (mev.bstate & BUTTON3_PRESSED) { + event.mouse->button = MOUSE_LEFT3; + event.type = EVENT_MOUSEDOWN; + } else { +#if NCURSES_MOUSE_VERSION > 1 + if (mev.bstate & BUTTON4_RELEASED) { + event.mouse->button = MOUSE_LEFT4; + event.type = EVENT_MOUSEUP; + } else if (mev.bstate & BUTTON4_PRESSED) { + event.mouse->button = MOUSE_LEFT4; + event.type = EVENT_MOUSEDOWN; + } else if (mev.bstate & BUTTON5_RELEASED) { + event.mouse->button = MOUSE_RIGHT; + event.type = EVENT_MOUSEUP; + } else if (mev.bstate & BUTTON5_PRESSED) { + event.mouse->button = MOUSE_RIGHT; + event.type = EVENT_MOUSEDOWN; + } +#else + if (mev.bstate & BUTTON4_RELEASED) { + event.mouse->button = MOUSE_RIGHT; + event.type = EVENT_MOUSEUP; + } else if (mev.bstate & BUTTON4_PRESSED) { + event.mouse->button = MOUSE_RIGHT; + event.type = EVENT_MOUSEDOWN; + } +#endif + } + if (event.type == EVENT_ERROR) { + // uncaught mouse event + event.type = EVENT_NONE; + } else { + obj->mode = MODE_VT102; + } + } else { + // bad mouse event + event.type = EVENT_NONE; + } + } else { + char *ch = const_cast(keyname(c)); + int ctrl = CTRL_NULL; + int which = WHICH_UNKNOWN; + + if (c >= 48 && c <= 57) { + which = c; // WHICH_CHAR0 to WHICH_CHAR9 + } else if (c >= 65 && c <= 90) { + ctrl |= CTRL_SHIFT; + which = c; // WHICH_CHARA to WHICH_CHARZ + } else if (c >= 97 && c <= 122) { + which = c - 32; // WHICH_CHARA to WHICH_CHARZ + } else if (c == KEY_COMMAND) { + ctrl |= CTRL_CMD; + } else if (c == KEY_SCOMMAND) { + ctrl |= CTRL_CMD | CTRL_SHIFT; + } else { + which = ttyu_unix_which(c); + if (sizeof(ch) > 4 && ch[0] == '^') { + ctrl |= CTRL_CTRL; + } + } + ttyu_event_create_key(&event, ctrl, ch, c, which); + } + + if (event.type != EVENT_NONE) { + MUTEX_LOCK(obj->emitstacklock, { + obj->emit_stack.push_back(event); + uv_cond_signal(obj->condition); + }); + } + return 0; +} + +int ttyu_js_c::curses_threaded_func_thread(WINDOW *win, void *that) { + ttyu_js_c *obj = static_cast(that); + uv_barrier_wait(obj->barrier); + while (curses_threaded_func(win, obj) == 0 && obj->running) usleep(100); + return 0; +} + +void ttyu_js_c::curses_thread_func(void *that) { + ttyu_js_c *obj = static_cast(that); + + noecho(); + cbreak(); + keypad(obj->win, TRUE); + mousemask(ALL_MOUSE_EVENTS | REPORT_MOUSE_POSITION, NULL); + nodelay(obj->win, TRUE); + mouseinterval(FALSE); + + use_window(obj->win, curses_threaded_func_thread, that); +} diff --git a/src/unix/cursor.cc b/src/unix/cursor.cc new file mode 100644 index 0000000..2c5891b --- /dev/null +++ b/src/unix/cursor.cc @@ -0,0 +1,34 @@ +/* ttyutil - unix/cursor.cc + * https://github.com/clidejs/ttyutil + * + * Copyright Bernhard Bücherl + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include + +JSFUNCTION(ttyu_js_c, js_hide, { + THROW_IF_STOPPED(that); + // TODO(@bbuecherl) +}) + +JSFUNCTION(ttyu_js_c, js_show, { + THROW_IF_STOPPED(that); + // TODO(@bbuecherl) +}) diff --git a/src/unix/emit.cc b/src/unix/emit.cc new file mode 100644 index 0000000..193a30e --- /dev/null +++ b/src/unix/emit.cc @@ -0,0 +1,70 @@ +/* ttyutil - unix/emit.cc + * https://github.com/clidejs/ttyutil + * + * Copyright Bernhard Bücherl + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include + +JSFUNCTION(ttyu_js_c, js_emit, { + THROW_IF_STOPPED(that); + int arg0 = args[0]->Int32Value(); + int arg1 = args[1]->Int32Value(); + int arg2 = args[2]->Int32Value(); + int arg3 = args[3]->Int32Value(); + int arg4 = args[4]->Int32Value(); + ttyu_event_t event; + switch (arg0) { + case EVENT_KEY: { + int c = -1; + char ch = 0x20; + if (arg2 & CTRL_CMD) { + c = KEY_COMMAND; + } else if (arg1 >= 65 && arg1 <= 90) { + if (arg2 & CTRL_SHIFT) { + c = arg1; // A-Z + } else { + c = arg1 + 32; // a-z + } + } else if (arg1 >= 48 && arg1 <= 57) { + c = arg1; // 0 - 9 + } else { + c = ttyu_unix_key(arg1); + } // TODO(@bbuecherl) v + ttyu_event_create_key(&event, arg2, &ch, c, arg1); + } break; + case EVENT_MOUSEDOWN: + case EVENT_MOUSEUP: + case EVENT_MOUSEMOVE: { + ttyu_event_create_mouse(&event, arg0, arg1, arg2, arg3, arg4); + } break; + default: + // EVENT_NONE, EVENT_ERROR, EVENT_SIGNAL, EVENT_RESIZE, EVENT_MOUSEWHEEL, + // EVENT_MOUSEHWHEEL + event.type = EVENT_NONE; + break; + } + + if (event.type != EVENT_NONE) { + MUTEX_LOCK(that->ungetlock, { + that->unget_stack.push(event); + }); + } +}) diff --git a/src/unix/goto.cc b/src/unix/goto.cc new file mode 100644 index 0000000..f69d01e --- /dev/null +++ b/src/unix/goto.cc @@ -0,0 +1,55 @@ +/* ttyutil - unix/goto.cc + * https://github.com/clidejs/ttyutil + * + * Copyright Bernhard Bücherl + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include + +JSFUNCTION(ttyu_js_c, js_setx, { + THROW_IF_STOPPED(that); + that->x = args[0]->Int32Value(); + REFRESH_POSITION(that); +}) + +JSFUNCTION(ttyu_js_c, js_getx, { + THROW_IF_STOPPED(that); + NanReturnValue(NanNew(that->x)); +}) + +JSFUNCTION(ttyu_js_c, js_sety, { + THROW_IF_STOPPED(that); + that->y = args[0]->Int32Value(); + REFRESH_POSITION(that); +}) + +JSFUNCTION(ttyu_js_c, js_gety, { + THROW_IF_STOPPED(that); + NanReturnValue(NanNew(that->y)); +}) + +JSFUNCTION(ttyu_js_c, js_goto, { + THROW_IF_STOPPED(that); + if (args.Length() == 2) { + that->x = args[0]->Int32Value(); + that->y = args[1]->Int32Value(); + REFRESH_POSITION(that); + } +}) diff --git a/src/unix/main.cc b/src/unix/main.cc new file mode 100644 index 0000000..bc14c5b --- /dev/null +++ b/src/unix/main.cc @@ -0,0 +1,56 @@ +/* ttyutil - unix/main.cc + * https://github.com/clidejs/ttyutil + * + * Copyright Bernhard Bücherl + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include + +ttyu_js_c::ttyu_js_c() : running(FALSE), stop(TRUE), mode(MODE_VT100), + x(0), y(0) { + colors = ttyu_get_colors(); +} + +ttyu_js_c::~ttyu_js_c() { + running = FALSE; + stop = TRUE; + delete emitter; +} + +TTYU_INLINE int ttyu_get_colors() { + FILE *pipe = popen("tput colors", "r"); + int out = 0; + if (!pipe) return out; + char buffer[64]; + std::string result = ""; + while (!feof(pipe)) { + if (fgets(buffer, 64, pipe) != NULL) + result += buffer; + } + int len = static_cast(result.size()); + for (int i = len; i > 0; --i) { + char d = result[i-1]; + if (d >= '0' && d <= '9') { + out += (d - '0') * pow(10, len - i - 1); + } + } + pclose(pipe); + return out; +} diff --git a/src/unix/mode.cc b/src/unix/mode.cc new file mode 100644 index 0000000..0ec2309 --- /dev/null +++ b/src/unix/mode.cc @@ -0,0 +1,28 @@ +/* ttyutil - unix/mode.cc + * https://github.com/clidejs/ttyutil + * + * Copyright Bernhard Bücherl + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include + +JSFUNCTION(ttyu_js_c, js_mode, { + NanReturnValue(NanNew(that->mode)); +}) diff --git a/src/unix/resize.cc b/src/unix/resize.cc new file mode 100644 index 0000000..6d28128 --- /dev/null +++ b/src/unix/resize.cc @@ -0,0 +1,58 @@ +/* ttyutil - unix/resize.cc + * https://github.com/clidejs/ttyutil + * + * Copyright Bernhard Bücherl + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include + +JSFUNCTION(ttyu_js_c::js_getwidth, { + struct winsize w; + ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); + NanReturnValue(NanNew(w.ws_col)); +}) + +JSFUNCTION(ttyu_js_c::js_getheight, { + struct winsize w; + ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); + NanReturnValue(NanNew(w.ws_row)); +}) + +JSFUNCTION(ttyu_js_c, js_setwidth, { + THROW_IF_STOPPED(that); + struct winsize w; + ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); + resizeterm(args[0]->Int32Value(), w.ws_row); + wrefresh(that->win); +}) + +JSFUNCTION(ttyu_js_c, js_setheight, { + THROW_IF_STOPPED(that); + struct winsize w; + ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); + resizeterm(w.ws_col, args[0]->Int32Value()); + wrefresh(that->win); +}) + +JSFUNCTION(ttyu_js_c, js_resize, { + THROW_IF_STOPPED(that); + resizeterm(args[0]->Int32Value(), args[1]->Int32Value()); + wrefresh(that->win); +}) diff --git a/src/unix/start.cc b/src/unix/start.cc new file mode 100644 index 0000000..2f4257d --- /dev/null +++ b/src/unix/start.cc @@ -0,0 +1,50 @@ +/* ttyutil - unix/start.cc + * https://github.com/clidejs/ttyutil + * + * Copyright Bernhard Bücherl + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include + +JSFUNCTION(ttyu_js_c, js_start, { + if (that->running) NanReturnUndefined(); + that->running = TRUE; + that->stop = FALSE; + that->worker_run = TRUE; + that->win = initscr(); + that->x = getcurx(that->win); + that->y = getcury(that->win); + + that->curses_thread = ALLOC(uv_thread_t, 1); + that->barrier = ALLOC(uv_barrier_t, 1); + that->emitstacklock = ALLOC(uv_mutex_t, 1); + that->ungetlock = ALLOC(uv_mutex_t, 1); + that->condition = ALLOC(uv_cond_t, 1); + + uv_barrier_init(that->barrier, 3); + uv_mutex_init(that->emitstacklock); + uv_mutex_init(that->ungetlock); + uv_cond_init(that->condition); + + uv_thread_create(that->curses_thread, ttyu_js_c::curses_thread_func, that); + that->check_queue(); + + uv_barrier_wait(that->barrier); +}) diff --git a/src/unix/stop.cc b/src/unix/stop.cc new file mode 100644 index 0000000..d8d4e42 --- /dev/null +++ b/src/unix/stop.cc @@ -0,0 +1,45 @@ +/* ttyutil - unix/stop.cc + * https://github.com/clidejs/ttyutil + * + * Copyright Bernhard Bücherl + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include + +JSFUNCTION(ttyu_js_c, js_stop, { + if (that->running) { + that->running = FALSE; + that->stop = TRUE; + + uv_thread_join(that->curses_thread); + uv_mutex_destroy(that->emitstacklock); + uv_mutex_destroy(that->ungetlock); + uv_cond_destroy(that->condition); + uv_barrier_destroy(that->barrier); + free(that->curses_thread); + free(that->emitstacklock); + free(that->ungetlock); + free(that->condition); + free(that->barrier); + + endwin(); + delwin(that->win); + } +}) diff --git a/src/unix/utils.cc b/src/unix/utils.cc new file mode 100644 index 0000000..97c6b68 --- /dev/null +++ b/src/unix/utils.cc @@ -0,0 +1,42 @@ +/* ttyutil - unix/utils.cc + * https://github.com/clidejs/ttyutil + * + * Copyright Bernhard Bücherl + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include + +int ttyu_unix_key(int which) { + #define XXKEY(w, key, shift) if (w == which) { \ + return key; \ + } + TTYU_UNIX_KW(XXKEY); + #undef XXKEY + return WHICH_UNKNOWN; +} + +int ttyu_unix_which(int key) { + #define XXWHICH(which, k, shift) if (k == key) { \ + return which; \ + } + TTYU_UNIX_KW(XXWHICH); + #undef XXWHICH + return WHICH_UNKNOWN; +} diff --git a/src/unix/worker.cc b/src/unix/worker.cc new file mode 100644 index 0000000..ee03c2e --- /dev/null +++ b/src/unix/worker.cc @@ -0,0 +1,60 @@ +/* ttyutil - unix/worker.cc + * https://github.com/clidejs/ttyutil + * + * Copyright Bernhard Bücherl + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include + +void ttyu_js_c::check_queue() { + if (running && !stop) { + NanAsyncQueueWorker(new ttyu_worker_c(this)); + } +} + +void ttyu_worker_c::Execute() { + MUTEX_LOCK(obj->emitstacklock, { + if (obj->worker_run) { + obj->worker_run = FALSE; + uv_barrier_wait(obj->barrier); + } + + while (obj->emit_stack.size() == 0) { + uv_cond_wait(obj->condition, obj->emitstacklock); + } + + SDBG("::Execute %zu", obj->emit_stack.size()); + + // copy stack into emit_worker and clear stack + std::copy(obj->emit_stack.begin(), obj->emit_stack.end(), + std::back_inserter(emit_stack)); + obj->emit_stack.clear(); + }); +} + +void ttyu_worker_c::HandleOKCallback() { + NanScope(); + for (std::vector::size_type i = 0; + i < emit_stack.size(); ++i) { + EMIT_EVENT_OBJECT((&emit_stack[i]), obj->emitter); + } + emit_stack.clear(); + obj->check_queue(); +} diff --git a/src/unix/write.cc b/src/unix/write.cc new file mode 100644 index 0000000..d136c9c --- /dev/null +++ b/src/unix/write.cc @@ -0,0 +1,30 @@ +/* ttyutil - unix/write.cc + * https://github.com/clidejs/ttyutil + * + * Copyright Bernhard Bücherl + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include + +JSFUNCTION(ttyu_js_c, js_write, { + THROW_IF_STOPPED(that); + printf(TTYU_TOSTRING(args[1]), TTYU_TOSTRING(args[0])); + refresh(); +}) diff --git a/src/util.cc b/src/util.cc deleted file mode 100644 index 3f5cc2f..0000000 --- a/src/util.cc +++ /dev/null @@ -1,191 +0,0 @@ -/* ttyutil - util.cc - implements utility methods (see include/util.h) - * https://github.com/clidejs/ttyutil - * - * Copyright Bernhard Bücherl - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ -#include - -unsigned long util_colors_a[] = { - 0x000000, - 0x800000, - 0x008000, - 0x808000, - 0x000080, - 0x800080, - 0x008080, - 0xc0c0c0, - 0x808080, - 0xff0000, - 0x00ff00, - 0xffff00, - 0x0000ff, - 0xff00ff, - 0x00ffff, - 0xffffff -}; - -short util_rgbi2term(short r, short g, short b) { - return (short)(r / 51 * 36 + g / 51 * 6 + b / 51 + 16); -} - -short util_rgbi2win(short r, short g, short b) { - unsigned long match; - short match_i = 0; - int match_diff = 256+256+256; - int diff; - unsigned char mr; - unsigned char mg; - unsigned char mb; - - for(short i = 0; i < 16; ++i) { - match = util_colors_a[i]; - mb = ((match << 8) >> 24); - mg = ((match << 16) >> 24); - mr = ((match << 24) >> 24); - - diff = util_abs(mr - r) + util_abs(mg - g) + util_abs(mb - b); - if(diff <= match_diff) { - match_diff = diff; - match_i = i; - } - } - return match_i; -} - -char *util_render(const char *ch, short fg, short bg) { - char *out = (char *)malloc(sizeof(char) * (strlen(ch) + 30)); - if(fg != -1 && bg != -1) { - sprintf(out, "\x1b[38;5;%dm\x1b[48;5;%dm%s\x1b[49m\x1b[39m", fg, bg, ch); - } else if(fg != -1) { - sprintf(out, "\x1b[38;5;%dm%s\x1b[39m", fg, ch); - } else if(bg != -1) { - sprintf(out, "\x1b[48;5;%dm%s\x1b[49m", bg, ch); - } else { - sprintf(out, "%s", ch); - } - return out; -} - -short util_parse_dec(char d) { - if(d >= '0' && d <= '9') { - return d - '0'; - } - return 0; -} - -short util_parse_hex(char h) { - if(h >= 'A' && h <= 'F') { - return h - 'A' + 10; - } else if(h >= 'a' && h <= 'f') { - return h - 'a' + 10; - } - return util_parse_dec(h); -} - -unsigned long util_term2argb(short t) { - unsigned long argb = 0; - t -= 16; - argb = ((t / 36) * 51) << 8; - argb += ((t % 36)/ 6) * 51; - argb = argb << 8; - argb += ((t % 36) % 6) * 51; - return argb; -} - -short util_rgb2term(const char *rgb) { - short r = 0; - short g = 0; - short b = 0; - short cur = 0; - short pos = 0; - - for(int i = (int)strlen(rgb); i >= 0; --i) { - if(rgb[i] == ',') { - ++cur; - pos = 0; - } else if(rgb[i] != ')') { - if(cur == 0) { - b += util_parse_dec(rgb[i]) * (pos == 2 ? 100 : (pos == 1 ? 10 : 1)); - } else if(cur == 1) { - g += util_parse_dec(rgb[i]) * (pos == 2 ? 100 : (pos == 1 ? 10 : 1)); - } else if(cur == 2) { - r += util_parse_dec(rgb[i]) * (pos == 2 ? 100 : (pos == 1 ? 10 : 1)); - } - ++pos; - } - } -#ifdef PLATFORM_WINDOWS - return util_rgbi2win(r, g, b); -#else - return util_rgbi2term(r, g, b); -#endif -} - -short util_hex2term(const char *hex) { - short r = 0; - short g = 0; - short b = 0; - if(strlen(hex) == 6) { - r = util_parse_hex(hex[0]) * 16 + util_parse_hex(hex[1]); - g = util_parse_hex(hex[2]) * 16 + util_parse_hex(hex[3]); - b = util_parse_hex(hex[4]) * 16 + util_parse_hex(hex[5]); - } else if(strlen(hex) == 3) { - r = util_parse_hex(hex[0]); - g = util_parse_hex(hex[1]); - b = util_parse_hex(hex[2]); - r += r*16; - g += g*16; - b += b*16; - } -#ifdef PLATFORM_WINDOWS - return util_rgbi2win(r, g, b); -#else - return util_rgbi2term(r, g, b); -#endif -} - -short util_color(const char *c) { - if(c[0] == '#') { - return util_hex2term(&c[1]); - } else if(c[0] == 'r' && c[1] == 'g' && c[2] == 'b' && c[3] == '(') { - return util_rgb2term(&c[4]); - } else { - return util_hex2term(c); - } -} - -int util_max(int a, int b) { - return (a>b)?a:b; -} - -int util_min(int a, int b) { - return (a>b)?b:a; -} - -int util_abs(int a) { - return (a<0)?a*-1:a; -} - -char *util_error(char *name, int id) { - char *out = (char *)malloc(sizeof(char) * (strlen(name) + 5)); - sprintf(out, name, id); - return out; -} diff --git a/src/win.cc b/src/win.cc deleted file mode 100644 index 6f3d8c3..0000000 --- a/src/win.cc +++ /dev/null @@ -1,550 +0,0 @@ -/* ttyutil - win.cc - implements methods for windows systems (see include/win.h) - * https://github.com/clidejs/ttyutil - * - * Copyright Bernhard Bücherl - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ -#include - -void ttyu_data_init(ttyu_data_t *data) { - DWORD new_mode; - - data->err = (ttyu_error_t *)malloc(sizeof(ttyu_error_t)); - data->hin = GetStdHandle(STD_INPUT_HANDLE); - data->hout = GetStdHandle(STD_OUTPUT_HANDLE); - data->closing = false; - - if(INVALID_HANDLE_VALUE == data->hin || INVALID_HANDLE_VALUE == data->hout) { - data->err->msg = ERROR(ERROR_WIN_INIT, GetLastError()); - data->err->kill = TRUE; - return; - } - data->err->msg = NULL; - data->err->kill = FALSE; - - GetConsoleMode(data->hin, &(data->old_mode)); - new_mode = ((data->old_mode | ENABLE_MOUSE_INPUT | ENABLE_EXTENDED_FLAGS | - ENABLE_PROCESSED_INPUT | ENABLE_WINDOW_INPUT) & - ~(ENABLE_QUICK_EDIT_MODE)); - SetConsoleMode(data->hin, new_mode); - - ttyu_win_scr_update(data, TRUE); -} - -void ttyu_data_destroy(ttyu_data_t *data) { - data->closing = true; - DWORD w; - INPUT_RECORD in; - in.EventType = MENU_EVENT; - WriteConsoleInput(data->hin, const_cast(&in), 1, &w); - SetConsoleMode(data->hin, data->old_mode); -} - -bool ttyu_worker_c::execute(const ttyu_worker_c::ttyu_progress_c& progress, - ttyu_data_t *data) { - DWORD readed; - INPUT_RECORD ir[WIN_BUFFER_SIZE]; - DWORD i; - - if(data->err->msg) { - ttyu_event_t *event = (ttyu_event_t *)malloc(sizeof(ttyu_event_t)); - ttyu_event_create_error(event, data->err->msg); - progress.send(const_cast(event)); - if(data->err->kill) { - return FALSE; - } - data->err->msg = NULL; - data->err->kill = FALSE; - } - - ReadConsoleInput(data->hin, ir, WIN_BUFFER_SIZE, &readed); - // exit - if(data->closing) { return FALSE; } - - for(i = 0; i < readed; ++i) { - if(MOUSE_EVENT == ir[i].EventType) { - ttyu_event_t *event = (ttyu_event_t *)malloc(sizeof(ttyu_event_t)); - ttyu_event_create_mouse(event, EVENT_ERROR, - (int)ir[i].Event.MouseEvent.dwButtonState, - (int)ir[i].Event.MouseEvent.dwMousePosition.X, - (int)ir[i].Event.MouseEvent.dwMousePosition.Y - data->top, - ttyu_win_ctrl(ir[i].Event.MouseEvent.dwControlKeyState)); - - if(ir[i].Event.MouseEvent.dwButtonState == 0 && - ir[i].Event.MouseEvent.dwEventFlags == 0) { - event->type = EVENT_MOUSEUP; - } else if(ir[i].Event.MouseEvent.dwEventFlags == 0 || - ir[i].Event.MouseEvent.dwEventFlags == 2) { - event->type = EVENT_MOUSEDOWN; - } else if(ir[i].Event.MouseEvent.dwEventFlags == 1) { - event->type = EVENT_MOUSEMOVE; - } else if(ir[i].Event.MouseEvent.dwEventFlags == 4) { - event->type = EVENT_MOUSEWHEEL; - } else if(ir[i].Event.MouseEvent.dwEventFlags == 8) { - event->type = EVENT_MOUSEHWHEEL; - } - - progress.send(const_cast(event)); - } else if(KEY_EVENT == ir[i].EventType && ir[i].Event.KeyEvent.bKeyDown) { - ttyu_event_t *event = (ttyu_event_t *)malloc(sizeof(ttyu_event_t)); - char *ch = (char *)std::malloc(sizeof(char) * 3); - memcpy(ch, &(ir[i].Event.KeyEvent.uChar.UnicodeChar), sizeof(char) * 2); - ch[2] = '\0'; - - ttyu_event_create_key(event, ttyu_win_ctrl( - ir[i].Event.KeyEvent.dwControlKeyState), ch, - (int)ir[i].Event.KeyEvent.wVirtualKeyCode, - ttyu_win_which(ir[i].Event.KeyEvent.wVirtualKeyCode)); - - free(ch); - - progress.send(const_cast(event)); - } else if(WINDOW_BUFFER_SIZE_EVENT == ir[i].EventType) { - ttyu_event_t *event = (ttyu_event_t *)malloc(sizeof(ttyu_event_t)); - - if(!ttyu_win_scr_update(data)) { - ttyu_event_create_error(event, data->err->msg); - } else { - ttyu_event_create_resize(event); - } - progress.send(const_cast(event)); - } - } - - return TRUE; -} - -int ttyu_win_which(DWORD code) { - if(code > 0) { - return (int) code; - } - return WHICH_UNKNOWN; -} - -int ttyu_win_ctrl(DWORD state) { - int ctrl = CTRL_NULL; - if(state & RIGHT_ALT_PRESSED || state & LEFT_ALT_PRESSED) { - ctrl |= CTRL_ALT; - } - if(state & RIGHT_CTRL_PRESSED || state & LEFT_CTRL_PRESSED) { - ctrl |= CTRL_CTRL; - } - if(state & SHIFT_PRESSED) { - ctrl |= CTRL_SHIFT; - } - if(state & ENHANCED_KEY) { - ctrl |= CTRL_ENHANCED; - } - if(state & NUMLOCK_ON) { - ctrl |= CTRL_NUMLOCK; - } - if(state & SCROLLLOCK_ON) { - ctrl |= CTRL_SCROLLLOCK; - } - if(state & CAPSLOCK_ON) { - ctrl |= CTRL_CAPSLOCK; - } - return ctrl; -} - -DWORD ttyu_win_state(int ctrl) { - DWORD state = 0; - if(ctrl & CTRL_ALT) { - state |= RIGHT_ALT_PRESSED & LEFT_ALT_PRESSED; - } - if(ctrl & CTRL_CTRL) { - state |= RIGHT_CTRL_PRESSED & LEFT_CTRL_PRESSED; - } - if(ctrl & CTRL_SHIFT) { - state |= SHIFT_PRESSED; - } - if(ctrl & CTRL_ENHANCED) { - state |= ENHANCED_KEY; - } - if(ctrl & CTRL_NUMLOCK) { - state |= NUMLOCK_ON; - } - if(ctrl & CTRL_SCROLLLOCK) { - state |= SCROLLLOCK_ON; - } - if(ctrl & CTRL_CAPSLOCK) { - state |= CAPSLOCK_ON; - } - return state; -} - -bool ttyu_win_scr_update(ttyu_data_t *data, bool initial) { - CONSOLE_SCREEN_BUFFER_INFO con_info; - - if(!GetConsoleScreenBufferInfo(data->hout, &con_info)) { - data->err->msg = ERROR(ERROR_WIN_GET, GetLastError()); - return !(data->err->kill = TRUE); - } - - data->top = (int)con_info.srWindow.Top; - data->width = (int)con_info.dwSize.X; - data->height = (int)con_info.dwSize.Y - data->top; - - data->curx = (int)con_info.dwCursorPosition.X; - data->cury = (int)con_info.dwCursorPosition.Y - data->top; - - if(initial) { - data->base_color = (short)con_info.wAttributes; - } - return TRUE; -} - -void ttyu_win_render(char *c, ttyu_data_t *data) { - short fg = -1; - short bg = -1; - int len = (int)strlen(c); // content length - int i; // position - int j; // lookahead position - short color; - - for(i = 0; i < len; ++i) { - if(c[i] == '\x1b' && c[i+1] == '[') { - j = 1; - while(i+j < len && c[i+j] != 'm') ++j; - - if(j == 4) { - // end token - if(c[i+2] == '3') { - fg = -1; - } else { - bg = -1; - } - } else { - // start token - if(c[i+2] == '3') { // foreground - if(j == 9) { - fg = util_parse_dec(c[i+7]) * 10 + util_parse_dec(c[i+8]); - } else { - fg = util_parse_dec(c[i+7]); - } - } else { // background - if(j == 9) { - bg = util_parse_dec(c[i+7]) * 10 + util_parse_dec(c[i+8]); - } else { - bg = util_parse_dec(c[i+7]); - } - } - } - - color = 0; - if(fg != -1) { - color += fg; - } else { - color += (data->base_color << 4) >> 4; - } - if(bg != -1) { - color += bg << 4; - } else { - color += (data->base_color >> 4) << 4; - } - - SetConsoleTextAttribute(data->hout, color); - - i += j; - } else { - std::cout << c[i]; - } - } - - SetConsoleTextAttribute(data->hout, data->base_color); -} - -bool ttyu_win_clrscr(ttyu_data_t *data, int x, int y, int width, int height) { - COORD coordhome = { x, y }; - DWORD written; - CONSOLE_SCREEN_BUFFER_INFO con_info; - DWORD size = width * height; - - // Fill the entire screen with blanks. - if(!FillConsoleOutputCharacter(data->hout, (TCHAR) ' ', size, coordhome, - &written)) { - data->err->msg = ERROR(ERROR_WIN_FILL, GetLastError()); - return (data->err->kill = FALSE); - } - - // Get the current text attribute. - if(!GetConsoleScreenBufferInfo(data->hout, &con_info)) { - data->err->msg = ERROR(ERROR_WIN_GET, GetLastError()); - return (data->err->kill = FALSE); - } - - // Set the buffer's attributes accordingly. - if( !FillConsoleOutputAttribute(data->hout, con_info.wAttributes, size, - coordhome, &written)) { - data->err->msg = ERROR(ERROR_WIN_FILL, GetLastError()); - return (data->err->kill = FALSE); - } - - // Put the cursor at its home coordinates. - SetConsoleCursorPosition(data->hout, coordhome); - return TRUE; -} - -NAN_METHOD(ttyu_js_c::emit) { - NanScope(); - ttyu_js_c *obj = ObjectWrap::Unwrap(args.This()); - TTYU_THROW_IF_NOT_RUNNING(obj); - if(obj->running) { - int ev = args[0]->Int32Value(); - INPUT_RECORD in[1]; - DWORD w; - - in[0].EventType = 0; - - switch(ev) { - case EVENT_KEY: - KEY_EVENT_RECORD kev; - - kev.bKeyDown = TRUE; - kev.wVirtualKeyCode = (WORD)args[1]->Int32Value(); - kev.dwControlKeyState = ttyu_win_state(args[2]->Int32Value()); - kev.uChar.UnicodeChar = (WCHAR)kev.wVirtualKeyCode; // TODO - kev.uChar.AsciiChar = (CHAR)kev.wVirtualKeyCode; // TODO - kev.wRepeatCount = 1; - kev.wVirtualScanCode = MapVirtualKey(kev.wVirtualKeyCode, - MAPVK_VK_TO_VSC); - - in[0].EventType = KEY_EVENT; - in[0].Event.KeyEvent = kev; - break; - case EVENT_RESIZE: - WINDOW_BUFFER_SIZE_RECORD wev; - COORD size; - - size.X = args[1]->Int32Value(); - size.Y = args[2]->Int32Value(); - - wev.dwSize = size; - in[0].EventType = WINDOW_BUFFER_SIZE_EVENT; - in[0].Event.WindowBufferSizeEvent = wev; - break; - case EVENT_MOUSEDOWN: - case EVENT_MOUSEUP: - case EVENT_MOUSEMOVE: - case EVENT_MOUSEWHEEL: - case EVENT_MOUSEHWHEEL: - MOUSE_EVENT_RECORD mev; - COORD pos; - - in[0].EventType = MOUSE_EVENT; - pos.X = (short)args[2]->Int32Value(); - pos.Y = (short)args[3]->Int32Value() + obj->data->top; - mev.dwControlKeyState = ttyu_win_state(args[4]->Int32Value()); - - if(ev == EVENT_MOUSEUP) { - mev.dwButtonState = args[1]->Int32Value(); - mev.dwEventFlags = 0; - } else if(ev == EVENT_MOUSEDOWN) { - mev.dwButtonState = args[1]->Int32Value(); - mev.dwEventFlags = 2; - } else if(ev == EVENT_MOUSEMOVE) { - mev.dwEventFlags = 1; - } else if(ev == EVENT_MOUSEWHEEL) { - mev.dwEventFlags = 4; - } else if(ev == EVENT_MOUSEHWHEEL) { - mev.dwEventFlags = 8; - } else { - // invalidate event - in[0].EventType = 0; - } - - mev.dwMousePosition = pos; - in[0].Event.MouseEvent = mev; - break; - default: // EVENT_ERROR, EVENT_SIGNAL - // do nothing - break; - } - - if(in[0].EventType != 0) { - WriteConsoleInput(obj->data->hin, in, 1, &w); - } - } - NanReturnUndefined(); -} - -NAN_GETTER(ttyu_js_c::get_width) { - NanScope(); - ttyu_js_c *obj = ObjectWrap::Unwrap(args.This()); - TTYU_THROW_IF_NOT_RUNNING(obj); - NanReturnValue(NanNew(obj->data->width)); -} - -NAN_GETTER(ttyu_js_c::get_height) { - NanScope(); - ttyu_js_c *obj = ObjectWrap::Unwrap(args.This()); - TTYU_THROW_IF_NOT_RUNNING(obj); - NanReturnValue(NanNew(obj->data->height)); -} - -NAN_GETTER(ttyu_js_c::get_mode) { - NanScope(); - ttyu_js_c *obj = ObjectWrap::Unwrap(args.This()); - TTYU_THROW_IF_NOT_RUNNING(obj); - NanReturnValue(NanNew(MODE_CMD)); -} - -NAN_GETTER(ttyu_js_c::get_colors) { - NanScope(); - ttyu_js_c *obj = ObjectWrap::Unwrap(args.This()); - TTYU_THROW_IF_NOT_RUNNING(obj); - NanReturnValue(NanNew(WIN_COLORS)); -} - -NAN_GETTER(ttyu_js_c::getx) { - NanScope(); - ttyu_js_c *obj = ObjectWrap::Unwrap(args.This()); - TTYU_THROW_IF_NOT_RUNNING(obj); - ttyu_win_scr_update(obj->data); - NanReturnValue(NanNew(obj->data->curx)); -} - -NAN_SETTER(ttyu_js_c::setx) { - NanScope(); - ttyu_js_c *obj = ObjectWrap::Unwrap(args.This()); - TTYU_THROW_IF_NOT_RUNNING_VOID(obj); - obj->data->curx = args.Data()->Int32Value(); - - CONSOLE_SCREEN_BUFFER_INFOEX con_info; - if(GetConsoleScreenBufferInfoEx(obj->data->hout, &con_info)) { - con_info.dwCursorPosition.X = obj->data->curx; - if(!SetConsoleScreenBufferInfoEx(obj->data->hout, &con_info)) { - obj->data->err->msg = ERROR(ERROR_WIN_SET, GetLastError()); - } - } else { - obj->data->err->msg = ERROR(ERROR_WIN_GET, GetLastError()); - } -} - -NAN_GETTER(ttyu_js_c::gety) { - NanScope(); - ttyu_js_c *obj = ObjectWrap::Unwrap(args.This()); - TTYU_THROW_IF_NOT_RUNNING(obj); - ttyu_win_scr_update(obj->data); - NanReturnValue(NanNew(obj->data->cury)); -} - -NAN_SETTER(ttyu_js_c::sety) { - NanScope(); - ttyu_js_c *obj = ObjectWrap::Unwrap(args.This()); - TTYU_THROW_IF_NOT_RUNNING_VOID(obj); - obj->data->cury = args.Data()->Int32Value(); - - CONSOLE_SCREEN_BUFFER_INFOEX con_info; - if(GetConsoleScreenBufferInfoEx(obj->data->hout, &con_info)) { - con_info.dwCursorPosition.Y = obj->data->cury; - if(!SetConsoleScreenBufferInfoEx(obj->data->hout, &con_info)) { - obj->data->err->msg = ERROR(ERROR_WIN_SET, GetLastError()); - } - } else { - obj->data->err->msg = ERROR(ERROR_WIN_GET, GetLastError()); - } -} - -NAN_METHOD(ttyu_js_c::gotoxy) { - NanScope(); - ttyu_js_c *obj = ObjectWrap::Unwrap(args.This()); - TTYU_THROW_IF_NOT_RUNNING(obj); - obj->data->curx = args[0]->Int32Value(); - obj->data->cury = args[1]->Int32Value(); - - CONSOLE_SCREEN_BUFFER_INFOEX con_info; - if(GetConsoleScreenBufferInfoEx(obj->data->hout, &con_info)) { - con_info.dwCursorPosition.X = obj->data->curx; - con_info.dwCursorPosition.Y = obj->data->cury; - if(!SetConsoleScreenBufferInfoEx(obj->data->hout, &con_info)) { - obj->data->err->msg = ERROR(ERROR_WIN_SET, GetLastError()); - } - } else { - obj->data->err->msg = ERROR(ERROR_WIN_GET, GetLastError()); - } - NanReturnUndefined(); -} - -NAN_METHOD(ttyu_js_c::write) { - NanScope(); - ttyu_js_c *obj = ObjectWrap::Unwrap(args.This()); - TTYU_THROW_IF_NOT_RUNNING(obj); - v8::String::Utf8Value *ch = new v8::String::Utf8Value(args[0]->ToString()); - int fg = args[1]->IsNumber() ? args[1]->Int32Value() : ( - args[1]->IsString() ? util_color( - (new v8::String::Utf8Value(args[1]->ToString()))->operator*()) : - 1); - int bg = args[2]->IsNumber() ? args[2]->Int32Value() : ( - args[2]->IsString() ? util_color( - (new v8::String::Utf8Value(args[2]->ToString()))->operator*()) : - 1); - ttyu_win_render(util_render(ch->operator*(), fg, bg), obj->data); - NanReturnThis(); -} - -NAN_METHOD(ttyu_js_c::beep) { - NanScope(); - ttyu_js_c *obj = ObjectWrap::Unwrap(args.This()); - TTYU_THROW_IF_NOT_RUNNING(obj); - Beep(750, 300); - NanReturnThis(); -} - -NAN_METHOD(ttyu_js_c::clear) { - NanScope(); - ttyu_js_c *obj = ObjectWrap::Unwrap(args.This()); - TTYU_THROW_IF_NOT_RUNNING(obj); - int x = 0; - int y = 0; - int width = obj->data->width; - int height = obj->data->height; - - if(args.Length() == 4 && args[0]->IsNumber() && args[1]->IsNumber() && - args[2]->IsNumber() && args[3]->IsNumber()) { - x = util_max(args[0]->Int32Value(), x); - y = util_max(args[1]->Int32Value(), y); - width = util_min(args[2]->Int32Value(), width); - height = util_min(args[3]->Int32Value(), height); - } - - ttyu_win_clrscr(obj->data, x, y, width, height); - NanReturnThis(); -} - -NAN_METHOD(ttyu_js_c::prepare) { - NanScope(); - ttyu_js_c *obj = ObjectWrap::Unwrap(args.This()); - TTYU_THROW_IF_NOT_RUNNING(obj); - v8::String::Utf8Value *ch = new v8::String::Utf8Value(args[0]->ToString()); - int fg = args[1]->IsNumber() ? args[1]->Int32Value() : ( - args[1]->IsString() ? util_color( - (new v8::String::Utf8Value(args[1]->ToString()))->operator*()) : - 1); - int bg = args[2]->IsNumber() ? args[2]->Int32Value() : ( - args[2]->IsString() ? util_color( - (new v8::String::Utf8Value(args[2]->ToString()))->operator*()) : - 1); - NanReturnValue(NanNew(util_render(ch->operator*(), fg, bg))); -} - -NAN_METHOD(ttyu_js_c::color) { - NanScope(); - ttyu_js_c *obj = ObjectWrap::Unwrap(args.This()); - TTYU_THROW_IF_NOT_RUNNING(obj); - v8::String::Utf8Value *ch = new v8::String::Utf8Value(args[0]->ToString()); - NanReturnValue(NanNew(util_color(ch->operator*()))); -} diff --git a/src/win/beep.cc b/src/win/beep.cc new file mode 100644 index 0000000..1e86c8b --- /dev/null +++ b/src/win/beep.cc @@ -0,0 +1,29 @@ +/* ttyutil - win/beep.cc + * https://github.com/clidejs/ttyutil + * + * Copyright Bernhard Bücherl + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include + +JSFUNCTION(ttyu_js_c, js_beep, { + THROW_IF_STOPPED(that); + // TODO(@bbuecherl) +}) diff --git a/src/win/clear.cc b/src/win/clear.cc new file mode 100644 index 0000000..cf6a7fd --- /dev/null +++ b/src/win/clear.cc @@ -0,0 +1,29 @@ +/* ttyutil - win/clear.cc + * https://github.com/clidejs/ttyutil + * + * Copyright Bernhard Bücherl + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include + +JSFUNCTION(ttyu_js_c, js_clear, { + THROW_IF_STOPPED(that); + // TODO(@bbuecherl) +}) diff --git a/src/win/colors.cc b/src/win/colors.cc new file mode 100644 index 0000000..10e7a47 --- /dev/null +++ b/src/win/colors.cc @@ -0,0 +1,28 @@ +/* ttyutil - win/colors.cc + * https://github.com/clidejs/ttyutil + * + * Copyright Bernhard Bücherl + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include + +JSFUNCTION(ttyu_js_c::js_colors, { + NanReturnValue(NanNew(16)); +}) diff --git a/src/win/cursor.cc b/src/win/cursor.cc new file mode 100644 index 0000000..a19ef41 --- /dev/null +++ b/src/win/cursor.cc @@ -0,0 +1,34 @@ +/* ttyutil - win/cursor.cc + * https://github.com/clidejs/ttyutil + * + * Copyright Bernhard Bücherl + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include + +JSFUNCTION(ttyu_js_c, js_hide, { + THROW_IF_STOPPED(that); + // TODO(@bbuecherl) +}) + +JSFUNCTION(ttyu_js_c, js_show, { + THROW_IF_STOPPED(that); + // TODO(@bbuecherl) +}) diff --git a/src/win/emit.cc b/src/win/emit.cc new file mode 100644 index 0000000..042e867 --- /dev/null +++ b/src/win/emit.cc @@ -0,0 +1,105 @@ +/* ttyutil - win/emit.cc + * https://github.com/clidejs/ttyutil + * + * Copyright Bernhard Bücherl + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include + +JSFUNCTION(ttyu_js_c, js_emit, { + DBG("::js_emit"); + THROW_IF_STOPPED(that); + if (that->running) { + int ev = args[0]->Int32Value(); + INPUT_RECORD in[1]; + DWORD w; + + in[0].EventType = 0; + + switch (ev) { + case EVENT_KEY: + KEY_EVENT_RECORD kev; + + kev.bKeyDown = TRUE; + kev.wVirtualKeyCode = (WORD)args[1]->Int32Value(); + kev.dwControlKeyState = ttyu_win_state(args[2]->Int32Value()); + kev.uChar.UnicodeChar = static_cast(kev.wVirtualKeyCode); + kev.uChar.AsciiChar = static_cast(kev.wVirtualKeyCode); + kev.wRepeatCount = 1; + kev.wVirtualScanCode = MapVirtualKey(kev.wVirtualKeyCode, + MAPVK_VK_TO_VSC); + + in[0].EventType = KEY_EVENT; + in[0].Event.KeyEvent = kev; + break; + case EVENT_RESIZE: + WINDOW_BUFFER_SIZE_RECORD wev; + COORD size; + + size.X = args[1]->Int32Value(); + size.Y = args[2]->Int32Value(); + + wev.dwSize = size; + in[0].EventType = WINDOW_BUFFER_SIZE_EVENT; + in[0].Event.WindowBufferSizeEvent = wev; + break; + case EVENT_MOUSEDOWN: + case EVENT_MOUSEUP: + case EVENT_MOUSEMOVE: + case EVENT_MOUSEWHEEL: + case EVENT_MOUSEHWHEEL: + MOUSE_EVENT_RECORD mev; + COORD pos; + + in[0].EventType = MOUSE_EVENT; + pos.X = static_cast(args[2]->Int32Value()); + pos.Y = static_cast(args[3]->Int32Value() + that->top); + mev.dwControlKeyState = ttyu_win_state(args[4]->Int32Value()); + + if (ev == EVENT_MOUSEUP) { + mev.dwButtonState = args[1]->Int32Value(); + mev.dwEventFlags = 0; + } else if (ev == EVENT_MOUSEDOWN) { + mev.dwButtonState = args[1]->Int32Value(); + mev.dwEventFlags = 2; + } else if (ev == EVENT_MOUSEMOVE) { + mev.dwEventFlags = 1; + } else if (ev == EVENT_MOUSEWHEEL) { + mev.dwEventFlags = 4; + } else if (ev == EVENT_MOUSEHWHEEL) { + mev.dwEventFlags = 8; + } else { + // invalidate event + in[0].EventType = 0; + } + + mev.dwMousePosition = pos; + in[0].Event.MouseEvent = mev; + break; + default: // EVENT_ERROR, EVENT_SIGNAL + // do nothing + break; + } + + if (in[0].EventType != 0) { + WriteConsoleInput(that->hin, in, 1, &w); + } + } +}) diff --git a/src/win/goto.cc b/src/win/goto.cc new file mode 100644 index 0000000..007c580 --- /dev/null +++ b/src/win/goto.cc @@ -0,0 +1,49 @@ +/* ttyutil - win/goto.cc + * https://github.com/clidejs/ttyutil + * + * Copyright Bernhard Bücherl + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include + +JSFUNCTION(ttyu_js_c, js_setx, { + THROW_IF_STOPPED(that); + // TODO(@bbuecherl) +}) + +JSFUNCTION(ttyu_js_c, js_getx, { + THROW_IF_STOPPED(that); + // TODO(@bbuecherl) +}) + +JSFUNCTION(ttyu_js_c, js_sety, { + THROW_IF_STOPPED(that); + // TODO(@bbuecherl) +}) + +JSFUNCTION(ttyu_js_c, js_gety, { + THROW_IF_STOPPED(that); + // TODO(@bbuecherl) +}) + +JSFUNCTION(ttyu_js_c, js_goto, { + THROW_IF_STOPPED(that); + // TODO(@bbuecherl) +}) diff --git a/src/ttyu.cc b/src/win/main.cc similarity index 74% rename from src/ttyu.cc rename to src/win/main.cc index 4278b08..c2f3247 100644 --- a/src/ttyu.cc +++ b/src/win/main.cc @@ -1,4 +1,4 @@ -/* ttyutil - ttyu.cc - implements additional functions +/* ttyutil - win/main.cc * https://github.com/clidejs/ttyutil * * Copyright Bernhard Bücherl @@ -23,20 +23,12 @@ */ #include -int ttyu_ee_cb_call(ee__listener_t *l, v8::Local data) { - v8::Local args[] = { - data - }; - int count = 0; - do { - if(l->cb) { - l->cb->Call(1, args); - ++count; - } - } while((l = l->next)); - return count; +ttyu_js_c::ttyu_js_c() : running(FALSE), stop(TRUE), top(0) { + DBG("::ttyu_js_c"); } -int ttyu_ee_compare(EE_CB_ARG(cb1), EE_CB_ARG(cb2)) { - return (int)(cb1->GetFunction() == cb2->GetFunction()); +ttyu_js_c::~ttyu_js_c() { + DBG("::~ttyu_js_c"); + running = FALSE; + stop = TRUE; } diff --git a/src/win/mode.cc b/src/win/mode.cc new file mode 100644 index 0000000..0af31f4 --- /dev/null +++ b/src/win/mode.cc @@ -0,0 +1,28 @@ +/* ttyutil - win/mode.cc + * https://github.com/clidejs/ttyutil + * + * Copyright Bernhard Bücherl + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include + +JSFUNCTION(ttyu_js_c::js_mode, { + NanReturnValue(NanNew(MODE_CMD)); +}) diff --git a/src/win/resize.cc b/src/win/resize.cc new file mode 100644 index 0000000..6dd4d8b --- /dev/null +++ b/src/win/resize.cc @@ -0,0 +1,46 @@ +/* ttyutil - win/resize.cc + * https://github.com/clidejs/ttyutil + * + * Copyright Bernhard Bücherl + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include + +JSFUNCTION(ttyu_js_c, js_getwidth, { + // TODO(@bbuecherl) + NanReturnValue(NanNew(0)); +}) + +JSFUNCTION(ttyu_js_c, js_getheight, { + // TODO(@bbuecherl) + NanReturnValue(NanNew(0)); +}) + +JSFUNCTION(ttyu_js_c, js_setwidth, { + // TODO(@bbuecherl) +}) + +JSFUNCTION(ttyu_js_c, js_setheight, { + // TODO(@bbuecherl) +}) + +JSFUNCTION(ttyu_js_c, js_resize, { + // TODO(@bbuecherl) +}) diff --git a/src/win/start.cc b/src/win/start.cc new file mode 100644 index 0000000..2e87131 --- /dev/null +++ b/src/win/start.cc @@ -0,0 +1,53 @@ +/* ttyutil - win/start.cc + * https://github.com/clidejs/ttyutil + * + * Copyright Bernhard Bücherl + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include + +JSFUNCTION(ttyu_js_c, js_start, { + if (that->running) NanReturnUndefined(); + that->running = TRUE; + that->stop = FALSE; + + DBG("::js_start()"); + that->barrier = ALLOC(uv_barrier_t, 1); + uv_barrier_init(that->barrier, 2); + + that->hin = GetStdHandle(STD_INPUT_HANDLE); + that->hout = GetStdHandle(STD_OUTPUT_HANDLE); + + if (INVALID_HANDLE_VALUE == that->hin || INVALID_HANDLE_VALUE == that->hout) { + NanThrowError("invalid std handles"); + } + + GetConsoleMode(that->hin, &(that->old_mode)); + DWORD new_mode = ((that->old_mode | ENABLE_MOUSE_INPUT | + ENABLE_EXTENDED_FLAGS | ENABLE_PROCESSED_INPUT | ENABLE_WINDOW_INPUT) & + ~(ENABLE_QUICK_EDIT_MODE)); + that->worker = new ttyu_worker_c(that); + SetConsoleMode(that->hin, new_mode); + + DBG(" async queue start"); + NanAsyncQueueWorker(that->worker); + + BARRIER_WAITKILL(that->barrier); +}) diff --git a/src/win/stop.cc b/src/win/stop.cc new file mode 100644 index 0000000..51e6837 --- /dev/null +++ b/src/win/stop.cc @@ -0,0 +1,46 @@ +/* ttyutil - win/stop.cc + * https://github.com/clidejs/ttyutil + * + * Copyright Bernhard Bücherl + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include + +JSFUNCTION(ttyu_js_c, js_stop, { + if (that->running) { + DBG("::stop stopping"); + INPUT_RECORD in[1]; + KEY_EVENT_RECORD kev; + DWORD w; + + kev.bKeyDown = TRUE; + kev.wVirtualKeyCode = WHICH_FN; + kev.dwControlKeyState = 0; + kev.wRepeatCount = 1; + kev.wVirtualScanCode = MapVirtualKey(WHICH_FN, MAPVK_VK_TO_VSC); + in[0].EventType = KEY_EVENT; + in[0].Event.KeyEvent = kev; + + that->running = FALSE; + that->stop = TRUE; + WriteConsoleInput(that->hin, in, 1, &w); + SetConsoleMode(that->hin, that->old_mode); + } +}) diff --git a/src/win/utils.cc b/src/win/utils.cc new file mode 100644 index 0000000..267195f --- /dev/null +++ b/src/win/utils.cc @@ -0,0 +1,101 @@ +/* ttyutil - win/utils.cc + * https://github.com/clidejs/ttyutil + * + * Copyright Bernhard Bücherl + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include + +bool ttyu_win_scr_update(ttyu_js_c *obj, bool initial) { + CONSOLE_SCREEN_BUFFER_INFO con_info; + + if (!GetConsoleScreenBufferInfo(obj->hout, &con_info)) { + /*data->err->msg = ERRMSG(0x02); + return !(data->err->kill = TRUE);*/ + return FALSE; + } + + obj->top = con_info.srWindow.Top; + obj->width = con_info.dwSize.X; + obj->height = con_info.dwSize.Y - obj->top; + + obj->curx = con_info.dwCursorPosition.X; + obj->cury = con_info.dwCursorPosition.Y - obj->top; + return TRUE; +} + +int ttyu_win_ctrl(DWORD state) { + int ctrl = CTRL_NULL; + if (state & RIGHT_ALT_PRESSED || state & LEFT_ALT_PRESSED) { + ctrl |= CTRL_ALT; + } + if (state & RIGHT_CTRL_PRESSED || state & LEFT_CTRL_PRESSED) { + ctrl |= CTRL_CTRL; + } + if (state & SHIFT_PRESSED) { + ctrl |= CTRL_SHIFT; + } + if (state & ENHANCED_KEY) { + ctrl |= CTRL_ENHANCED; + } + if (state & NUMLOCK_ON) { + ctrl |= CTRL_NUMLOCK; + } + if (state & SCROLLLOCK_ON) { + ctrl |= CTRL_SCROLLLOCK; + } + if (state & CAPSLOCK_ON) { + ctrl |= CTRL_CAPSLOCK; + } + return ctrl; +} + +int ttyu_win_which(DWORD code) { + if (code > 0) { + return static_cast(code); + } + return WHICH_UNKNOWN; +} + +DWORD ttyu_win_state(int ctrl) { + DWORD state = 0; + if (ctrl & CTRL_ALT) { + state |= RIGHT_ALT_PRESSED & LEFT_ALT_PRESSED; + } + if (ctrl & CTRL_CTRL) { + state |= RIGHT_CTRL_PRESSED & LEFT_CTRL_PRESSED; + } + if (ctrl & CTRL_SHIFT) { + state |= SHIFT_PRESSED; + } + if (ctrl & CTRL_ENHANCED) { + state |= ENHANCED_KEY; + } + if (ctrl & CTRL_NUMLOCK) { + state |= NUMLOCK_ON; + } + if (ctrl & CTRL_SCROLLLOCK) { + state |= SCROLLLOCK_ON; + } + if (ctrl & CTRL_CAPSLOCK) { + state |= CAPSLOCK_ON; + } + return state; +} diff --git a/src/win/worker.cc b/src/win/worker.cc new file mode 100644 index 0000000..cde915a --- /dev/null +++ b/src/win/worker.cc @@ -0,0 +1,112 @@ +/* ttyutil - win/worker.cc + * https://github.com/clidejs/ttyutil + * + * Copyright Bernhard Bücherl + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include + +bool ttyu_worker_c::execute(const ttyu_worker_c::ttyu_progress_c& progress, + ttyu_js_c *obj) { + DBG("::execute()"); + DWORD readed; + DWORD i; + DWORD type; + INPUT_RECORD ir[WIN_BUFFER_SIZE]; + + ReadConsoleInput(obj->hin, ir, WIN_BUFFER_SIZE, &readed); + if (obj->stop) { return FALSE; } // exit + for (i = 0; i < readed; ++i) { + if (MOUSE_EVENT == ir[i].EventType) { + type = MOUSE_EVENT; + } else if (KEY_EVENT == ir[i].EventType && ir[i].Event.KeyEvent.bKeyDown) { + type = KEY_EVENT; + } else if (WINDOW_BUFFER_SIZE_EVENT == ir[i].EventType) { + type = WINDOW_BUFFER_SIZE_EVENT; + } else { + SDBG(" uncaught event %d", ir[i].EventType); + continue; + } + SDBG(" capturing event (type: %d)", type); + ttyu_event_t event; + switch (type) { + case MOUSE_EVENT: + ttyu_event_create_mouse(&event, EVENT_ERROR, + static_cast(ir[i].Event.MouseEvent.dwButtonState), + static_cast(ir[i].Event.MouseEvent.dwMousePosition.X), + static_cast(ir[i].Event.MouseEvent.dwMousePosition.Y-obj->top), + ttyu_win_ctrl(ir[i].Event.MouseEvent.dwControlKeyState)); + + if (ir[i].Event.MouseEvent.dwButtonState == 0 && + ir[i].Event.MouseEvent.dwEventFlags == 0) { + event.type = EVENT_MOUSEUP; + } else if (ir[i].Event.MouseEvent.dwEventFlags == 0 || + ir[i].Event.MouseEvent.dwEventFlags == 2) { + event.type = EVENT_MOUSEDOWN; + } else if (ir[i].Event.MouseEvent.dwEventFlags == 1) { + event.type = EVENT_MOUSEMOVE; + } else if (ir[i].Event.MouseEvent.dwEventFlags == 4) { + event.type = EVENT_MOUSEWHEEL; + } else if (ir[i].Event.MouseEvent.dwEventFlags == 8) { + event.type = EVENT_MOUSEHWHEEL; + } + break; + case KEY_EVENT: { + char *ch = ALLOC(char, 3); + wcstombs(ch, &(ir[i].Event.KeyEvent.uChar.UnicodeChar), + sizeof(char) * 2); + ch[2] = '\0'; + + ttyu_event_create_key(&event, + ttyu_win_ctrl(ir[i].Event.KeyEvent.dwControlKeyState), ch, + static_cast(ir[i].Event.KeyEvent.wVirtualKeyCode), + ttyu_win_which(ir[i].Event.KeyEvent.wVirtualKeyCode)); + free(ch); + } break; + default: // WINDOW_BUFFER_SIZE_EVENT + if (!ttyu_win_scr_update(obj, FALSE)) { + ttyu_event_create_error(&event); + } else { + ttyu_event_create_resize(&event); + } + break; + } + + DBG(" emitting"); + progress.send(const_cast(&event)); + } + return TRUE; +} + +void ttyu_worker_c::handle(ttyu_event_t *event) { + NanScope(); + DBG("::handle"); + EMIT_EVENT_OBJECT(event, obj_->emitter); + DBG(" handled"); +} + +void ttyu_worker_c::Execute() { + DBG("::Execute"); + ttyu_progress_c progress(this); + BARRIER_WAITKILL(obj_->barrier); + DBG(" barrier_waited"); + // loop execute until it returns false (error) + while (execute(progress, obj_)) continue; +} diff --git a/src/win/write.cc b/src/win/write.cc new file mode 100644 index 0000000..021a1bc --- /dev/null +++ b/src/win/write.cc @@ -0,0 +1,28 @@ +/* ttyutil - win/write.cc + * https://github.com/clidejs/ttyutil + * + * Copyright Bernhard Bücherl + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include + +JSFUNCTION(ttyu_js_c::js_write, { + printf("%s", TTYU_TOSTRING(args[0])); +}) diff --git a/tests/index.js b/tests/index.js new file mode 100644 index 0000000..760529e --- /dev/null +++ b/tests/index.js @@ -0,0 +1,9 @@ +var util = require("../installer/util"); + +util.waterfall([ + require("./key"), + require("./mouse") +], function() { + console.log("finished without errors"); + process.exit(0); +}); diff --git a/tests/key.js b/tests/key.js index b7ab482..697a7e9 100644 --- a/tests/key.js +++ b/tests/key.js @@ -1,155 +1,104 @@ -require("it-each")({ testPerIteration: true}); -var is = require("node-is"); -var Const = require("../const"); - -var which = []; - -var keys = Object.keys(Const.Which); -for(var i = 0; i < keys.length; ++i) { - c = Const.Which[keys[i]]; - if(c != 19 && c != -1 && which.indexOf(c) === -1) { - which.push(c); - } -} +var ttyu = require("../index"); +var assert = require("assert"); var unix_required = [ - Const.Which.BACK, - Const.Which.TAB, - Const.Which.CLEAR, - Const.Which.ENTER, - Const.Which.SHIFT, - Const.Which.SPACE, - Const.Which.PRIOR, - Const.Which.NEXT, - Const.Which.END, - Const.Which.HOME, - Const.Which.LEFT, - Const.Which.UP, - Const.Which.RIGHT, - Const.Which.DOWN, - Const.Which.PRINT, - Const.Which.INSERT, - Const.Which.DELETE, - Const.Which.HELP, - Const.Which.CHAR0, - Const.Which.CHAR1, - Const.Which.CHAR2, - Const.Which.CHAR3, - Const.Which.CHAR4, - Const.Which.CHAR5, - Const.Which.CHAR6, - Const.Which.CHAR7, - Const.Which.CHAR8, - Const.Which.CHAR9, - Const.Which.CHARA, - Const.Which.CHARB, - Const.Which.CHARC, - Const.Which.CHARD, - Const.Which.CHARE, - Const.Which.CHARF, - Const.Which.CHARG, - Const.Which.CHARH, - Const.Which.CHARI, - Const.Which.CHARJ, - Const.Which.CHARK, - Const.Which.CHARL, - Const.Which.CHARM, - Const.Which.CHARN, - Const.Which.CHARO, - Const.Which.CHARP, - Const.Which.CHARQ, - Const.Which.CHARR, - Const.Which.CHARS, - Const.Which.CHART, - Const.Which.CHARU, - Const.Which.CHARV, - Const.Which.CHARW, - Const.Which.CHARX, - Const.Which.CHARY, - Const.Which.CHARZ, - Const.Which.F1, - Const.Which.F2, - Const.Which.F3, - Const.Which.F4, - Const.Which.F5, - Const.Which.F6, - Const.Which.F7, - Const.Which.F8, - Const.Which.F9, - Const.Which.F10, - Const.Which.F11, - Const.Which.F12, - Const.Which.F13, - Const.Which.F14, - Const.Which.F15, - Const.Which.F16, - Const.Which.F17, - Const.Which.F18, - Const.Which.F19, - Const.Which.F20, - Const.Which.F21, - Const.Which.F22, - Const.Which.F23, - Const.Which.F24, - Const.Which.BROWSER_REFRESH + ttyu.WHICH.BACK, + ttyu.WHICH.TAB, + ttyu.WHICH.CLEAR, + ttyu.WHICH.ENTER, + ttyu.WHICH.SHIFT, + ttyu.WHICH.SPACE, + ttyu.WHICH.PRIOR, + ttyu.WHICH.NEXT, + ttyu.WHICH.END, + ttyu.WHICH.HOME, + ttyu.WHICH.LEFT, + ttyu.WHICH.UP, + ttyu.WHICH.RIGHT, + ttyu.WHICH.DOWN, + ttyu.WHICH.PRINT, + ttyu.WHICH.INSERT, + ttyu.WHICH.DELETE, + ttyu.WHICH.HELP, + ttyu.WHICH.CHAR0, + ttyu.WHICH.CHAR1, + ttyu.WHICH.CHAR2, + ttyu.WHICH.CHAR3, + ttyu.WHICH.CHAR4, + ttyu.WHICH.CHAR5, + ttyu.WHICH.CHAR6, + ttyu.WHICH.CHAR7, + ttyu.WHICH.CHAR8, + ttyu.WHICH.CHAR9, + ttyu.WHICH.CHARA, + ttyu.WHICH.CHARB, + ttyu.WHICH.CHARC, + ttyu.WHICH.CHARD, + ttyu.WHICH.CHARE, + ttyu.WHICH.CHARF, + ttyu.WHICH.CHARG, + ttyu.WHICH.CHARH, + ttyu.WHICH.CHARI, + ttyu.WHICH.CHARJ, + ttyu.WHICH.CHARK, + ttyu.WHICH.CHARL, + ttyu.WHICH.CHARM, + ttyu.WHICH.CHARN, + ttyu.WHICH.CHARO, + ttyu.WHICH.CHARP, + ttyu.WHICH.CHARQ, + ttyu.WHICH.CHARR, + ttyu.WHICH.CHARS, + ttyu.WHICH.CHART, + ttyu.WHICH.CHARU, + ttyu.WHICH.CHARV, + ttyu.WHICH.CHARW, + ttyu.WHICH.CHARX, + ttyu.WHICH.CHARY, + ttyu.WHICH.CHARZ, + ttyu.WHICH.F1, + ttyu.WHICH.F2, + ttyu.WHICH.F3, + ttyu.WHICH.F4, + ttyu.WHICH.F5, + ttyu.WHICH.F6, + ttyu.WHICH.F7, + ttyu.WHICH.F8, + ttyu.WHICH.F9, + ttyu.WHICH.F10, + ttyu.WHICH.F11, + ttyu.WHICH.F12, + ttyu.WHICH.F13, + ttyu.WHICH.F14, + ttyu.WHICH.F15, + ttyu.WHICH.F16, + ttyu.WHICH.F17, + ttyu.WHICH.F18, + ttyu.WHICH.F19, + ttyu.WHICH.F20, + ttyu.WHICH.F21, + ttyu.WHICH.F22, + ttyu.WHICH.F23, + ttyu.WHICH.F24, + ttyu.WHICH.BROWSER_REFRESH ]; -var current = []; - -module.exports = function(TTYUtil, expect) { - describe("TTYUtil `key` event handling", function() { - describe(".which", function() { - var ttyu; - - before(function() { - ttyu = new TTYUtil(); - ttyu.start(); - }); - - it.each(which, "should recognize character #%s", ['element'], - function(element, next) { - this.timeout(100); - ttyu.on(TTYUtil.EVENT.KEY, createTest(element, next)); - ttyu.emit(TTYUtil.KeyEvent(element, 0)); - }); - - afterEach(function() { - var el; - while((el = current.pop())) { - ttyu.removeListener(TTYUtil.EVENT.KEY, el); - } - }) - - after(function(done) { - var el; - while((el = current.pop())) { - ttyu.removeListener(TTYUtil.EVENT.KEY, el); - } - setTimeout(function() { - ttyu.destroy(); - done(); - }, 100); - }); - - function createTest(element, callback) { - var test = function(ev) { - is.expect.type.of(ev).to.be.equal("Object"); - - if(process.platform !== "win32" && - unix_required.indexOf(element) === -1) { - expect([element, Const.Which.UNKNOWN]).to. - include(ev.which); - } else { - expect(ev.which).to.be.equal(element); - } - callback(); - }; - current.push(test); - - return test; - } - }); +module.exports = function(cb) { + console.log("\r\ntest 'tests/key.js'\r"); + ttyu.start(); - // TODO (@bbuecherl) add tests for .ctrl + (function check(i, cb) { + var listener = function(ev) { + assert.equal(ev.which, unix_required[i], " which should be " + + unix_required[i]); + ttyu.off(ttyu.EVENT.KEY, listener); + if(i <= 0) cb(); else check(i - 1, cb); + }; + ttyu.on(ttyu.EVENT.KEY, listener); + ttyu.emit(ttyu.KeyEvent(unix_required[i], 0)); + })(unix_required.length - 1, function() { + ttyu.stop(); + console.log("test 'tests/key.js' passed\r"); + cb(); }); }; diff --git a/tests/mouse-down.js b/tests/mouse-down.js deleted file mode 100644 index 36ff1af..0000000 --- a/tests/mouse-down.js +++ /dev/null @@ -1,71 +0,0 @@ -require("it-each")({ testPerIteration: true}); -var is = require("node-is"); -var Const = require("../const"); -var mouse = [Const.Mouse.LEFT, Const.Mouse.LEFT2, - Const.Mouse.LEFT3, Const.Mouse.RIGHT]; - -function makeEvent() { - return [mouse[Math.floor(Math.random() * mouse.length)], - Math.round(Math.random() * 60), Math.round(Math.random() * 15), 0]; -} - -var current = []; -var mevents = []; - -for(var i = 0; i < 100; ++i) { - mevents.push(makeEvent()); -} - -module.exports = function(TTYUtil, expect) { - return function() { - var ttyu; - - before(function() { - ttyu = new TTYUtil(); - ttyu.start(); - }); - - it.each(mevents, "should recognize mouse input: %s", ['element'], - function(element, next) { - this.timeout(500); - ttyu.on(TTYUtil.EVENT.MOUSEDOWN, createTest(element, next)); - ttyu.emit(TTYUtil.MouseEvent(TTYUtil.EVENT.MOUSEDOWN, element[0], - element[1], element[2], element[3])); - }); - - afterEach(function() { - var el; - while((el = current.pop())) { - ttyu.removeListener(TTYUtil.EVENT.MOUSEDOWN, el); - } - }) - - after(function(done) { - var el; - while((el = current.pop())) { - ttyu.removeListener(TTYUtil.EVENT.MOUSEDOWN, el); - } - setTimeout(function() { - ttyu.destroy(); - done(); - }, 100); - }); - - function createTest(element, callback) { - var test = function(ev) { - is.expect.type.of(ev).to.be.equal("Object"); - expect(ev.button).to.be.equal(element[0]); - expect(ev.x).to.be.equal(element[1]); - expect(ev.y).to.be.equal(element[2]); - expect(ev.ctrl).to.be.equal(element[3]); - ttyu.removeListener(TTYUtil.EVENT.MOUSEDOWN, test); - callback(); - }; - current.push(test); - - return test; - } - - // TODO (@bbuecherl) add tests for .ctrl - }; -}; diff --git a/tests/mouse.js b/tests/mouse.js index 4a0a495..17c4899 100644 --- a/tests/mouse.js +++ b/tests/mouse.js @@ -1,5 +1,38 @@ -module.exports = function(TTYUtil, expect) { - describe("TTYUtil `mouse` event handling", function() { - describe("MOUSEDOWN", require("./mouse-down")(TTYUtil, expect)); +var ttyu = require("../index"); +var assert = require("assert"); +var mouseEvent = []; + +// mouse up & mouse down +for(var type in ttyu.MOUSE) { + mouseEvent.push([ttyu.EVENT.MOUSEUP, ttyu.MOUSE[type]]); + mouseEvent.push([ttyu.EVENT.MOUSEDOWN, ttyu.MOUSE[type]]); +} + +module.exports = function(cb) { + console.log("\r\ntest 'tests/mouse.js'\r"); + ttyu.start(); + + (function check(i, cb) { + var event = ttyu.MouseEvent.call(null, mouseEvent[i][0], + mouseEvent[i][1], Math.round(Math.random() * 10), + Math.round(Math.random() * 10), 0); + var listener = function(ev) { + console.log(JSON.stringify(ev)); + assert.equal(ev.type, event.type); + assert.equal(ev.button, event.button); + assert.equal(ev.x, event.x); + assert.equal(ev.y, event.y); + assert.equal(ev.ctrl, event.ctrl); + ttyu.off(event.type, listener); + console.log(" done!\r"); + if(i <= 0) cb(); else check(i - 1, cb); + }; + ttyu.on(event.type, listener); + ttyu.emit(event); + console.log("testing " + JSON.stringify(event) + " ... "); + })(mouseEvent.length - 1, function() { + ttyu.stop(); + console.log("test 'tests/mouse.js' passed\r"); + cb(); }); }; diff --git a/tests/run.js b/tests/run.js deleted file mode 100644 index 972dfef..0000000 --- a/tests/run.js +++ /dev/null @@ -1,13 +0,0 @@ -var TTYUtil = require("../export")(require("../build/Release/ttyu")); -var expect = require("chai").expect; - -// test specs -require("./spec")(TTYUtil, expect); - -// test input listeners -require("./key")(TTYUtil, expect); -require("./mouse")(TTYUtil, expect); -//require("./signal")(TTYUtil, expect); - -// test output functions -//require("./output")(TTYUtil, expect); diff --git a/tests/signal.js b/tests/signal.js deleted file mode 100644 index d968367..0000000 --- a/tests/signal.js +++ /dev/null @@ -1,5 +0,0 @@ -var is = require("node-is"); - -module.exports = function(TTYUtil, expect) { - -}; diff --git a/tests/spec.js b/tests/spec.js deleted file mode 100644 index 2ec4ed7..0000000 --- a/tests/spec.js +++ /dev/null @@ -1,93 +0,0 @@ -var is = require("node-is"); - -module.exports = function(TTYUtil, expect) { - describe("TTYUtil Specification", function() { - - describe("TTYUtil.prototype", function() { - var keys = Object.keys(TTYUtil.prototype); - - it("should have 13 enumerable properties", function() { - expect(keys.length).to.be.equal(13); - }); - - it("#on() should be an enumerable function", function() { - expect(keys.indexOf("on")).to.be.not.equal(-1); - is.expect.type.of(TTYUtil.prototype.on) - .to.be.equal("Function"); - }); - - it("#off() should be an enumerable function", function() { - expect(keys.indexOf("off")).to.be.not.equal(-1); - is.expect.type.of(TTYUtil.prototype.off) - .to.be.equal("Function"); - }); - - it("#removeListener() should be an enumerable function", - function() { - expect(keys.indexOf("removeListener")).to.be.not.equal(-1); - is.expect.type.of(TTYUtil.prototype.removeListener) - .to.be.equal("Function"); - }); - - it("#emit() should be an enumerable function", function() { - expect(keys.indexOf("emit")).to.be.not.equal(-1); - is.expect.type.of(TTYUtil.prototype.emit) - .to.be.equal("Function"); - }); - - it("#start() should be an enumerable function", function() { - expect(keys.indexOf("start")).to.be.not.equal(-1); - is.expect.type.of(TTYUtil.prototype.start) - .to.be.equal("Function"); - }); - - it("#pause() should be an enumerable function", function() { - expect(keys.indexOf("pause")).to.be.not.equal(-1); - is.expect.type.of(TTYUtil.prototype.pause) - .to.be.equal("Function"); - }); - - it("#destroy() should be an enumerable function", function() { - expect(keys.indexOf("destroy")).to.be.not.equal(-1); - is.expect.type.of(TTYUtil.prototype.destroy) - .to.be.equal("Function"); - }); - - it("#goto() should be an enumerable function", function() { - expect(keys.indexOf("goto")).to.be.not.equal(-1); - is.expect.type.of(TTYUtil.prototype.goto) - .to.be.equal("Function"); - }); - - it("#write() should be an enumerable function", function() { - expect(keys.indexOf("write")).to.be.not.equal(-1); - is.expect.type.of(TTYUtil.prototype.write) - .to.be.equal("Function"); - }); - - it("#prepare() should be an enumerable function", function() { - expect(keys.indexOf("prepare")).to.be.not.equal(-1); - is.expect.type.of(TTYUtil.prototype.prepare) - .to.be.equal("Function"); - }); - - it("#beep() should be an enumerable function", function() { - expect(keys.indexOf("beep")).to.be.not.equal(-1); - is.expect.type.of(TTYUtil.prototype.beep) - .to.be.equal("Function"); - }); - - it("#clear() should be an enumerable function", function() { - expect(keys.indexOf("clear")).to.be.not.equal(-1); - is.expect.type.of(TTYUtil.prototype.clear) - .to.be.equal("Function"); - }); - - it("#color() should be an enumerable function", function() { - expect(keys.indexOf("color")).to.be.not.equal(-1); - is.expect.type.of(TTYUtil.prototype.color) - .to.be.equal("Function"); - }); - }); - }); -};