Added messaging from browser to server, tap handling, improved resize on clientside

pull/30/head
Ondřej Hruška 8 years ago
parent 5c948b079b
commit ca93a3b90d
  1. 2
      html/script.js
  2. 2
      html/style.css
  3. 2
      html/term.tpl
  4. 168
      html_orig/script.js
  5. 14
      html_orig/style.css
  6. 9
      html_orig/term.html
  7. 21
      html_orig/termtest.html
  8. 2
      user/screen.c
  9. 14
      user/user_main.c

@ -1 +1 @@
var $=function(d,c){d=d.match(/^(\W)?(.*)/);return(c||document)["getElement"+(d[1]?d[1]=="#"?"ById":"sByClassName":"sByTagName")](d[2])};var m=function(e,d,f){d=document;f=d.createElement("p");f.innerHTML=e;e=d.createDocumentFragment();while(d=f.firstChild){e.appendChild(d)}return e};(function(){function a(r){return document.createElement(r)}var b=26,k=10;var l={a:false,x:0,y:0,suppress:false,hidden:false};var o=[];var g=["#111213","#CC0000","#4E9A06","#C4A000","#3465A4","#75507B","#06989A","#D3D7CF","#555753","#EF2929","#8AE234","#FCE94F","#729FCF","#AD7FA8","#34E2E2","#EEEEEC"];function p(){o.forEach(function(r,s){r.t=" ";r.fg=7;r.bg=0;d(r)})}function c(s,r){return o[s*b+r]}function i(){return c(l.y,l.x)}function q(r){l.hidden=!r;l.a&=r;d(i(),l.a)}function f(s,r){l.suppress=true;d(i(),false);l.x=r;l.y=s;l.suppress=false;d(i(),l.a)}function d(s,r){var v=s.e,t,u;t=r?s.bg:s.fg;u=r?s.fg:s.bg;v.innerText=(s.t+" ")[0];v.style.color=e(t);v.style.backgroundColor=e(u);v.style.fontWeight=t>7?"bold":"normal"}function h(){o.forEach(function(s,t){var r=l.a&&(t==l.y*b+l.x);d(s,r)})}function j(v){l.x=v.x;l.y=v.y;var w=0,C=0,y=v.screen;var r=7,x=0;while(w<y.length&&C<b*k){var A=o[C++];var u=y[w];if(u!=","){r=A.fg=parseInt(y[w++],16);x=A.bg=parseInt(y[w++],16)}else{w++;A.fg=r;A.bg=x}var B=A.t=y[w++];var s=0;switch(y[w]){case"r":s=1;break;case"s":s=2;break;case"t":s=3;break;case"u":s=4;break;default:s=0}if(s>0){var z=parseInt(y.substr(w+1,s));w=w+s+1;for(;z>0&&C<b*k;z--){A=o[C++];A.fg=r;A.bg=x;A.t=B}}}h()}function e(r){r=parseInt(r);if(r<0||r>15){r=0}return g[r]}function n(v){var u,r,t=$("#screen");for(var s=0;s<b*k;s++){u=a("span");if((s>0)&&(s%b==0)){t.appendChild(a("br"))}t.appendChild(u);r={t:" ",fg:7,bg:0,e:u};o.push(r);d(r)}setInterval(function(){l.a=!l.a;if(l.hidden){l.a=false}if(!l.suppress){d(i(),l.a)}},500);j(v)}window.Term={init:n,load:j,setCursor:f,enableCursor:q,clear:p}})();(function(){var g="ws://"+window.location.host+"/ws/update.cgi";var d;function c(i){console.log("CONNECTED")}function b(i){console.error("SOCKET CLOSED")}function f(i){try{console.log("RX: ",i.data);Term.load(JSON.parse(i.data))}catch(j){console.error(j)}}function e(i){console.error(i.data)}function a(i){if(typeof i!="string"){i=JSON.stringify(i)}d.send(i)}function h(){d=new WebSocket(g);d.onopen=c;d.onclose=b;d.onmessage=f;d.onerror=e;console.log("Opening socket.")}window.Conn={ws:null,init:h,send:a}})();function init(a){Term.init(a);Conn.init()}; function mk(a){return document.createElement(a)}function q1(a){return document.querySelector(a)}function qa(a){return document.querySelectorAll(a)}(function(){var a,j;var k={a:false,x:0,y:0,suppress:false,hidden:false};var m=[];var f=["#111213","#CC0000","#4E9A06","#C4A000","#3465A4","#75507B","#06989A","#D3D7CF","#555753","#EF2929","#8AE234","#FCE94F","#729FCF","#AD7FA8","#34E2E2","#EEEEEC"];function n(){m.forEach(function(p,q){p.t=" ";p.fg=7;p.bg=0;c(p)})}function b(q,p){return m[q*a+p]}function h(){return b(k.y,k.x)}function o(p){k.hidden=!p;k.a&=p;c(h(),k.a)}function e(q,p){k.suppress=true;c(h(),false);k.x=p;k.y=q;k.suppress=false;c(h(),k.a)}function c(q,p){var t=q.e,r,s;r=p?q.bg:q.fg;s=p?q.fg:q.bg;t.innerText=(q.t+" ")[0];t.style.color=d(r);t.style.backgroundColor=d(s);t.style.fontWeight=r>7?"bold":"normal"}function g(){m.forEach(function(q,r){var p=k.a&&(r==k.y*a+k.x);c(q,p)})}function i(s){k.x=s.x;k.y=s.y;if(s.w!=a||s.h!=j){Term.init(s);return}var u=0,A=0,w=s.screen;var p=7,v=0;while(u<w.length&&A<a*j){var y=m[A++];var r=w[u];if(r!=","){p=y.fg=parseInt(w[u++],16);v=y.bg=parseInt(w[u++],16)}else{u++;y.fg=p;y.bg=v}var z=y.t=w[u++];var q=0;switch(w[u]){case"r":q=1;break;case"s":q=2;break;case"t":q=3;break;case"u":q=4;break;default:q=0}if(q>0){var x=parseInt(w.substr(u+1,q));u=u+q+1;for(;x>0&&A<a*j;x--){y=m[A++];y.fg=p;y.bg=v;y.t=z}}}g()}function d(p){p=parseInt(p);if(p<0||p>15){p=0}return f[p]}function l(t){a=t.w;j=t.h;var s,p,r=q1("#screen");while(r.firstChild){r.removeChild(r.firstChild)}m=[];for(var q=0;q<a*j;q++){s=mk("span");(function(){var u=q%a;var v=Math.floor(q/a);s.addEventListener("click",function(){Kinp.onTap(v,u)})})();if((q>0)&&(q%a==0)){r.appendChild(mk("br"))}r.appendChild(s);p={t:" ",fg:7,bg:0,e:s};m.push(p);c(p)}setInterval(function(){k.a=!k.a;if(k.hidden){k.a=false}if(!k.suppress){c(h(),k.a)}},500);i(t)}window.Term={init:l,load:i,setCursor:e,enableCursor:o,clear:n}})();(function(){var g="ws://"+window.location.host+"/ws/update.cgi";var d;function c(i){console.log("CONNECTED")}function b(i){console.error("SOCKET CLOSED")}function f(i){try{console.log("RX: ",i.data);Term.load(JSON.parse(i.data))}catch(j){console.error(j)}}function e(i){console.error(i.data)}function a(i){console.log("TX: ",i);if(d.readyState!=1){console.error("Socket not ready");return}if(typeof i!="string"){i=JSON.stringify(i)}d.send(i)}function h(){d=new WebSocket(g);d.onopen=c;d.onclose=b;d.onmessage=f;d.onerror=e;console.log("Opening socket.")}window.Conn={ws:null,init:h,send:a}})();(function(){function a(e){Conn.send("STR:"+e)}function b(f,e){Conn.send("TAP:"+f+","+e)}function d(e){Conn.send("BTN:"+e)}function c(){window.addEventListener("keypress",function(h){var g=+h.which;if(g>=32&&g<127){var f=String.fromCharCode(g);a(f)}});window.addEventListener("keydown",function(g){var f=g.keyCode;switch(f){case 8:a("\x08");break;case 13:a("\x0d\x0a");break;case 27:a("\x1b");break;case 37:a("\x1b[D");break;case 38:a("\x1b[A");break;case 39:a("\x1b[C");break;case 40:a("\x1b[B");break}});qa("#buttons button").forEach(function(e){e.addEventListener("click",function(){d(+this.dataset.n)})})}window.Kinp={init:c,onTap:b}})();function init(a){Term.init(a);Conn.init();Kinp.init()};

@ -1 +1 @@
html,body{background:#48505f;color:#eee;font-family:monospace;font-size:16pt;text-align:center}header{font-weight:bold;font-size:14pt;padding:6px;display:block}#screen{display:block;white-space:nowrap;background:#111213;border-radius:3px;padding:6px;box-shadow:inset 0 0 5px black;display:inline-block}#screen span{white-space:pre}#buttons{margin-top:10px}button{margin:0 2px;padding:10px 0;width:22%;max-width:80px;cursor:pointer} html,body{background:#48505f;color:#eee;font-family:monospace;font-size:16pt;text-align:center}header{font-weight:bold;font-size:14pt;padding:6px;display:block}#screen{white-space:nowrap;background:#111213;border-radius:3px;padding:6px;box-shadow:inset 0 0 5px black;display:inline-block}#screen span{white-space:pre;cursor:pointer}#screen span:hover{outline:1px solid rgba(255,255,255,0.5)}#buttons{margin-top:10px;white-space:nowrap}button{margin:0 3px;padding:10px 0;width:18%;max-width:65px;cursor:pointer;font-weight:bold}

@ -1 +1 @@
<!doctype html><meta charset=utf-8><title>ESP8266 Remote Terminal</title><meta name=viewport content="width=device-width,shrink-to-fit=no,user-scalable=no,initial-scale=1,maximum-scale=1,minimum-scale=1"><link rel=stylesheet href=style.css><script src=script.js></script><header>ESP8266 Remote Terminal</header><div id=screen></div><div id=buttons><button>1</button><button>2</button><button>3</button><button>4</button></div><script>init(%screenData%)</script> <!doctype html><meta charset=utf-8><title>ESP8266 Remote Terminal</title><meta name=viewport content="width=device-width,shrink-to-fit=no,user-scalable=no,initial-scale=1,maximum-scale=1,minimum-scale=1"><link rel=stylesheet href=style.css><script src=script.js></script><header>ESP8266 Remote Terminal</header><div id=screen></div><div id=buttons><button data-n=1>1</button><button data-n=2>2</button><button data-n=3>3</button><button data-n=4>4</button><button data-n=5>5</button></div><script>init(%screenData%)</script>

@ -1,83 +1,12 @@
//region Libs / utils function mk(e) {return document.createElement(e)}
function q1(s) {return document.querySelector(s)}
/* function qa(s) {return document.querySelectorAll(s)}
* DOM selector
*
* Usage:
* $('div');
* $('#name');
* $('.name');
*
*
* Copyright (C) 2011 Jed Schmidt <http://jed.is> - WTFPL
* More: https://gist.github.com/991057
*
*/
var $ = function(
a, // take a simple selector like "name", "#name", or ".name", and
b // an optional context, and
){
a = a.match(/^(\W)?(.*)/); // split the selector into name and symbol.
return( // return an element or list, from within the scope of
b // the passed context
|| document // or document,
)[
"getElement" + ( // obtained by the appropriate method calculated by
a[1]
? a[1] == "#"
? "ById" // the node by ID,
: "sByClassName" // the nodes by class name, or
: "sByTagName" // the nodes by tag name,
)
](
a[2] // called with the name.
)
};
/*
* Create DOM element
*
* Usage:
* var el = m('<h1>Hello</h1>');
* document.body.appendChild(el);
*
*
* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
* Version 2, December 2004
*
* Copyright (C) 2011 Jed Schmidt <http://jed.is> - WTFPL
* More: https://gist.github.com/966233
*
*/
var m = function(
a, // an HTML string
b, // placeholder
c // placeholder
){
b = document; // get the document,
c = b.createElement("p"); // create a container element,
c.innerHTML = a; // write the HTML to it, and
a = b.createDocumentFragment(); // create a fragment.
while ( // while
b = c.firstChild // the container element has a first child
) a.appendChild(b); // append the child to the fragment,
return a // and then return the fragment.
};
//endregion
// //
// Terminal class // Terminal class
// //
(function () { (function () {
function make(e) {return document.createElement(e)} var W, H;
var W = 26, H = 10; //26, 10
var cursor = {a: false, x: 0, y: 0, suppress: false, hidden: false}; var cursor = {a: false, x: 0, y: 0, suppress: false, hidden: false};
var screen = []; var screen = [];
@ -162,6 +91,12 @@ var m = function(
cursor.x = obj.x; cursor.x = obj.x;
cursor.y = obj.y; cursor.y = obj.y;
// full re-init if size changed
if (obj.w != W || obj.h != H) {
Term.init(obj);
return;
}
// Simple compression - hexFG hexBG 'ASCII' (r/s/t/u NUM{1,2,3,4})? // Simple compression - hexFG hexBG 'ASCII' (r/s/t/u NUM{1,2,3,4})?
// comma instead of both colors = same as before // comma instead of both colors = same as before
@ -215,14 +150,31 @@ var m = function(
/** Init the terminal */ /** Init the terminal */
function init(obj) { function init(obj) {
W = obj.w;
H = obj.h;
/* Build screen & show */ /* Build screen & show */
var e, cell, scr = $('#screen'); var e, cell, scr = q1('#screen');
// Empty the screen node
while (scr.firstChild) scr.removeChild(scr.firstChild);
screen = [];
for(var i = 0; i < W*H; i++) { for(var i = 0; i < W*H; i++) {
e = make('span'); e = mk('span');
(function() {
var x = i % W;
var y = Math.floor(i / W);
e.addEventListener('click', function () {
Kinp.onTap(y, x);
});
})();
/* End of line */ /* End of line */
if ((i > 0) && (i % W == 0)) { if ((i > 0) && (i % W == 0)) {
scr.appendChild(make('br')); scr.appendChild(mk('br'));
} }
/* The cell */ /* The cell */
scr.appendChild(e); scr.appendChild(e);
@ -287,6 +239,11 @@ var m = function(
} }
function doSend(message) { function doSend(message) {
console.log("TX: ", message);
if (ws.readyState != 1) {
console.error("Socket not ready");
return;
}
if (typeof message != "string") { if (typeof message != "string") {
message = JSON.stringify(message); message = JSON.stringify(message);
} }
@ -310,7 +267,62 @@ var m = function(
}; };
})(); })();
//
// Keyboard (& mouse) input
//
(function() {
function sendStrMsg(str) {
Conn.send("STR:"+str);
}
function sendPosMsg(y, x) {
Conn.send("TAP:"+y+','+x);
}
function sendBtnMsg(n) {
Conn.send("BTN:"+n);
}
function init() {
window.addEventListener('keypress', function(e) {
var code = +e.which;
if (code >= 32 && code < 127) {
var ch = String.fromCharCode(code);
//console.log("Typed ", ch, "code", code, e);
sendStrMsg(ch);
}
});
window.addEventListener('keydown', function(e) {
var code = e.keyCode;
//console.log("Down ", code, e);
switch(code) {
case 8: sendStrMsg('\x08'); break;
case 13: sendStrMsg('\x0d\x0a'); break;
case 27: sendStrMsg('\x1b'); break; // this allows to directly enter control sequences
case 37: sendStrMsg('\x1b[D'); break;
case 38: sendStrMsg('\x1b[A'); break;
case 39: sendStrMsg('\x1b[C'); break;
case 40: sendStrMsg('\x1b[B'); break;
}
});
qa('#buttons button').forEach(function(s) {
s.addEventListener('click', function() {
sendBtnMsg(+this.dataset['n']);
});
});
}
window.Kinp = {
init: init,
onTap: sendPosMsg
};
})();
function init(obj) { function init(obj) {
Term.init(obj); Term.init(obj);
Conn.init(); Conn.init();
Kinp.init();
} }

@ -14,7 +14,6 @@ header {
} }
#screen { #screen {
display: block;
white-space: nowrap; white-space: nowrap;
background: #111213; background: #111213;
border-radius: 3px; border-radius: 3px;
@ -25,16 +24,23 @@ header {
#screen span { #screen span {
white-space: pre; white-space: pre;
cursor: pointer;
}
#screen span:hover {
outline: 1px solid rgba(255,255,255,0.5);
} }
#buttons { #buttons {
margin-top: 10px; margin-top: 10px;
white-space: nowrap;
} }
button { button {
margin: 0 2px; margin: 0 3px;
padding: 10px 0; padding: 10px 0;
width: 22%; width: 18%;
max-width: 80px; max-width: 65px;
cursor: pointer; cursor: pointer;
font-weight: bold;
} }

@ -6,15 +6,16 @@
<link rel="stylesheet" href="style.css"> <link rel="stylesheet" href="style.css">
<script src="script.js"></script> <script src="script.js"></script>
<!-- Katedra mereni FEL, 2017 -->
<!-- TODO GitHub link -->
<header>ESP8266 Remote Terminal</header> <header>ESP8266 Remote Terminal</header>
<div id="screen"></div> <div id="screen"></div>
<div id="buttons"> <div id="buttons">
<button>1</button><button>2</button><button>3</button><button>4</button> <button data-n="1">1</button><!--
--><button data-n="2">2</button><!--
--><button data-n="3">3</button><!--
--><button data-n="4">4</button><!--
--><button data-n="5">5</button>
</div> </div>
<script>init(%screenData%)</script> <script>init(%screenData%)</script>

@ -0,0 +1,21 @@
<!DOCTYPE html>
<meta charset="UTF-8">
<title>ESP8266 Remote Terminal</title>
<meta name="viewport" content="width=device-width,shrink-to-fit=no,user-scalable=no,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0">
<link rel="stylesheet" href="style.css">
<script src="script.js"></script>
<header>ESP8266 Remote Terminal</header>
<div id="screen"></div>
<div id="buttons">
<button data-n="1">1</button><!--
--><button data-n="2">2</button><!--
--><button data-n="3">3</button><!--
--><button data-n="4">4</button><!--
--><button data-n="5">5</button>
</div>
<script>init({w: 26, h:10, x:0,y:0,screen:"70 t259"})</script>

@ -579,7 +579,7 @@ screenSerializeToBuffer(char *buffer, size_t buf_len, void **data)
ss->lastFg = 0; ss->lastFg = 0;
ss->lastChar = '\0'; ss->lastChar = '\0';
bufprint("{\"x\":%d,\"y\":%d,\"screen\":\"", cursor.x, cursor.y); bufprint("{\"w\":%d,\"h\":%d,\"x\":%d,\"y\":%d,\"screen\":\"", W, H, cursor.x, cursor.y);
} }
int i = ss->index; int i = ss->index;

@ -32,7 +32,7 @@ void screen_notifyChange() {
void *data = NULL; void *data = NULL;
const int bufsiz = 1024; const int bufsiz = 256;
char buff[bufsiz]; char buff[bufsiz];
for (int i = 0; i < 20; i++) { for (int i = 0; i < 20; i++) {
httpd_cgi_state cont = screenSerializeToBuffer(buff, bufsiz, &data); httpd_cgi_state cont = screenSerializeToBuffer(buff, bufsiz, &data);
@ -41,9 +41,14 @@ void screen_notifyChange() {
} }
} }
void myWebsocketRecv(Websock *ws, char *data, int len, int flags) {
dbg("Sock RX str: %s, len %d", data, len);
}
/** Socket connected for updates */ /** Socket connected for updates */
void ICACHE_FLASH_ATTR myWebsocketConnect(Websock *ws) { void ICACHE_FLASH_ATTR myWebsocketConnect(Websock *ws) {
dbg("Socket connected."); dbg("Socket connected.");
ws->recvCb=myWebsocketRecv;
} }
/** /**
@ -61,7 +66,7 @@ httpd_cgi_state ICACHE_FLASH_ATTR tplScreen(HttpdConnData *connData, char *token
return HTTPD_CGI_DONE; return HTTPD_CGI_DONE;
} }
const int bufsiz = 1024; const int bufsiz = 256;
char buff[bufsiz]; char buff[bufsiz];
if (streq(token, "screenData")) { if (streq(token, "screenData")) {
@ -118,8 +123,9 @@ static void ICACHE_FLASH_ATTR prHeapTimerCb(void *arg) {
static int led = 0; static int led = 0;
static unsigned int cnt = 0; static unsigned int cnt = 0;
if (cnt%3==0) { if (cnt==5) {
os_printf("Free heap: %ld bytes\n", (unsigned long) system_get_free_heap_size()); dbg("HEAP: %ld bytes free", (unsigned long) system_get_free_heap_size());
cnt = 0;
} }
//cgiWebsockBroadcast("/ws/update.cgi", "HELLO", 5, WEBSOCK_FLAG_NONE); //cgiWebsockBroadcast("/ws/update.cgi", "HELLO", 5, WEBSOCK_FLAG_NONE);

Loading…
Cancel
Save