diff --git a/base.php b/base.php index 717365d..9181445 100644 --- a/base.php +++ b/base.php @@ -25,7 +25,6 @@ define('JS_WEB_ROOT', $root); define('ESP_DEMO', (bool)getenv('ESP_DEMO')); if (ESP_DEMO) { - define('DEMO_SCREEN', '"S\u0019\u0001Q\u0001\u0018\u0001P\u0001\u0014\u0003\u0001\u0005\b\u0001 \u0002P\u0001\u0005\u0005\u0001\u0004\u0002\u0001~ $ \u0005\b\u0001\u0004\u0001\u0001archey3|lolcat -F .3 \u0002\t\u0002\u0005^\u0001 \u0005@\u0001 \u0005\"\u0001 \u0005(\u0001 \u0005\'\u0001 \u0005-\u0001 +\u0005,\u0001 \u00052\u0001 \u00051\u0001 \u0005T\u0001 \u0005w\u0001 \u0005\u001c\u0002 O\u0005\u0016\u0002S\u0005:\u0002: A\u00054\u0002r\u0005X\u0002ch \u0005R\u0002Lin\u0005M\u0002ux x\u0005H\u000286_\u0005I\u000264\u0005\b\u0001 \u0002\u001b\u0001\u0005@\u0001 \u0005\"\u0001 \u0005(\u0001 \u0005\'\u0001 \u0005-\u0001 \u0005,\u0001 \u00052\u0001 # \u00051\u0001 \u0005T\u0001 \u0005w\u0001 \u0005\u001c\u0002 \u0005\u0016\u0002 \u0005:\u0002 Ho\u00054\u0002s\u0005X\u0002tna\u0005R\u0002me:\u0005M\u0002 N20\u0005H\u00022\u0005\b\u0001 \u0002\"\u0001\u0005\"\u0001 \u0005(\u0001 \u0005\'\u0001 \u0005-\u0001 \u0005,\u0001 \u00052\u0001 \u00051\u0001###\u0005T\u0001 \u0005w\u0001 \u0005\u001c\u0002 \u0005\u0016\u0002 \u0005:\u0002 \u00054\u0002 \u0005X\u0002Ker\u0005R\u0002nel\u0005M\u0002 Rel\u0005H\u0002eas\u0005I\u0002e: \u0005%\u00024\u0005&\u0002.9.\u0005\u0002\u00024\u0005\u0003\u00027-1\u0005^\u0001-lt\u0005@\u0001s\u0005\b\u0001 \u0002\u0014\u0001\u0005(\u0001 \u0005\'\u0001 \u0005-\u0001 \u0005,\u0001 \u00052\u0001 \u00051\u0001 #\u0005T\u0001####\u0005w\u0001 \u0005\u001c\u0002 \u0005\u0016\u0002 \u0005:\u0002 \u00054\u0002 \u0005X\u0002 \u0005R\u0002Upt\u0005M\u0002ime:\u0005H\u0002 19\u0005I\u0002:26\u0005\b\u0001 \u0002#\u0001\u0005\'\u0001 \u0005-\u0001 \u0005,\u0001 \u00052\u0001 \u00051\u0001 \u0005T\u0001 ##\u0005w\u0001###\u0005\u001c\u0002# \u0005\u0016\u0002 \u0005:\u0002 \u00054\u0002 \u0005X\u0002 \u0005R\u0002 \u0005M\u0002WM: \u0005H\u0002KWi\u0005I\u0002n\u0005\b\u0001 \u0002(\u0001\u0005-\u0001 \u0005,\u0001 \u00052\u0001 \u00051\u0001 \u0005T\u0001 \u0005w\u0001; #\u0005\u001c\u0002###\u0005\u0016\u0002#\u0005:\u0002; \u00054\u0002 \u0005X\u0002 \u0005R\u0002 \u0005M\u0002 D\u0005H\u0002E: \u0005I\u0002KDE\u0005\b\u0001 \u0002)\u0001\u00052\u0001 \u00051\u0001 \u0005T\u0001 \u0005w\u0001 +\u0005\u001c\u0002##.\u0005\u0016\u0002#\u0005:\u0002###\u00054\u0002#\u0005X\u0002 \u0005R\u0002 \u0005M\u0002 \u0005H\u0002 P\u0005I\u0002ack\u0005%\u0002a\u0005&\u0002ges\u0005\u0002\u0002:\u0005\u0003\u0002 18\u0005^\u000121\u0005\b\u0001 \u0002\"\u0001\u00051\u0001 \u0005T\u0001 \u0005w\u0001 \u0005\u001c\u0002 +#\u0005\u0016\u0002#\u0005:\u0002###\u00054\u0002#\u0005X\u0002###\u0005R\u0002# \u0005M\u0002 \u0005H\u0002 \u0005I\u0002 R\u0005%\u0002A\u0005&\u0002M: \u0005\u0002\u00029\u0005\u0003\u0002256\u0005^\u0001 MB\u0005@\u0001 / 1\u0005\"\u0001599\u0005(\u00019 M\u0005\'\u0001B\u0005\b\u0001 \u0002\u0019\u0001\u0005T\u0001 \u0005w\u0001 \u0005\u001c\u0002 \u0005\u0016\u0002#\u0005:\u0002###\u00054\u0002#\u0005X\u0002###\u0005R\u0002###\u0005M\u0002##; \u0005H\u0002 \u0005I\u0002 \u0005%\u0002 \u0005&\u0002 Pr\u0005\u0002\u0002o\u0005\u0003\u0002ces\u0005^\u0001sor\u0005@\u0001 Typ\u0005\"\u0001e: \u0005(\u0001Int\u0005\'\u0001e\u0005-\u0001l(R\u0005,\u0001)\u00052\u0001 Co\u00051\u0001re(\u0005T\u0001TM) \u0005w\u0001i5-\u0005\u001c\u0002640\u0005\u0016\u00020\u0005:\u0002 CP\u00054\u0002U\u0005X\u0002 @ \u0005R\u00022.7\u0005M\u00020GHz\u0005R\u0002 \u0002I\u0001\u0005w\u0001 \u0005\u001c\u0002 \u0005\u0016\u0002 \u0005:\u0002 ##\u00054\u0002#\u0005X\u0002###\u0005R\u0002###\u0005M\u0002####\u0005H\u0002##+\u0005I\u0002 \u0005%\u0002 \u0005&\u0002 \u0005\u0002\u0002 \u0005\u0003\u0002$ED\u0005^\u0001ITO\u0005@\u0001R: n\u0005\"\u0001ano\u0005\b\u0001 \u0002#\u0001\u0005\u001c\u0002 \u0005\u0016\u0002 \u0005:\u0002 \u00054\u0002#\u0005X\u0002###\u0005R\u0002###\u0005M\u0002 #\u0005H\u0002###\u0005I\u0002###\u0005%\u0002 \u0005&\u0002 \u0005\u0002\u0002 \u0005\u0003\u0002 \u0005^\u0001Roo\u0005@\u0001t: 1\u0005\"\u000160G\u0005(\u0001 / \u0005\'\u00011\u0005-\u000196G\u0005,\u0001 \u00052\u0001(81\u00051\u0001%) \u0005T\u0001(ext\u0005w\u00014)\u0005\b\u0001 \u0002\u0012\u0001\u0005\u0016\u0002 \u0005:\u0002 \u00054\u0002 \u0005X\u0002.##\u0005R\u0002###\u0005M\u0002#; \u0005H\u0002 \u0005I\u0002;##\u0005%\u0002#\u0005&\u0002;`\"\u0005\u0002\u0002.\u0005\u0003\u0002 \u0005^\u0001 \u0005\b\u0001 \u00020\u0001\u0005:\u0002 \u00054\u0002 \u0005X\u0002 .\u0005R\u0002###\u0005M\u0002####\u0005H\u0002; \u0005I\u0002 \u0005%\u0002;\u0005&\u0002###\u0005\u0002\u0002#\u0005\u0003\u0002#. \u0005^\u0001 \u0005@\u0001 \u0005\b\u0001 \u00020\u0001\u0005X\u0002 \u0005R\u0002 #\u0005M\u0002####\u0005H\u0002###\u0005I\u0002#. \u0005%\u0002 \u0005&\u0002 .#\u0005\u0002\u0002#\u0005\u0003\u0002###\u0005^\u0001###\u0005@\u0001` \u0005\"\u0001 \u0005\b\u0001 \u00020\u0001\u0005R\u0002 \u0005M\u0002 ###\u0005H\u0002###\u0005I\u0002\' \u0005%\u0002 \u0005&\u0002 \u0005\u0002\u0002 \u0005\u0003\u0002 \u0005^\u0001 \'#\u0005@\u0001####\u0005\"\u0001# \u0005(\u0001 \u0005\b\u0001 \u00020\u0001\u0005M\u0002 ;\u0005H\u0002###\u0005I\u0002# \u0005%\u0002 \u0005&\u0002 \u0005\u0002\u0002 \u0005\u0003\u0002 \u0005^\u0001 \u0005@\u0001 \u0005\"\u0001###\u0005(\u0001#; \u0005\'\u0001 \u0005-\u0001 \u0005\b\u0001 \u00020\u0001\u0005H\u0002 #\u0005I\u0002#\' \u0005%\u0002 \u0005&\u0002 \u0005\u0002\u0002 \u0005\u0003\u0002 \u0005^\u0001 \u0005@\u0001 \u0005\"\u0001 \u0005(\u0001 \'\u0005\'\u0001#\u0005-\u0001# \u0005,\u0001 \u0005\b\u0001 \u00020\u0001\u0005I\u0002 #\'\u0005%\u0002 \u0005&\u0002 \u0005\u0002\u0002 \u0005\u0003\u0002 \u0005^\u0001 \u0005@\u0001 \u0005\"\u0001 \u0005(\u0001 \u0005\'\u0001 \u0005-\u0001 \u0005,\u0001`\u00052\u0001# \u0005\b\u0001 \u0002\u0001\u0002\u0005\u0005\u0001\u0004\u0002\u0001~ $ \u0005\b\u0001\u0004\u0001\u0001This is a static demo of the web interface. Have a look through the menus ↓ "'); define('DEMO_APS', <<= this.height) this.scroll() + } + writeChar (character) { + this.screen[this.cursor.y * this.width + this.cursor.x] = [character, this.style] + this.cursor.x++ + if (this.cursor.x >= this.width) { + this.cursor.x = 0 + this.newLine() + } + } + deleteChar () { + this.cursor.x-- + if (this.cursor.x < 0) { + this.cursor.x = this.width - 1 + this.cursor.y = Math.max(0, this.cursor.y - 1) + } + this.screen.splice((this.cursor.y + 1) * this.width, 0, [' ', TERM_DEFAULT_STYLE]) + this.screen.splice(this.cursor.y * this.width + this.cursor.x, 1) + } + clampCursor () { + if (this.cursor.x < 0) this.cursor.x = 0 + if (this.cursor.y < 0) this.cursor.y = 0 + if (this.cursor.x > this.width - 1) this.cursor.x = this.width - 1 + if (this.cursor.y > this.height - 1) this.cursor.y = this.height - 1 + } + applySequence (sequence) { + if (sequence[0] === '[') { + let type = sequence[sequence.length - 1] + let content = sequence.substring(1, sequence.length - 1) + + let numbers = content ? content.split(';').map(i => +i.replace(/\D/g, '')) : [] + let numOr1 = numbers.length ? numbers[0] : 1 + if (type === 'H') { + this.cursor.x = (numbers[0] | 0) - 1 + this.cursor.y = (numbers[1] | 0) - 1 + this.clampCursor() + } else if (type >= 'A' && type <= 'D') { + this.cursor[type <= 'B' ? 'y' : 'x'] += ((type === 'B' || type === 'C') ? 1 : -1) * numOr1 + this.clampCursor() + } else if (type === 'E' || type === 'F') { + this.cursor.x = 0 + this.cursor.y += (type === 'E' ? 1 : -1) * numOr1 + this.clampCursor() + } else if (type === 'G') { + this.cursor.x = numOr1 - 1 + this.clampCursor() + } else if (type === 'q') this.cursor.style = numOr1 + else if (type === 'm') { + if (!numbers.length) { + this.style = TERM_DEFAULT_STYLE + return + } + let type = numbers[0] + if (type === 1) this.style |= 1 << 16 // bold + else if (type === 2) this.style |= 1 << 1 << 16 // faint + else if (type === 3) this.style |= 1 << 2 << 16 // italic + else if (type === 4) this.style |= 1 << 3 << 16 // underline + else if (type === 5 || type === 6) this.style |= 1 << 4 << 16 // blink + else if (type === 7) { + // invert + this.style = (this.style & 0xFF0000) | ((this.style >> 8) & 0xFF) | ((this.style & 0xFF) << 8) + } else if (type === 9) this.style |= 1 << 6 << 16 // strike + else if (type === 20) this.style |= 1 << 5 << 16 // fraktur + else if (type >= 30 && type <= 38) this.style = (this.style & 0xFFFF00) | (type % 10) + else if (type >= 40 && type <= 48) this.style = (this.style & 0xFF00FF) | ((type % 10) << 8) + else if (type === 39) this.style = (this.style & 0xFFFF00) | 7 + else if (type === 49) this.style = (this.style & 0xFF00FF) | (7 << 8) + else if (type >= 90 && type <= 98) this.style = (this.style & 0xFFFF00) | ((type % 10) + 8) + else if (type >= 100 && type <= 108) this.style = (this.style & 0xFF00FF) | (((type % 10) + 8) << 8) + else if (type === 38 || type === 48) { + if (numbers[1] === 5) { + let color = (numbers[2] | 0) & 0xFF + if (type === 38) this.style = (this.style & 0xFFFF00) | color + if (type === 48) this.style = (this.style & 0xFF00FF) | (color << 8) + } + } + } + } + } + write (text) { + for (let character of text) { + let code = character.codePointAt(0) + if (code === 0x1b) this.currentSequence = 1 + else if (this.currentSequence === 1 && character === '[') { + this.currentSequence = 2 + this.sequence += '[' + } else if (this.currentSequence && character.match(/[\x40-\x7e]/)) { + this.applySequence(this.sequence + character) + this.currentSequence = 0 + this.sequence = '' + } else if (this.currentSequence > 1) this.sequence += character + else if (this.currentSequence === 1) { + // something something nothing + this.currentSequence = 0 + this.writeChar(character) + } else if (code === 0x07) this.termScreen.load('B') + else if (code === 0x08) this.deleteChar() + else if (code === 0x0a) this.newLine() + else if (code === 0x0d) this.cursor.x = 0 + else this.writeChar(character) + } + this.scheduleLoad() + } + serialize () { + let serialized = 'S' + serialized += encode2B(this.height) + encode2B(this.width) + serialized += encode2B(this.cursor.y) + encode2B(this.cursor.x) + + let attributes = 1 // cursor always visible + attributes |= (3 << 5) * +this.trackMouse // track mouse controls both + attributes |= 3 << 7 // buttons/links always visible + attributes |= (this.cursor.shape << 9) + serialized += encode3B(attributes) + + let lastStyle = null + for (let cell of this.screen) { + if (cell[1] !== lastStyle) { + let foreground = cell[1] & 0xFF + let background = (cell[1] >> 8) & 0xFF + let attributes = (cell[1] >> 16) & 0xFF + let setForeground = foreground !== (lastStyle & 0xFF) + let setBackground = background !== ((lastStyle >> 8) & 0xFF) + let setAttributes = attributes !== ((lastStyle >> 16) & 0xFF) + + if (setForeground && setBackground) serialized += '\x03' + encode3B(cell[1] & 0xFFFF) + else if (setForeground) serialized += '\x05' + encode2B(foreground) + else if (setBackground) serialized += '\x06' + encode2B(background) + if (setAttributes) serialized += '\x04' + encode2B(attributes) + + lastStyle = cell[1] + } + serialized += cell[0] + } + return serialized + } + scheduleLoad () { + clearInterval(this._scheduledLoad) + if (this._lastLoad < Date.now() - TERM_MIN_DRAW_DELAY) { + this.termScreen.load(this.serialize()) + } else { + this._scheduledLoad = setTimeout(() => { + this.termScreen.load(this.serialize()) + }, TERM_MIN_DRAW_DELAY - this._lastLoad) + } + } +} + +window.demoInterface = { + input (data) { + let type = data[0] + let content = data.substr(1) + + if (type === 's') { + this.terminal.write(content) + } else if (type === 'b') { + let button = content.charCodeAt(0) + console.log(`button ${button} pressed`) + } else if (type === 'm' || type === 'p' || type === 'r') { + console.log(JSON.stringify(data)) + } + }, + init (screen) { + this.terminal = new ScrollingTerminal(screen) + } +} diff --git a/js/term_conn.js b/js/term_conn.js index 4521e3d..65506d7 100644 --- a/js/term_conn.js +++ b/js/term_conn.js @@ -67,7 +67,7 @@ window.Conn = function (screen) { function doSend (message) { if (_demo) { - console.log('TX: ', message) + window.demoInterface.input(message) return true // Simulate success } if (xoff) { @@ -91,7 +91,7 @@ window.Conn = function (screen) { function init () { if (window._demo) { console.log('Demo mode!') - screen.load(_demo_screen) + demoInterface.init(screen) showPage() return } diff --git a/pages/_head.php b/pages/_head.php index 3a39b02..cd47c53 100644 --- a/pages/_head.php +++ b/pages/_head.php @@ -10,7 +10,6 @@