From 9d1cefeaabbb70a7ab8aaa4c06b3241e6bc7c99f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Mon, 4 Sep 2017 01:46:00 +0200 Subject: [PATCH 1/4] done client-side mouse reporting --- html_orig/js/app.js | 77 ++++++++++++++++++++++++++++++++---- html_orig/jssrc/keymaster.js | 9 +++++ html_orig/jssrc/term.js | 68 +++++++++++++++++++++++++++---- 3 files changed, 140 insertions(+), 14 deletions(-) diff --git a/html_orig/js/app.js b/html_orig/js/app.js index edd6e14..7844f4e 100644 --- a/html_orig/js/app.js +++ b/html_orig/js/app.js @@ -770,6 +770,14 @@ for(k in _mods) _mods[k] = event[modifierMap[k]]; }; + function isModifierPressed(mod) { + if (mod=='control'||mod=='ctrl') return _mods[17]; + if (mod=='shift') return _mods[16]; + if (mod=='meta') return _mods[91]; + if (mod=='alt') return _mods[18]; + return false; + } + // handle keydown event function dispatch(event) { var key, handler, k, i, modifiersMatch, scope; @@ -995,6 +1003,7 @@ global.key.deleteScope = deleteScope; global.key.filter = filter; global.key.isPressed = isPressed; + global.key.isModifier = isModifierPressed; global.key.getPressedKeyCodes = getPressedKeyCodes; global.key.noConflict = noConflict; global.key.unbind = unbindKey; @@ -1681,8 +1690,21 @@ var Screen = (function () { (function() { var x = i % W; var y = Math.floor(i / W); - e.addEventListener('click', function () { - Input.onTap(y, x); + e.addEventListener('mouseenter', function (evt) { + Input.onMouseMove(x, y); + }); + e.addEventListener('mousedown', function (evt) { + Input.onMouseDown(x, y, evt.button+1); + }); + e.addEventListener('mouseup', function (evt) { + Input.onMouseUp(x, y, evt.button+1); + }); + e.addEventListener('contextmenu', function (evt) { + evt.preventDefault(); + }); + e.addEventListener('mousewheel', function (evt) { + Input.onMouseWheel(x, y, evt.deltaY>0?1:-1); + return false; }); })(); @@ -2003,10 +2025,6 @@ var Input = (function() { Conn.send("STR:"+str); } - function sendPosMsg(y, x) { - Conn.send("TAP:"+y+','+x); - } - function sendBtnMsg(n) { Conn.send("BTN:"+n); } @@ -2129,6 +2147,10 @@ var Input = (function() { _bindFnKeys(); } + var mb1 = 0; + var mb2 = 0; + var mb3 = 0; + function init() { _initKeys(); @@ -2138,11 +2160,30 @@ var Input = (function() { sendBtnMsg(+this.dataset['n']); }); }); + + window.addEventListener('mousedown', function(evt) { + if (evt.button == 0) mb1 = 1; + if (evt.button == 1) mb2 = 1; + if (evt.button == 2) mb3 = 1; + }); + + window.addEventListener('mouseup', function(evt) { + if (evt.button == 0) mb1 = 0; + if (evt.button == 1) mb2 = 0; + if (evt.button == 2) mb3 = 0; + }); + } + + function packModifiersForMouse() { + return (key.isModifier('ctrl')?1:0) | + (key.isModifier('shift')?2:0) | + (key.isModifier('alt')?4:0) | + (key.isModifier('meta')?8:0); } return { init: init, - onTap: sendPosMsg, + // onTap: sendPosMsg, sendString: sendStrMsg, setAlts: function(cu, np, fn) { if (opts.cu_alt != cu || opts.np_alt != np || opts.fn_alt != fn) { @@ -2154,6 +2195,28 @@ var Input = (function() { _bindFnKeys(); } }, + onMouseMove: function(x, y) { + // TODO gather held buttons & key modifiers + var b = (mb1?1:0) | (mb2?2:0) | (mb3?4:0); + var m = packModifiersForMouse(); + Conn.send("MM:"+y+','+x+','+b+','+m); + }, + onMouseDown: function(x, y, which) { + if(which>3) return; + var m = packModifiersForMouse(); + Conn.send("MP:"+y+','+x+','+which+','+m); + }, + onMouseUp: function(x, y, which) { + if(which>3) return; + var m = packModifiersForMouse(); + Conn.send("MR:"+y+','+x+','+which+','+m); + }, + onMouseWheel: function(x, y, dir) { + // -1 ... btn 4 (away from user) + // +1 ... btn 5 (towards user) + var m = packModifiersForMouse(); + Conn.send("MP:"+y+','+x+','+(dir<0?4:5)+','+m); + }, }; })(); diff --git a/html_orig/jssrc/keymaster.js b/html_orig/jssrc/keymaster.js index 254620b..88b9629 100644 --- a/html_orig/jssrc/keymaster.js +++ b/html_orig/jssrc/keymaster.js @@ -67,6 +67,14 @@ for(k in _mods) _mods[k] = event[modifierMap[k]]; }; + function isModifierPressed(mod) { + if (mod=='control'||mod=='ctrl') return _mods[17]; + if (mod=='shift') return _mods[16]; + if (mod=='meta') return _mods[91]; + if (mod=='alt') return _mods[18]; + return false; + } + // handle keydown event function dispatch(event) { var key, handler, k, i, modifiersMatch, scope; @@ -292,6 +300,7 @@ global.key.deleteScope = deleteScope; global.key.filter = filter; global.key.isPressed = isPressed; + global.key.isModifier = isModifierPressed; global.key.getPressedKeyCodes = getPressedKeyCodes; global.key.noConflict = noConflict; global.key.unbind = unbindKey; diff --git a/html_orig/jssrc/term.js b/html_orig/jssrc/term.js index 313b8e1..c914349 100644 --- a/html_orig/jssrc/term.js +++ b/html_orig/jssrc/term.js @@ -124,8 +124,21 @@ var Screen = (function () { (function() { var x = i % W; var y = Math.floor(i / W); - e.addEventListener('click', function () { - Input.onTap(y, x); + e.addEventListener('mouseenter', function (evt) { + Input.onMouseMove(x, y); + }); + e.addEventListener('mousedown', function (evt) { + Input.onMouseDown(x, y, evt.button+1); + }); + e.addEventListener('mouseup', function (evt) { + Input.onMouseUp(x, y, evt.button+1); + }); + e.addEventListener('contextmenu', function (evt) { + evt.preventDefault(); + }); + e.addEventListener('mousewheel', function (evt) { + Input.onMouseWheel(x, y, evt.deltaY>0?1:-1); + return false; }); })(); @@ -446,10 +459,6 @@ var Input = (function() { Conn.send("STR:"+str); } - function sendPosMsg(y, x) { - Conn.send("TAP:"+y+','+x); - } - function sendBtnMsg(n) { Conn.send("BTN:"+n); } @@ -572,6 +581,10 @@ var Input = (function() { _bindFnKeys(); } + var mb1 = 0; + var mb2 = 0; + var mb3 = 0; + function init() { _initKeys(); @@ -581,11 +594,30 @@ var Input = (function() { sendBtnMsg(+this.dataset['n']); }); }); + + window.addEventListener('mousedown', function(evt) { + if (evt.button == 0) mb1 = 1; + if (evt.button == 1) mb2 = 1; + if (evt.button == 2) mb3 = 1; + }); + + window.addEventListener('mouseup', function(evt) { + if (evt.button == 0) mb1 = 0; + if (evt.button == 1) mb2 = 0; + if (evt.button == 2) mb3 = 0; + }); + } + + function packModifiersForMouse() { + return (key.isModifier('ctrl')?1:0) | + (key.isModifier('shift')?2:0) | + (key.isModifier('alt')?4:0) | + (key.isModifier('meta')?8:0); } return { init: init, - onTap: sendPosMsg, + // onTap: sendPosMsg, sendString: sendStrMsg, setAlts: function(cu, np, fn) { if (opts.cu_alt != cu || opts.np_alt != np || opts.fn_alt != fn) { @@ -597,6 +629,28 @@ var Input = (function() { _bindFnKeys(); } }, + onMouseMove: function(x, y) { + // TODO gather held buttons & key modifiers + var b = (mb1?1:0) | (mb2?2:0) | (mb3?4:0); + var m = packModifiersForMouse(); + Conn.send("MM:"+y+','+x+','+b+','+m); + }, + onMouseDown: function(x, y, which) { + if(which>3) return; + var m = packModifiersForMouse(); + Conn.send("MP:"+y+','+x+','+which+','+m); + }, + onMouseUp: function(x, y, which) { + if(which>3) return; + var m = packModifiersForMouse(); + Conn.send("MR:"+y+','+x+','+which+','+m); + }, + onMouseWheel: function(x, y, dir) { + // -1 ... btn 4 (away from user) + // +1 ... btn 5 (towards user) + var m = packModifiersForMouse(); + Conn.send("MP:"+y+','+x+','+(dir<0?4:5)+','+m); + }, }; })(); From 16a36a3d264de70b0cc2fbdfcb7b4ac48363645d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Mon, 4 Sep 2017 01:49:17 +0200 Subject: [PATCH 2/4] cleaning --- html_orig/js/app.js | 13 ++++++------- html_orig/jssrc/term.js | 13 ++++++------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/html_orig/js/app.js b/html_orig/js/app.js index 7844f4e..bc27b37 100644 --- a/html_orig/js/app.js +++ b/html_orig/js/app.js @@ -2196,20 +2196,19 @@ var Input = (function() { } }, onMouseMove: function(x, y) { - // TODO gather held buttons & key modifiers var b = (mb1?1:0) | (mb2?2:0) | (mb3?4:0); var m = packModifiersForMouse(); Conn.send("MM:"+y+','+x+','+b+','+m); }, - onMouseDown: function(x, y, which) { - if(which>3) return; + onMouseDown: function(x, y, b) { + if(b>3) return; var m = packModifiersForMouse(); - Conn.send("MP:"+y+','+x+','+which+','+m); + Conn.send("MP:"+y+','+x+','+b+','+m); }, - onMouseUp: function(x, y, which) { - if(which>3) return; + onMouseUp: function(x, y, b) { + if(b>3) return; var m = packModifiersForMouse(); - Conn.send("MR:"+y+','+x+','+which+','+m); + Conn.send("MR:"+y+','+x+','+b+','+m); }, onMouseWheel: function(x, y, dir) { // -1 ... btn 4 (away from user) diff --git a/html_orig/jssrc/term.js b/html_orig/jssrc/term.js index c914349..4722c2b 100644 --- a/html_orig/jssrc/term.js +++ b/html_orig/jssrc/term.js @@ -630,20 +630,19 @@ var Input = (function() { } }, onMouseMove: function(x, y) { - // TODO gather held buttons & key modifiers var b = (mb1?1:0) | (mb2?2:0) | (mb3?4:0); var m = packModifiersForMouse(); Conn.send("MM:"+y+','+x+','+b+','+m); }, - onMouseDown: function(x, y, which) { - if(which>3) return; + onMouseDown: function(x, y, b) { + if(b>3) return; var m = packModifiersForMouse(); - Conn.send("MP:"+y+','+x+','+which+','+m); + Conn.send("MP:"+y+','+x+','+b+','+m); }, - onMouseUp: function(x, y, which) { - if(which>3) return; + onMouseUp: function(x, y, b) { + if(b>3) return; var m = packModifiersForMouse(); - Conn.send("MR:"+y+','+x+','+which+','+m); + Conn.send("MR:"+y+','+x+','+b+','+m); }, onMouseWheel: function(x, y, dir) { // -1 ... btn 4 (away from user) From 58ed6098c430304f209552c487a6c4b71a84309f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Mon, 4 Sep 2017 22:03:08 +0200 Subject: [PATCH 3/4] untested impl of mouse tracking modes --- CMakeLists.txt | 2 +- html_orig/js/app.js | 89 ++++++++++++++++++------- html_orig/jssrc/term.js | 89 ++++++++++++++++++------- user/apars_csi.c | 20 ++++-- user/apars_osc.c | 14 +--- user/cgi_sockets.c | 143 +++++++++++++++++++++++++++------------- user/jstring.c | 41 ++++++++++++ user/jstring.h | 29 ++++++++ user/screen.c | 43 ++++++------ user/screen.h | 37 +++++++---- 10 files changed, 354 insertions(+), 153 deletions(-) create mode 100644 user/jstring.c create mode 100644 user/jstring.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ddd05cf..cd28108 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -141,7 +141,7 @@ set(SOURCE_FILES user/apars_osc.c user/apars_osc.h user/apars_dcs.c - user/apars_dcs.h user/uart_buffer.c user/uart_buffer.h) + user/apars_dcs.h user/uart_buffer.c user/uart_buffer.h user/jstring.c user/jstring.h) include_directories(include) include_directories(user) diff --git a/html_orig/js/app.js b/html_orig/js/app.js index bc27b37..3c64eb3 100644 --- a/html_orig/js/app.js +++ b/html_orig/js/app.js @@ -1564,6 +1564,41 @@ function tr(key) { return _tr[key] || '?'+key+'?'; } w.init = wifiInit; w.startScanning = startScanning; })(window.WiFi = {}); +/** Decode two-byte number */ +function parse2B(s, i) { + return (s.charCodeAt(i++) - 1) + (s.charCodeAt(i) - 1) * 127; +} + +/** Decode three-byte number */ +function parse3B(s, i) { + return (s.charCodeAt(i) - 1) + (s.charCodeAt(i+1) - 1) * 127 + (s.charCodeAt(i+2) - 1) * 127 * 127; +} + +function Chr(n) { + return String.fromCharCode(n); +} + +function encode2B(n) { + var lsb, msb; + lsb = (n % 127); + n = ((n - lsb) / 127); + lsb += 1; + msb = (n + 1); + return Chr(lsb) + Chr(msb); +} + +function encode3B(n) { + var lsb, msb, xsb; + lsb = (n % 127); + n = (n - lsb) / 127; + lsb += 1; + msb = (n % 127); + n = (n - msb) / 127; + msb += 1; + xsb = (n + 1); + return Chr(lsb) + Chr(msb) + Chr(xsb); +} + var Screen = (function () { var W = 0, H = 0; // dimensions var inited = false; @@ -1756,16 +1791,6 @@ var Screen = (function () { inited = true; } - /** Decode two-byte number */ - function parse2B(s, i) { - return (s.charCodeAt(i++) - 1) + (s.charCodeAt(i) - 1) * 127; - } - - /** Decode three-byte number */ - function parse3B(s, i) { - return (s.charCodeAt(i) - 1) + (s.charCodeAt(i+1) - 1) * 127 + (s.charCodeAt(i+2) - 1) * 127 * 127; - } - var SEQ_SET_COLOR_ATTR = 1; var SEQ_REPEAT = 2; var SEQ_SET_COLOR = 3; @@ -2013,7 +2038,22 @@ var Conn = (function() { }; })(); -/** User input */ +/** + * User input + * + * --- Rx messages: --- + * S - screen content (binary encoding of the entire screen with simple compression) + * T - text labels - Title and buttons, \0x01-separated + * B - beep + * . - heartbeat + * + * --- Tx messages --- + * s - string + * b - action button + * p - mb press + * r - mb release + * m - mouse move + */ var Input = (function() { var opts = { np_alt: false, @@ -2022,11 +2062,11 @@ var Input = (function() { }; function sendStrMsg(str) { - Conn.send("STR:"+str); + Conn.send("s"+str); } function sendBtnMsg(n) { - Conn.send("BTN:"+n); + Conn.send("b"+Chr(n)); } function fa(alt, normal) { @@ -2195,26 +2235,27 @@ var Input = (function() { _bindFnKeys(); } }, - onMouseMove: function(x, y) { - var b = (mb1?1:0) | (mb2?2:0) | (mb3?4:0); + onMouseMove: function (x, y) { + var b = mb1 ? 1 : mb2 ? 2 : mb3 ? 3 : 0; var m = packModifiersForMouse(); - Conn.send("MM:"+y+','+x+','+b+','+m); + Conn.send("m" + encode2B(y) + encode2B(x) + encode2B(b) + encode2B(m)); }, - onMouseDown: function(x, y, b) { - if(b>3) return; + onMouseDown: function (x, y, b) { + if (b > 3 || b < 1) return; var m = packModifiersForMouse(); - Conn.send("MP:"+y+','+x+','+b+','+m); + Conn.send("p" + encode2B(y) + encode2B(x) + encode2B(b) + encode2B(m)); }, - onMouseUp: function(x, y, b) { - if(b>3) return; + onMouseUp: function (x, y, b) { + if (b > 3 || b < 1) return; var m = packModifiersForMouse(); - Conn.send("MR:"+y+','+x+','+b+','+m); + Conn.send("r" + encode2B(y) + encode2B(x) + encode2B(b) + encode2B(m)); }, - onMouseWheel: function(x, y, dir) { + onMouseWheel: function (x, y, dir) { // -1 ... btn 4 (away from user) // +1 ... btn 5 (towards user) var m = packModifiersForMouse(); - Conn.send("MP:"+y+','+x+','+(dir<0?4:5)+','+m); + var b = (dir < 0 ? 4 : 5); + Conn.send("p" + encode2B(y) + encode2B(x) + encode2B(b) + encode2B(m)); }, }; })(); diff --git a/html_orig/jssrc/term.js b/html_orig/jssrc/term.js index 4722c2b..14d1bfa 100644 --- a/html_orig/jssrc/term.js +++ b/html_orig/jssrc/term.js @@ -1,3 +1,38 @@ +/** Decode two-byte number */ +function parse2B(s, i) { + return (s.charCodeAt(i++) - 1) + (s.charCodeAt(i) - 1) * 127; +} + +/** Decode three-byte number */ +function parse3B(s, i) { + return (s.charCodeAt(i) - 1) + (s.charCodeAt(i+1) - 1) * 127 + (s.charCodeAt(i+2) - 1) * 127 * 127; +} + +function Chr(n) { + return String.fromCharCode(n); +} + +function encode2B(n) { + var lsb, msb; + lsb = (n % 127); + n = ((n - lsb) / 127); + lsb += 1; + msb = (n + 1); + return Chr(lsb) + Chr(msb); +} + +function encode3B(n) { + var lsb, msb, xsb; + lsb = (n % 127); + n = (n - lsb) / 127; + lsb += 1; + msb = (n % 127); + n = (n - msb) / 127; + msb += 1; + xsb = (n + 1); + return Chr(lsb) + Chr(msb) + Chr(xsb); +} + var Screen = (function () { var W = 0, H = 0; // dimensions var inited = false; @@ -190,16 +225,6 @@ var Screen = (function () { inited = true; } - /** Decode two-byte number */ - function parse2B(s, i) { - return (s.charCodeAt(i++) - 1) + (s.charCodeAt(i) - 1) * 127; - } - - /** Decode three-byte number */ - function parse3B(s, i) { - return (s.charCodeAt(i) - 1) + (s.charCodeAt(i+1) - 1) * 127 + (s.charCodeAt(i+2) - 1) * 127 * 127; - } - var SEQ_SET_COLOR_ATTR = 1; var SEQ_REPEAT = 2; var SEQ_SET_COLOR = 3; @@ -447,7 +472,22 @@ var Conn = (function() { }; })(); -/** User input */ +/** + * User input + * + * --- Rx messages: --- + * S - screen content (binary encoding of the entire screen with simple compression) + * T - text labels - Title and buttons, \0x01-separated + * B - beep + * . - heartbeat + * + * --- Tx messages --- + * s - string + * b - action button + * p - mb press + * r - mb release + * m - mouse move + */ var Input = (function() { var opts = { np_alt: false, @@ -456,11 +496,11 @@ var Input = (function() { }; function sendStrMsg(str) { - Conn.send("STR:"+str); + Conn.send("s"+str); } function sendBtnMsg(n) { - Conn.send("BTN:"+n); + Conn.send("b"+Chr(n)); } function fa(alt, normal) { @@ -629,26 +669,27 @@ var Input = (function() { _bindFnKeys(); } }, - onMouseMove: function(x, y) { - var b = (mb1?1:0) | (mb2?2:0) | (mb3?4:0); + onMouseMove: function (x, y) { + var b = mb1 ? 1 : mb2 ? 2 : mb3 ? 3 : 0; var m = packModifiersForMouse(); - Conn.send("MM:"+y+','+x+','+b+','+m); + Conn.send("m" + encode2B(y) + encode2B(x) + encode2B(b) + encode2B(m)); }, - onMouseDown: function(x, y, b) { - if(b>3) return; + onMouseDown: function (x, y, b) { + if (b > 3 || b < 1) return; var m = packModifiersForMouse(); - Conn.send("MP:"+y+','+x+','+b+','+m); + Conn.send("p" + encode2B(y) + encode2B(x) + encode2B(b) + encode2B(m)); }, - onMouseUp: function(x, y, b) { - if(b>3) return; + onMouseUp: function (x, y, b) { + if (b > 3 || b < 1) return; var m = packModifiersForMouse(); - Conn.send("MR:"+y+','+x+','+b+','+m); + Conn.send("r" + encode2B(y) + encode2B(x) + encode2B(b) + encode2B(m)); }, - onMouseWheel: function(x, y, dir) { + onMouseWheel: function (x, y, dir) { // -1 ... btn 4 (away from user) // +1 ... btn 5 (towards user) var m = packModifiersForMouse(); - Conn.send("MP:"+y+','+x+','+(dir<0?4:5)+','+m); + var b = (dir < 0 ? 4 : 5); + Conn.send("p" + encode2B(y) + encode2B(x) + encode2B(b) + encode2B(m)); }, }; })(); diff --git a/user/apars_csi.c b/user/apars_csi.c index c1ce4f5..1ad43c1 100644 --- a/user/apars_csi.c +++ b/user/apars_csi.c @@ -451,16 +451,24 @@ static void ICACHE_FLASH_ATTR do_csi_privattr(CSI_Data *opts) // - discard repeated keypress events between keydown and keyup. ansi_noimpl("Auto-repeat toggle"); } - else if (n == 9 || (n >= 1000 && n <= 1006)) { - // TODO mouse - // 1000 - C11 mouse - Send Mouse X & Y on button press and release. - // 1001 - Hilite mouse tracking + else if (n == 9 || (n >= 1000 && n <= 1006) || n == 1015) { + // 9 - X10 tracking + // 1000 - X11 mouse - Send Mouse X & Y on button press and release. + // 1001 - Hilite mouse tracking - not impl // 1002 - Cell Motion Mouse Tracking // 1003 - All Motion Mouse Tracking // 1004 - Send FocusIn/FocusOut events - // 1005 - Enable UTF-8 Mouse Mode + // 1005 - Enable UTF-8 Mouse Mode - we implement this as an alias to X10 mode // 1006 - SGR mouse mode - ansi_noimpl("Mouse tracking"); + + if (n == 9) mouse_tracking.mode = yn ? MTM_X10 : MTM_NONE; + else if (n == 1000) mouse_tracking.mode = yn ? MTM_NORMAL : MTM_NONE; + else if (n == 1002) mouse_tracking.mode = yn ? MTM_BUTTON_MOTION : MTM_NONE; + else if (n == 1003) mouse_tracking.mode = yn ? MTM_ANY_MOTION : MTM_NONE; + else if (n == 1004) mouse_tracking.focus_tracking = yn; + else if (n == 1005) mouse_tracking.encoding = yn ? MTE_UTF8 : MTE_SIMPLE; + else if (n == 1006) mouse_tracking.encoding = yn ? MTE_SGR : MTE_SIMPLE; + else if (n == 1015) mouse_tracking.encoding = yn ? MTE_URXVT : MTE_SIMPLE; } else if (n == 12) { // TODO Cursor blink on/off diff --git a/user/apars_osc.c b/user/apars_osc.c index 18f9855..fc2b27b 100644 --- a/user/apars_osc.c +++ b/user/apars_osc.c @@ -15,18 +15,6 @@ #include "screen.h" #include "ansi_parser.h" -/** - * Helper function to set terminal button label - * @param num - button number 1-5 - * @param str - button text - */ -static void ICACHE_FLASH_ATTR -set_button_text(int num, const char *str) -{ - strncpy(termconf_scratch.btn[num-1], str, TERM_BTN_LEN); - screen_notifyChange(CHANGE_LABELS); -} - /** * Helper function to parse incoming OSC (Operating System Control) * @param buffer - the OSC body (after OSC and before ST) @@ -52,7 +40,7 @@ apars_handle_osc(const char *buffer) screen_set_title(buffer); } else if (n >= 81 && n <= 85) { // numbers chosen to not collide with any xterm supported codes - set_button_text(n - 80, buffer); + screen_set_button_text(n - 80, buffer); } else { ansi_noimpl("OSC %d ; %s ST", n, buffer); diff --git a/user/cgi_sockets.c b/user/cgi_sockets.c index c3a164a..ded74f7 100644 --- a/user/cgi_sockets.c +++ b/user/cgi_sockets.c @@ -7,6 +7,7 @@ #include "screen.h" #include "uart_buffer.h" #include "ansi_parser.h" +#include "jstring.h" #define LOOPBACK 0 @@ -118,75 +119,123 @@ void ICACHE_FLASH_ATTR screen_notifyChange(ScreenNotifyChangeTopic topic) } } +void ICACHE_FLASH_ATTR sendMouseAction(char evt, int y, int x, int button, u8 mods) +{ + bool ctrl = (mods & 1) > 0; + bool shift = (mods & 2) > 0; + bool alt = (mods & 4) > 0; + bool meta = (mods & 8) > 0; + enum MTM mtm = mouse_tracking.mode; + enum MTE mte = mouse_tracking.encoding; + + // No message on release in X10 mode + if (mtm == MTM_X10 && button == 0) { + return; + } + + if (evt == 'm' && mtm != MTM_BUTTON_MOTION && mtm != MTM_ANY_MOTION) { + return; + } + + if (evt == 'm' && mtm == MTM_BUTTON_MOTION && button == 0) { + return; + } + + int eventcode = 0; + + if (mtm == MTM_X10) { + eventcode = button; + } + else { + if (button == 0 || (evt == 'r' && mte != MTE_SGR)) eventcode = 3; // release + else if (button == 1) eventcode = 0; + else if (button == 2) eventcode = 1; + else if (button == 3) eventcode = 2; + else if (button == 4) eventcode = 64; + else if (button == 5) eventcode = 65; + + if (shift) eventcode |= 4; + if (alt || meta) eventcode |= 8; + if (ctrl) eventcode |= 16; + + if (mtm == MTM_BUTTON_MOTION || mtm == MTM_ANY_MOTION) { + if (evt == 'm') { + eventcode |= 32; + } + } + } + + // Encode + char buf[20]; + buf[0] = 0; + if (mte == MTE_SIMPLE || mte == MTE_UTF8) { + // strictly, for UTF8 this will break if any coord is over 127, + // but that is unlikely due to screen size limitations in ESPTerm + sprintf(buf, "\x1b[M%c%c%c", (u8)(32+eventcode), (u8)(32+x), (u8)(32+y)); + } + else if (mte == MTE_SGR) { + sprintf(buf, "\x1b[%d;%d;%d%c", eventcode, x, y, evt == 'p' ? 'M' : 'm'); + } + else if (mte == MTE_URXVT) { + sprintf(buf, "\x1b[%d;%d;%dM", (u8)(32+eventcode), (u8)(32+x), (u8)(32+y)); + } + + UART_SendAsync(buf, -1); +} + /** Socket received a message */ void ICACHE_FLASH_ATTR updateSockRx(Websock *ws, char *data, int len, int flags) { - char buf[20]; // Add terminator if missing (seems to randomly happen) data[len] = 0; - // TODO re-implement those, use single byte markers and B2 encoding - ws_dbg("Sock RX str: %s, len %d", data, len); - if (strstarts(data, "STR:")) { + int y, x, m, b; + u8 btnNum; + + char c = data[0]; + switch (c) { + case 's': // pass string verbatim #if LOOPBACK for(int i=4;i 0 && btnNum < 10) { - UART_SendAsync((const char *) &btnNum, 1); - } - } - else if (strstarts(data, "TAP:")) { - // this comes in as 0-based - - int y=0, x=0; - - char *pc=data+4; - char c; - int phase=0; - - while((c=*pc++) != '\0') { - if (c==','||c==';') { - phase++; - } - else if (c>='0' && c<='9') { - if (phase==0) { - y=y*10+(c-'0'); - } else { - x=x*10+(c-'0'); - } + break; + case 'b': + // action button press + btnNum = (u8) (data[1]); + if (btnNum > 0 && btnNum < 10) { + UART_SendAsync((const char *) &btnNum, 1); // TODO this is where we use user-configured codes } - } - - if (!screen_isCoordValid(y, x)) { - ws_warn("Mouse input at invalid coordinates"); - return; - } - - ws_dbg("Screen clicked at row %d, col %d", y+1, x+1); - - // Send as 1-based to user - sprintf(buf, "\033[%d;%dM", y+1, x+1); - UART_SendAsync(buf, -1); - } - else { - ws_warn("Bad command."); + break; + case 'm': + case 'p': + case 'r': + if (mouse_tracking.mode == MTM_NONE) break; // no need to parse, not enabled + + // mouse move + y = parse2B(data+1); // row, 0-based + x = parse2B(data+3); // column, 0-based + b = parse2B(data+5); // mouse button, 0 = none, 1-5 = button number + m = parse2B(data+7); // modifier keys held + + sendMouseAction(c,y,x,b,m); + break; + default: + ws_warn("Bad command."); } } void ICACHE_FLASH_ATTR heartbeatTimCb(void *unused) { if (notify_available) { + // Heartbeat packet - indicate we're still connected + // JS reloads the page if heartbeat is lost for a couple seconds cgiWebsockBroadcast(URL_WS_UPDATE, ".", 1, 0); } } diff --git a/user/jstring.c b/user/jstring.c new file mode 100644 index 0000000..f3057dc --- /dev/null +++ b/user/jstring.c @@ -0,0 +1,41 @@ +// +// Created by MightyPork on 2017/09/04. +// + +#include "jstring.h" + +void ICACHE_FLASH_ATTR +encode2B(u16 number, WordB2 *stru) +{ + stru->lsb = (u8) (number % 127); + number = (u16) ((number - stru->lsb) / 127); + stru->lsb += 1; + + stru->msb = (u8) (number + 1); +} + +void ICACHE_FLASH_ATTR +encode3B(u32 number, WordB3 *stru) +{ + stru->lsb = (u8) (number % 127); + number = (number - stru->lsb) / 127; + stru->lsb += 1; + + stru->msb = (u8) (number % 127); + number = (number - stru->msb) / 127; + stru->msb += 1; + + stru->xsb = (u8) (number + 1); +} + +u16 ICACHE_FLASH_ATTR +parse2B(const char *str) +{ + return (u16) ((str[0] - 1) + (str[1] - 1) * 127); +} + +u32 ICACHE_FLASH_ATTR +parse3B(const char *str) +{ + return (u32) ((str[0] - 1) + (str[1] - 1) * 127 + (str[2] - 1) * 127 * 127); +} diff --git a/user/jstring.h b/user/jstring.h new file mode 100644 index 0000000..8d99286 --- /dev/null +++ b/user/jstring.h @@ -0,0 +1,29 @@ +// +// Created by MightyPork on 2017/09/04. +// + +#ifndef ESPTERM_JSTRING_H +#define ESPTERM_JSTRING_H + +#include + +typedef struct { + u8 lsb; + u8 msb; +} WordB2; + +typedef struct { + u8 lsb; + u8 msb; + u8 xsb; +} WordB3; + +void encode2B(u16 number, WordB2 *stru); + +void encode3B(u32 number, WordB3 *stru); + +u16 parse2B(const char *str); + +u32 parse3B(const char *str); + +#endif //ESPTERM_JSTRING_H diff --git a/user/screen.c b/user/screen.c index 5c29a53..7ea042f 100644 --- a/user/screen.c +++ b/user/screen.c @@ -5,10 +5,13 @@ #include "sgr.h" #include "ascii.h" #include "apars_logging.h" +#include "jstring.h" TerminalConfigBundle * const termconf = &persist.current.termconf; TerminalConfigBundle termconf_scratch; +MouseTrackingConfig mouse_tracking; + // forward declare static void utf8_remap(char* out, char g, char charset); @@ -255,6 +258,10 @@ screen_reset(void) scr.vm0 = 0; scr.vm1 = H-1; + mouse_tracking.encoding = MTE_SIMPLE; + mouse_tracking.focus_tracking = false; + mouse_tracking.mode = MTM_NONE; + // size is left unchanged screen_clear(CLEAR_ALL); @@ -627,6 +634,18 @@ screen_set_title(const char *title) screen_notifyChange(CHANGE_LABELS); } +/** + * Helper function to set terminal button label + * @param num - button number 1-5 + * @param str - button text + */ +void ICACHE_FLASH_ATTR +screen_set_button_text(int num, const char *text) +{ + strncpy(termconf_scratch.btn[num-1], text, TERM_BTN_LEN); + screen_notifyChange(CHANGE_LABELS); +} + /** * Shift screen upwards */ @@ -1307,30 +1326,6 @@ struct ScreenSerializeState { int index; }; -void ICACHE_FLASH_ATTR -encode2B(u16 number, WordB2 *stru) -{ - stru->lsb = (u8) (number % 127); - number = (u16) ((number - stru->lsb) / 127); - stru->lsb += 1; - - stru->msb = (u8) (number + 1); -} - -void ICACHE_FLASH_ATTR -encode3B(u32 number, WordB3 *stru) -{ - stru->lsb = (u8) (number % 127); - number = (number - stru->lsb) / 127; - stru->lsb += 1; - - stru->msb = (u8) (number % 127); - number = (number - stru->msb) / 127; - stru->msb += 1; - - stru->xsb = (u8) (number + 1); -} - /** * buffer should be at least 64+5*10+6 long (title + buttons + 6), ie. 120 * @param buffer diff --git a/user/screen.h b/user/screen.h index 66b97fe..7c23c94 100644 --- a/user/screen.h +++ b/user/screen.h @@ -80,6 +80,29 @@ extern TerminalConfigBundle * const termconf; */ extern TerminalConfigBundle termconf_scratch; +enum MTM { + MTM_NONE, + MTM_X10, + MTM_NORMAL, + MTM_BUTTON_MOTION, + MTM_ANY_MOTION, +}; + +enum MTE { + MTE_SIMPLE, + MTE_UTF8, + MTE_SGR, + MTE_URXVT, +}; + +typedef struct { + enum MTM mode; + bool focus_tracking; + enum MTE encoding; +} MouseTrackingConfig; + +extern MouseTrackingConfig mouse_tracking; + /** Restore default settings to termconf. Does not apply or copy to scratch. */ void terminal_restore_defaults(void); /** Apply settings, redraw (clears the screen) */ @@ -97,17 +120,6 @@ void screen_set_button_text(int num, const char *text); // --- Encoding --- -typedef struct { - u8 lsb; - u8 msb; -} WordB2; - -typedef struct { - u8 lsb; - u8 msb; - u8 xsb; -} WordB3; - typedef enum { CS_USASCII = 'B', CS_UKASCII = 'A', @@ -115,9 +127,6 @@ typedef enum { CS_DOS_437 = '1', } CHARSET; -/** Encode number to two nice ASCII bytes */ -void encode2B(u16 number, WordB2 *stru); - httpd_cgi_state screenSerializeToBuffer(char *buffer, size_t buf_len, void **data); void screenSerializeLabelsToBuffer(char *buffer, size_t buf_len); From 7aa3cd7f7591fa1a462dcf1bd27e115d28e726bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Mon, 4 Sep 2017 22:53:29 +0200 Subject: [PATCH 4/4] working mouse --- html_orig/js/app.js | 3 +++ html_orig/jssrc/term.js | 3 +++ user/apars_csi.c | 2 ++ user/cgi_sockets.c | 10 +++++++--- user/screen.h | 18 +++++++++--------- 5 files changed, 24 insertions(+), 12 deletions(-) diff --git a/html_orig/js/app.js b/html_orig/js/app.js index 3c64eb3..33d2510 100644 --- a/html_orig/js/app.js +++ b/html_orig/js/app.js @@ -2244,11 +2244,13 @@ var Input = (function() { if (b > 3 || b < 1) return; var m = packModifiersForMouse(); Conn.send("p" + encode2B(y) + encode2B(x) + encode2B(b) + encode2B(m)); + console.log("B ",b," M ",m); }, onMouseUp: function (x, y, b) { if (b > 3 || b < 1) return; var m = packModifiersForMouse(); Conn.send("r" + encode2B(y) + encode2B(x) + encode2B(b) + encode2B(m)); + console.log("B ",b," M ",m); }, onMouseWheel: function (x, y, dir) { // -1 ... btn 4 (away from user) @@ -2256,6 +2258,7 @@ var Input = (function() { var m = packModifiersForMouse(); var b = (dir < 0 ? 4 : 5); Conn.send("p" + encode2B(y) + encode2B(x) + encode2B(b) + encode2B(m)); + console.log("B ",b," M ",m); }, }; })(); diff --git a/html_orig/jssrc/term.js b/html_orig/jssrc/term.js index 14d1bfa..4686cee 100644 --- a/html_orig/jssrc/term.js +++ b/html_orig/jssrc/term.js @@ -678,11 +678,13 @@ var Input = (function() { if (b > 3 || b < 1) return; var m = packModifiersForMouse(); Conn.send("p" + encode2B(y) + encode2B(x) + encode2B(b) + encode2B(m)); + console.log("B ",b," M ",m); }, onMouseUp: function (x, y, b) { if (b > 3 || b < 1) return; var m = packModifiersForMouse(); Conn.send("r" + encode2B(y) + encode2B(x) + encode2B(b) + encode2B(m)); + console.log("B ",b," M ",m); }, onMouseWheel: function (x, y, dir) { // -1 ... btn 4 (away from user) @@ -690,6 +692,7 @@ var Input = (function() { var m = packModifiersForMouse(); var b = (dir < 0 ? 4 : 5); Conn.send("p" + encode2B(y) + encode2B(x) + encode2B(b) + encode2B(m)); + console.log("B ",b," M ",m); }, }; })(); diff --git a/user/apars_csi.c b/user/apars_csi.c index 1ad43c1..6179996 100644 --- a/user/apars_csi.c +++ b/user/apars_csi.c @@ -469,6 +469,8 @@ static void ICACHE_FLASH_ATTR do_csi_privattr(CSI_Data *opts) else if (n == 1005) mouse_tracking.encoding = yn ? MTE_UTF8 : MTE_SIMPLE; else if (n == 1006) mouse_tracking.encoding = yn ? MTE_SGR : MTE_SIMPLE; else if (n == 1015) mouse_tracking.encoding = yn ? MTE_URXVT : MTE_SIMPLE; + + dbg("Mouse opt %d yesno %d", n, yn); } else if (n == 12) { // TODO Cursor blink on/off diff --git a/user/cgi_sockets.c b/user/cgi_sockets.c index ded74f7..f65bb6e 100644 --- a/user/cgi_sockets.c +++ b/user/cgi_sockets.c @@ -121,6 +121,10 @@ void ICACHE_FLASH_ATTR screen_notifyChange(ScreenNotifyChangeTopic topic) void ICACHE_FLASH_ATTR sendMouseAction(char evt, int y, int x, int button, u8 mods) { + // one-based + x++; + y++; + bool ctrl = (mods & 1) > 0; bool shift = (mods & 2) > 0; bool alt = (mods & 4) > 0; @@ -129,7 +133,7 @@ void ICACHE_FLASH_ATTR sendMouseAction(char evt, int y, int x, int button, u8 mo enum MTE mte = mouse_tracking.encoding; // No message on release in X10 mode - if (mtm == MTM_X10 && button == 0) { + if (mtm == MTM_X10 && (button == 0 || evt == 'r')) { return; } @@ -144,7 +148,7 @@ void ICACHE_FLASH_ATTR sendMouseAction(char evt, int y, int x, int button, u8 mo int eventcode = 0; if (mtm == MTM_X10) { - eventcode = button; + eventcode = button-1; } else { if (button == 0 || (evt == 'r' && mte != MTE_SGR)) eventcode = 3; // release @@ -174,7 +178,7 @@ void ICACHE_FLASH_ATTR sendMouseAction(char evt, int y, int x, int button, u8 mo sprintf(buf, "\x1b[M%c%c%c", (u8)(32+eventcode), (u8)(32+x), (u8)(32+y)); } else if (mte == MTE_SGR) { - sprintf(buf, "\x1b[%d;%d;%d%c", eventcode, x, y, evt == 'p' ? 'M' : 'm'); + sprintf(buf, "\x1b[<%d;%d;%d%c", eventcode, x, y, evt == 'p' ? 'M' : 'm'); } else if (mte == MTE_URXVT) { sprintf(buf, "\x1b[%d;%d;%dM", (u8)(32+eventcode), (u8)(32+x), (u8)(32+y)); diff --git a/user/screen.h b/user/screen.h index 7c23c94..3c1ea3b 100644 --- a/user/screen.h +++ b/user/screen.h @@ -81,18 +81,18 @@ extern TerminalConfigBundle * const termconf; extern TerminalConfigBundle termconf_scratch; enum MTM { - MTM_NONE, - MTM_X10, - MTM_NORMAL, - MTM_BUTTON_MOTION, - MTM_ANY_MOTION, + MTM_NONE = 0, + MTM_X10 = 1, + MTM_NORMAL = 2, + MTM_BUTTON_MOTION = 3, + MTM_ANY_MOTION = 4, }; enum MTE { - MTE_SIMPLE, - MTE_UTF8, - MTE_SGR, - MTE_URXVT, + MTE_SIMPLE = 0, + MTE_UTF8 = 1, + MTE_SGR = 2, + MTE_URXVT = 3, }; typedef struct {