Update demo for new protocol, add `themes` command

box-drawing
cpsdqs 7 years ago
parent 61953f608e
commit 5245917c88
Signed by untrusted user: cpsdqs
GPG Key ID: 3F59586BB7448DD1
  1. 272
      js/term/demo.js

@ -36,30 +36,41 @@ class ANSIParser {
this.handler('insert-blanks', numOr1) this.handler('insert-blanks', numOr1)
} else if (type === 'q') this.handler('set-cursor-style', numOr1) } else if (type === 'q') this.handler('set-cursor-style', numOr1)
else if (type === 'm') { else if (type === 'm') {
if (!numbers.length || numbers[0] === 0) { if (!numbers.length) {
this.handler('reset-style') this.handler('reset-style')
return return
} }
let type = numbers[0] let type
if (type === 1) this.handler('add-attrs', 1) // bold while ((type = numbers.shift())) {
else if (type === 2) this.handler('add-attrs', 1 << 1) // faint if (type === 0) this.handler('reset-style')
else if (type === 3) this.handler('add-attrs', 1 << 2) // italic else if (type === 1) this.handler('add-attrs', 1 << 2) // bold
else if (type === 4) this.handler('add-attrs', 1 << 3) // underline else if (type === 2) this.handler('add-attrs', 1 << 9) // faint
else if (type === 5 || type === 6) this.handler('add-attrs', 1 << 4) // blink else if (type === 3) this.handler('add-attrs', 1 << 6) // italic
else if (type === 7) this.handler('add-attrs', -1) // invert else if (type === 4) this.handler('add-attrs', 1 << 3) // underline
else if (type === 9) this.handler('add-attrs', 1 << 6) // strike else if (type === 5 || type === 6) this.handler('add-attrs', 1 << 5) // blink
else if (type === 20) this.handler('add-attrs', 1 << 5) // fraktur else if (type === 7) this.handler('add-attrs', 1 << 4) // invert
else if (type >= 30 && type <= 37) this.handler('set-color-fg', type % 10) else if (type === 9) this.handler('add-attrs', 1 << 7) // strike
else if (type >= 40 && type <= 47) this.handler('set-color-bg', type % 10) else if (type === 20) this.handler('add-attrs', 1 << 10) // fraktur
else if (type === 39) this.handler('reset-color-fg') else if (type >= 30 && type <= 37) this.handler('set-color-fg', type % 10)
else if (type === 49) this.handler('reset-color-bg') else if (type >= 40 && type <= 47) this.handler('set-color-bg', type % 10)
else if (type >= 90 && type <= 98) this.handler('set-color-fg', (type % 10) + 8) else if (type === 39) this.handler('reset-color-fg')
else if (type >= 100 && type <= 108) this.handler('set-color-bg', (type % 10) + 8) else if (type === 49) this.handler('reset-color-bg')
else if (type === 38 || type === 48) { else if (type >= 90 && type <= 98) this.handler('set-color-fg', (type % 10) + 8)
if (numbers[1] === 5) { else if (type >= 100 && type <= 108) this.handler('set-color-bg', (type % 10) + 8)
let color = (numbers[2] | 0) & 0xFF else if (type === 38 || type === 48) {
if (type === 38) this.handler('set-color-fg', color) let mode = numbers.shift()
if (type === 48) this.handler('set-color-bg', color) if (mode === 2) {
let r = numbers.shift()
let g = numbers.shift()
let b = numbers.shift()
let color = (r << 16 | g << 8 | b) + 256
if (type === 38) this.handler('set-color-fg', color)
if (type === 48) this.handler('set-color-bg', color)
} else if (mode === 5) {
let color = (numbers.shift() | 0) & 0xFF
if (type === 38) this.handler('set-color-fg', color)
if (type === 48) this.handler('set-color-bg', color)
}
} }
} }
} else if (type === 'h' || type === 'l') { } else if (type === 'h' || type === 'l') {
@ -101,7 +112,7 @@ class ANSIParser {
if (!this.joinChunks) this.reset() if (!this.joinChunks) this.reset()
} }
} }
const TERM_DEFAULT_STYLE = 0 const TERM_DEFAULT_STYLE = [0, 0, 0]
const TERM_MIN_DRAW_DELAY = 10 const TERM_MIN_DRAW_DELAY = 10
let getRainbowColor = t => { let getRainbowColor = t => {
@ -117,19 +128,20 @@ class ScrollingTerminal {
this.height = 25 this.height = 25
this.termScreen = screen this.termScreen = screen
this.parser = new ANSIParser((...args) => this.handleParsed(...args)) this.parser = new ANSIParser((...args) => this.handleParsed(...args))
this.buttonLabels = ['', '', '^C', '', 'Help']
this.reset() this.reset()
this._lastLoad = Date.now() this._lastLoad = Date.now()
this.termScreen.load(this.serialize()) this.loadTimer()
window.showPage() window.showPage()
} }
reset () { reset () {
this.style = TERM_DEFAULT_STYLE this.style = TERM_DEFAULT_STYLE.slice()
this.cursor = { x: 0, y: 0, style: 1, visible: true } this.cursor = { x: 0, y: 0, style: 1, visible: true }
this.trackMouse = false this.trackMouse = false
this.theme = -1 this.theme = 0
this.rainbow = false this.rainbow = false
this.parser.reset() this.parser.reset()
this.clear() this.clear()
@ -137,13 +149,13 @@ class ScrollingTerminal {
clear () { clear () {
this.screen = [] this.screen = []
for (let i = 0; i < this.width * this.height; i++) { for (let i = 0; i < this.width * this.height; i++) {
this.screen.push([' ', this.style]) this.screen.push([' ', this.style.slice()])
} }
} }
scroll () { scroll () {
this.screen.splice(0, this.width) this.screen.splice(0, this.width)
for (let i = 0; i < this.width; i++) { for (let i = 0; i < this.width; i++) {
this.screen.push([' ', TERM_DEFAULT_STYLE]) this.screen.push([' ', TERM_DEFAULT_STYLE.slice()])
} }
this.cursor.y-- this.cursor.y--
} }
@ -152,7 +164,7 @@ class ScrollingTerminal {
if (this.cursor.y >= this.height) this.scroll() if (this.cursor.y >= this.height) this.scroll()
} }
writeChar (character) { writeChar (character) {
this.screen[this.cursor.y * this.width + this.cursor.x] = [character, this.style] this.screen[this.cursor.y * this.width + this.cursor.x] = [character, this.style.slice()]
this.cursor.x++ this.cursor.x++
if (this.cursor.x >= this.width) { if (this.cursor.x >= this.width) {
this.cursor.x = 0 this.cursor.x = 0
@ -181,12 +193,12 @@ class ScrollingTerminal {
} }
deleteChar () { // FIXME unused? deleteChar () { // FIXME unused?
this.moveBack() this.moveBack()
this.screen.splice((this.cursor.y + 1) * this.width, 0, [' ', TERM_DEFAULT_STYLE]) this.screen.splice((this.cursor.y + 1) * this.width, 0, [' ', TERM_DEFAULT_STYLE.slice()])
this.screen.splice(this.cursor.y * this.width + this.cursor.x, 1) this.screen.splice(this.cursor.y * this.width + this.cursor.x, 1)
} }
deleteForward (n) { deleteForward (n) {
n = Math.min(this.width, n) n = Math.min(this.width, n)
for (let i = 0; i < n; i++) this.screen.splice((this.cursor.y + 1) * this.width, 0, [' ', TERM_DEFAULT_STYLE]) for (let i = 0; i < n; i++) this.screen.splice((this.cursor.y + 1) * this.width, 0, [' ', TERM_DEFAULT_STYLE.slice()])
this.screen.splice(this.cursor.y * this.width + this.cursor.x, n) this.screen.splice(this.cursor.y * this.width + this.cursor.x, n)
} }
clampCursor () { clampCursor () {
@ -205,11 +217,12 @@ class ScrollingTerminal {
} else if (action === 'clear') { } else if (action === 'clear') {
this.clear() this.clear()
} else if (action === 'bell') { } else if (action === 'bell') {
this.termScreen.load('B') this.termScreen.load('U\x01B')
} else if (action === 'back') { } else if (action === 'back') {
this.moveBack() this.moveBack()
} else if (action === 'new-line') { } else if (action === 'new-line') {
this.newLine() this.newLine()
this.cursor.x = 0
} else if (action === 'return') { } else if (action === 'return') {
this.cursor.x = 0 this.cursor.x = 0
} else if (action === 'set-cursor') { } else if (action === 'set-cursor') {
@ -231,17 +244,21 @@ class ScrollingTerminal {
} else if (action === 'set-cursor-style') { } else if (action === 'set-cursor-style') {
this.cursor.style = Math.max(0, Math.min(6, args[0])) this.cursor.style = Math.max(0, Math.min(6, args[0]))
} else if (action === 'reset-style') { } else if (action === 'reset-style') {
this.style = TERM_DEFAULT_STYLE this.style = TERM_DEFAULT_STYLE.slice()
} else if (action === 'add-attrs') { } else if (action === 'add-attrs') {
this.style |= (args[0] << 16) this.style[2] |= args[0]
} else if (action === 'set-color-fg') { } else if (action === 'set-color-fg') {
this.style = (this.style & 0xFFFFFF00) | (1 << 8 << 16) | args[0] this.style[0] = args[0]
this.style[2] |= 1
} else if (action === 'set-color-bg') { } else if (action === 'set-color-bg') {
this.style = (this.style & 0xFFFF00FF) | (1 << 9 << 16) | (args[0] << 8) this.style[1] = args[0]
this.style[2] |= 1 << 1
} else if (action === 'reset-color-fg') { } else if (action === 'reset-color-fg') {
this.style = this.style & 0xFFFEFF00 this.style[0] = 0
if (this.style[2] & 1) this.style[2] ^= 1
} else if (action === 'reset-color-bg') { } else if (action === 'reset-color-bg') {
this.style = this.style & 0xFFFD00FF this.style[1] = 0
if (this.style[2] & (1 << 1)) this.style[2] ^= (1 << 1)
} else if (action === 'hide-cursor') { } else if (action === 'hide-cursor') {
this.cursor.visible = false this.cursor.visible = false
} else if (action === 'show-cursor') { } else if (action === 'show-cursor') {
@ -250,65 +267,119 @@ class ScrollingTerminal {
} }
write (text) { write (text) {
this.parser.write(text) this.parser.write(text)
this.scheduleLoad()
} }
serialize () { getScreenOpts () {
let serialized = 'S' let data = 'O'
serialized += String.fromCodePoint(this.height + 1) + String.fromCodePoint(this.width + 1) data += String.fromCodePoint(26)
serialized += String.fromCodePoint(this.cursor.y + 1) + String.fromCodePoint(this.cursor.x + 1) data += String.fromCodePoint(81)
data += String.fromCodePoint(this.theme + 1)
data += String.fromCodePoint(8)
data += String.fromCodePoint(1)
data += String.fromCodePoint(1)
data += String.fromCodePoint(1)
let attributes = +this.cursor.visible let attributes = +this.cursor.visible
attributes |= (3 << 5) * +this.trackMouse // track mouse controls both attributes |= (3 << 5) * +this.trackMouse // track mouse controls both
attributes |= 3 << 7 // buttons/links always visible attributes |= 3 << 7 // buttons/links always visible
attributes |= (this.cursor.style << 9) attributes |= (this.cursor.style << 9)
serialized += String.fromCodePoint(attributes + 1) data += String.fromCodePoint(attributes + 1)
return data
}
getButtons () {
let data = 'B'
data += String.fromCodePoint(6)
data += this.buttonLabels.map(x => x + '\x01').join('')
return data
}
getCursor () {
let data = 'C'
data += String.fromCodePoint(this.cursor.y + 1)
data += String.fromCodePoint(this.cursor.x + 1)
data += String.fromCodePoint(1)
return data
}
encodeColor (color) {
if (color < 256) {
return String.fromCodePoint(color + 1)
} else {
color -= 256
return String.fromCodePoint(((color & 0xFFF) | 0x10000) + 1) + String.fromCodePoint((color >> 12) + 1)
}
}
serializeScreen () {
let serialized = 'S'
serialized += String.fromCodePoint(1) + String.fromCodePoint(1)
serialized += String.fromCodePoint(this.height + 1) + String.fromCodePoint(this.width + 1)
let lastStyle = null let lastStyle = [null, null, null]
let index = 0 let index = 0
for (let cell of this.screen) { for (let cell of this.screen) {
let style = cell[1] let style = cell[1].slice()
if (this.rainbow) { if (this.rainbow) {
let x = index % this.width let x = index % this.width
let y = Math.floor(index / this.width) let y = Math.floor(index / this.width)
// C instead of F in mask and 1 << 8 in attrs to change attr bits 8 and 9 // C instead of F in mask and 1 << 8 in attrs to change attr bits 1 and 2
style = (style & 0xFFFC0000) | (1 << 8 << 16) | getRainbowColor((x + y) / 10 + Date.now() / 1000) style[0] = getRainbowColor((x + y) / 10 + Date.now() / 1000)
style[1] = 0
style[2] = style[2] | 1
if (style[2] & (1 << 1)) style[2] ^= (1 << 1)
index++ index++
} }
if (style !== lastStyle) {
let foreground = style & 0xFF let foreground = style[0]
let background = (style >> 8) & 0xFF let background = style[1]
let attributes = (style >> 16) & 0xFFFF let attributes = style[2]
let setForeground = foreground !== (lastStyle & 0xFF) let setForeground = foreground !== lastStyle[0]
let setBackground = background !== ((lastStyle >> 8) & 0xFF) let setBackground = background !== lastStyle[1]
let setAttributes = attributes !== ((lastStyle >> 16) & 0xFFFF) let setAttributes = attributes !== lastStyle[2]
if (setForeground && setBackground) serialized += '\x03' + String.fromCodePoint((style & 0xFFFF) + 1) if (setForeground && setBackground) {
else if (setForeground) serialized += '\x05' + String.fromCodePoint(foreground + 1) if (foreground < 256 && background < 256) {
else if (setBackground) serialized += '\x06' + String.fromCodePoint(background + 1) serialized += '\x03' + String.fromCodePoint(((background << 8) | foreground) + 1)
if (setAttributes) serialized += '\x04' + String.fromCodePoint(attributes + 1) } else {
lastStyle = style serialized += '\x05' + this.encodeColor(foreground)
} serialized += '\x06' + this.encodeColor(background)
}
} else if (setForeground) serialized += '\x05' + this.encodeColor(foreground)
else if (setBackground) serialized += '\x06' + this.encodeColor(background)
if (setAttributes) serialized += '\x04' + String.fromCodePoint(attributes + 1)
lastStyle = style
serialized += cell[0] serialized += cell[0]
} }
return serialized return serialized
} }
scheduleLoad () { getUpdate () {
clearTimeout(this._scheduledLoad) let topics = 0
if (this._lastLoad < Date.now() - TERM_MIN_DRAW_DELAY) { let topicData = []
this.termScreen.load(this.serialize(), { theme: this.theme }) let screenOpts = this.getScreenOpts()
this.theme = -1 // prevent useless theme setting next time let buttons = this.getButtons()
} else { let cursor = this.getCursor()
this._scheduledLoad = setTimeout(() => { let screen = this.serializeScreen()
this.termScreen.load(this.serialize()) if (this._screenOpts !== screenOpts) {
}, TERM_MIN_DRAW_DELAY - this._lastLoad) this._screenOpts = screenOpts
} topicData.push(screenOpts)
} }
rainbowTimer () { if (this._buttons !== buttons) {
if (!this.rainbow) return this._buttons = buttons
clearInterval(this._rainbowTimer) topicData.push(buttons)
this._rainbowTimer = setInterval(() => { }
if (this.rainbow) this.scheduleLoad() if (this._cursor !== cursor) {
}, 50) this._cursor = cursor
topicData.push(cursor)
}
if (this._screen !== screen) {
this._screen = screen
topicData.push(screen)
}
if (!topicData.length) return ''
return 'U' + String.fromCodePoint(topics + 1) + topicData.join('')
}
loadTimer () {
clearInterval(this._loadTimer)
this._loadTimer = setInterval(() => {
let update = this.getUpdate()
if (update) this.termScreen.load(update)
}, 30)
} }
} }
@ -329,7 +400,7 @@ let demoData = {
buttons: { buttons: {
1: '', 1: '',
2: '', 2: '',
3: '', 3: (terminal, shell) => shell.write('\x03'),
4: '', 4: '',
5: function (terminal, shell) { 5: function (terminal, shell) {
if (shell.child) shell.child.destroy() if (shell.child) shell.child.destroy()
@ -466,7 +537,7 @@ let demoshIndex = {
if (++x < 69) { if (++x < 69) {
if (++cycles >= 3) { if (++cycles >= 3) {
setTimeout(loop, 20) setTimeout(loop, 50)
cycles = 0 cycles = 0
} else loop() } else loop()
} else { } else {
@ -557,7 +628,7 @@ let demoshIndex = {
let theme = +args[0] | 0 let theme = +args[0] | 0
const maxnum = themes.length const maxnum = themes.length
if (!args.length || !Number.isFinite(theme) || theme < 0 || theme >= maxnum) { if (!args.length || !Number.isFinite(theme) || theme < 0 || theme >= maxnum) {
this.emit('write', `\x1b[31mUsage: theme [0–${maxnum - 1}]\r\n`) this.emit('write', `\x1b[31mUsage: theme [0–${maxnum - 1}]\n`)
this.destroy() this.destroy()
return return
} }
@ -568,6 +639,40 @@ let demoshIndex = {
this.destroy() this.destroy()
} }
}, },
themes: class ShowThemes extends Process {
color (hex) {
hex = parseInt(hex.substr(1), 16)
let r = hex >> 16
let g = (hex >> 8) & 0xFF
let b = hex & 0xFF
this.emit('write', `\x1b[48;2;${r};${g};${b}m`)
if (((r + g + b) / 3) > 127) {
this.emit('write', '\x1b[38;5;16m')
} else {
this.emit('write', '\x1b[38;5;255m')
}
}
run (...args) {
for (let i in themes) {
let theme = themes[i]
let name = ` ${i}`.substr(-2)
this.emit('write', `Theme ${name}: `)
for (let col = 0; col < 16; col++) {
let text = ` ${col}`.substr(-2)
this.color(theme[col])
this.emit('write', text)
this.emit('write', '\x1b[m ')
}
this.emit('write', '\n')
}
this.destroy()
}
},
cursor: class SetCursor extends Process { cursor: class SetCursor extends Process {
run (...args) { run (...args) {
let steady = args.includes('--steady') let steady = args.includes('--steady')
@ -590,7 +695,6 @@ let demoshIndex = {
} }
run () { run () {
this.shell.terminal.rainbow = !this.shell.terminal.rainbow this.shell.terminal.rainbow = !this.shell.terminal.rainbow
this.shell.terminal.rainbowTimer()
this.emit('write', '') this.emit('write', '')
this.destroy() this.destroy()
} }
@ -618,7 +722,7 @@ let demoshIndex = {
} }
render () { render () {
this.emit('write', '\x1b[m\x1b[2J\x1b[1;1H') this.emit('write', '\x1b[m\x1b[2J\x1b[1;1H')
this.emit('write', '\x1b[97m\x1b[1mMouse Demo\r\n\x1b[mMouse movement, clicking and scrolling!') this.emit('write', '\x1b[97m\x1b[1mMouse Demo\r\n\x1b[mMouse movement, clicking, and scrolling!')
// render random data for scrolling // render random data for scrolling
for (let y = 0; y < 23; y++) { for (let y = 0; y < 23; y++) {

Loading…
Cancel
Save