Merge remote-tracking branch 'origin/work'

pull/2/head
Ondřej Hruška 7 years ago
commit 855d308962
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 11
      base.php
  2. 1
      js/appcommon.js
  3. 220
      js/term/demo.js
  4. 13
      js/term/screen.js
  5. 15
      js/term/screen_attr_bits.js
  6. 63
      js/term/screen_parser.js
  7. 75
      js/term/screen_renderer.js
  8. 3
      lang/cs.php
  9. 1
      lang/en.php
  10. 6
      pages/cfg_term.php
  11. 2
      pages/help/charsets.php
  12. 41
      pages/help/cmd_system.php
  13. 4
      pages/help/sgr_colors.php
  14. 14
      sass/pages/_term.scss

@ -173,9 +173,18 @@ if (!function_exists('load_esp_charsets')) {
$rows = array_map('trim', $rows);
foreach($rows as $j => $v) {
$literal = false;
if (strpos($v, '0x') === 0) {
// hexa codes
$v = substr($v, 2);
$v = hexdec($v);
} else if (strpos($v, 'u\'\\0\'') === 0) {
// zero
$v = 0;
} else if (strpos($v, 'u\'') === 0) {
// utf8 literals
$v = mb_substr($v, 2, 1, 'utf-8');
$literal = true;
} else {
$v = intval($v);
}
@ -183,7 +192,7 @@ if (!function_exists('load_esp_charsets')) {
$table[] = [
$ascii,
chr($ascii),
utf8($v==0? $ascii :$v),
$literal ? $v : utf8($v==0? $ascii :$v),
];
}
$cps[$name] = $table;

@ -169,6 +169,7 @@ $.ready(function () {
'Server connection failed! Trying again' +
'<span class="anim-dots" style="width:1.5em;text-align:left;display:inline-block">.</span>'
qs('#screen').appendChild(bnr)
qs('#screen').classList.add('failed')
showPage()
}
}, 2000)

@ -143,6 +143,8 @@ class ScrollingTerminal {
this.cursor = { x: 0, y: 0, style: 1, visible: true }
this.trackMouse = false
this.theme = 0
this.defaultFG = 7
this.defaultBG = 0
this.rainbow = this.superRainbow = false
this.parser.reset()
this.clear()
@ -274,10 +276,10 @@ class ScrollingTerminal {
data += encodeAsCodePoint(25)
data += encodeAsCodePoint(80)
data += encodeAsCodePoint(this.theme)
data += encodeAsCodePoint(7)
data += encodeAsCodePoint(0)
data += encodeAsCodePoint(0)
data += encodeAsCodePoint(0)
data += encodeAsCodePoint(this.defaultFG & 0xFFFF)
data += encodeAsCodePoint(this.defaultFG >> 16)
data += encodeAsCodePoint(this.defaultBG & 0xFFFF)
data += encodeAsCodePoint(this.defaultBG >> 16)
let attributes = +this.cursor.visible
attributes |= (3 << 5) * +this.trackMouse // track mouse controls both
attributes |= 3 << 7 // buttons/links always visible
@ -647,37 +649,203 @@ let demoshIndex = {
}
},
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')
}
constructor (shell) {
super()
this.shell = shell
this.parser = new ANSIParser((...args) => this.handler(...args))
}
destroy () {
this.shell.terminal.cursor.style = this.savedCursorStyle
this.emit('write', '\n\n')
super.destroy()
}
run (...args) {
for (let i in themes) {
let theme = themes[i]
this.savedCursorStyle = this.shell.terminal.cursor.style
this.shell.terminal.cursor.style = 3
this.fgType = 0
this.bgType = 0
let get24FG = () => this.shell.terminal.defaultFG - 256
let set24FG = v => { this.shell.terminal.defaultFG = v + 256 }
let get24BG = () => this.shell.terminal.defaultBG - 256
let set24BG = v => { this.shell.terminal.defaultBG = v + 256 }
let make24Control = (label, index, getValue, setValue, type) => {
index *= 4
return {
label,
length: 1,
getValue: () => (getValue() >> index) & 0xF,
getDisplay: () => ((getValue() >> index) & 0xF).toString(16),
setValue: value => {
setValue((getValue() & (0xFFFFFF ^ (0xF << index))) | ((value & 0xF) << index))
},
shouldShow: () => this[type + 'Type'] === 1,
parseValue: input => {
return parseInt(input, 16) & 0xF
},
moveAfterInput: index !== 0
}
}
this.controls = [
{
label: 'Theme: ',
length: themes.length.toString().length,
getValue: () => this.shell.terminal.theme,
setValue: value => {
let count = themes.length
this.shell.terminal.theme = ((value % count) + count) % count
}
},
{
label: ' Default Foreground: ',
length: 6,
getValue: () => this.fgType,
getDisplay: () => this.fgType === 0 ? '256' : '24-bit',
setValue: value => {
this.fgType = ((value % 2) + 2) % 2
}
},
{
label: ' ',
length: 3,
getValue: () => this.shell.terminal.defaultFG & 0xFF,
setValue: value => {
this.shell.terminal.defaultFG = value & 0xFF
},
shouldShow: () => this.fgType === 0,
parseValue: input => parseInt(input, 16)
},
make24Control(' #', 5, get24FG, set24FG, 'fg'),
make24Control('', 4, get24FG, set24FG, 'fg'),
make24Control('', 3, get24FG, set24FG, 'fg'),
make24Control('', 2, get24FG, set24FG, 'fg'),
make24Control('', 1, get24FG, set24FG, 'fg'),
make24Control('', 0, get24FG, set24FG, 'fg'),
{
label: ' Default Background: ',
length: 6,
getValue: () => this.bgType,
getDisplay: () => this.bgType === 0 ? '256' : '24-bit',
setValue: value => {
this.bgType = ((value % 2) + 2) % 2
}
},
{
label: ' ',
length: 3,
getValue: () => this.shell.terminal.defaultBG & 0xFF,
setValue: value => {
this.shell.terminal.defaultBG = value & 0xFF
},
shouldShow: () => this.bgType === 0,
parseValue: input => parseInt(input, 16)
},
make24Control(' #', 5, get24BG, set24BG, 'bg'),
make24Control('', 4, get24BG, set24BG, 'bg'),
make24Control('', 3, get24BG, set24BG, 'bg'),
make24Control('', 2, get24BG, set24BG, 'bg'),
make24Control('', 1, get24BG, set24BG, 'bg'),
make24Control('', 0, get24BG, set24BG, 'bg')
]
this.selection = 0
this.emit('write', '\x1b[1mThemes\x1b[m\n\n\n\n\x1b[2A')
this.render()
}
render () {
this.emit('write', '\x1b[m\r')
// no ^[2K implementation, here's a hack
this.emit('write', ' '.repeat(this.shell.terminal.width - 1) + '\r')
let name = ` ${i}`.substr(-2)
let index = 0
let selectedX = 0
for (let control of this.controls) {
if (control.shouldShow && !control.shouldShow()) continue
if (control.label) {
this.emit('write', `\x1b[1m${control.label}\x1b[m`)
}
// TODO: colors
this.emit('write', '\x1b[38;5;255m\x1b[48;5;16m')
let value = control.getDisplay ? control.getDisplay() : control.getValue().toString()
this.emit('write', (control.fill || ' ').repeat(Math.max(0, control.length - value.length)))
this.emit('write', value.substr(0, control.length))
this.emit('write', '\x1b[m')
this.emit('write', `Theme ${name}: `)
if (index === this.selection) {
selectedX = this.shell.terminal.cursor.x - 1
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 ')
// draw arrows
this.emit('write', '\x1b[m\x1b[D\x1b[A▲\x1b[D\x1b[2B▼\x1b[A')
} else {
// clear arrows if they were there
this.emit('write', '\x1b[m\x1b[D\x1b[A \x1b[D\x1b[2B \x1b[A')
}
this.emit('write', '\n')
index++
}
this.destroy()
this.shell.terminal.cursor.x = selectedX
}
write (data) {
this.parser.write(data)
}
getControlCount () {
let count = 0
for (let control of this.controls) {
if (control.shouldShow && !control.shouldShow()) continue
count++
}
return count
}
getSelectedControl () {
let selected = null
let index = 0
for (let control of this.controls) {
if (control.shouldShow && !control.shouldShow()) continue
if (index === this.selection) {
selected = control
break
}
index++
}
return selected
}
handler (action, ...args) {
console.log(action, ...args)
if (action === 'move-cursor-x') {
this.selection += args[0]
let count = this.getControlCount()
this.selection = ((this.selection % count) + count) % count
} else if (action === 'move-cursor-y') {
let control = this.getSelectedControl()
if (control) control.setValue(control.getValue() - args[0])
} else if (action === 'write') {
let control = this.getSelectedControl()
if (control && control.parseValue) {
let parsed = control.parseValue(args[0])
if (Number.isFinite(parsed)) {
control.setValue(parsed)
if (control.moveAfterInput) {
if (this.selection < this.getControlCount() - 1) this.selection++
}
}
}
}
this.render()
}
},
cursor: class SetCursor extends Process {

@ -305,6 +305,19 @@ module.exports = class TermScreen extends EventEmitter {
this._scheduledSizeUpdate = setTimeout(() => this.updateSize(), 1)
}
get backgroundImage () {
return this.canvas.style.backgroundImage
}
set backgroundImage (value) {
this.canvas.style.backgroundImage = value ? `url(${value})` : ''
if (this.renderer.backgroundImage !== !!value) {
this.renderer.backgroundImage = !!value
this.renderer.resetDrawn()
this.renderer.scheduleDraw('background-image')
}
}
/**
* Returns a CSS font string with this TermScreen's font settings and the
* font modifiers.

@ -0,0 +1,15 @@
// Bits in the cell attribs word
/* eslint-disable no-multi-spaces */
exports.ATTR_FG = (1 << 0) // 1 if not using default background color (ignore cell bg) - color extension bit
exports.ATTR_BG = (1 << 1) // 1 if not using default foreground color (ignore cell fg) - color extension bit
exports.ATTR_BOLD = (1 << 2) // Bold font
exports.ATTR_UNDERLINE = (1 << 3) // Underline decoration
exports.ATTR_INVERSE = (1 << 4) // Invert colors - this is useful so we can clear then with SGR manipulation commands
exports.ATTR_BLINK = (1 << 5) // Blinking
exports.ATTR_ITALIC = (1 << 6) // Italic font
exports.ATTR_STRIKE = (1 << 7) // Strike-through decoration
exports.ATTR_OVERLINE = (1 << 8) // Over-line decoration
exports.ATTR_FAINT = (1 << 9) // Faint foreground color (reduced alpha)
exports.ATTR_FRAKTUR = (1 << 10) // Fraktur font (unicode substitution)
/* eslint-enable no-multi-spaces */

@ -1,6 +1,17 @@
const $ = require('../lib/chibi')
const { qs } = require('../utils')
const {
ATTR_FG,
ATTR_BG,
ATTR_BOLD,
ATTR_UNDERLINE,
ATTR_BLINK,
ATTR_STRIKE,
ATTR_OVERLINE,
ATTR_FAINT
} = require('./screen_attr_bits')
// constants for decoding the update blob
const SEQ_SKIP = 1
const SEQ_REPEAT = 2
@ -24,6 +35,7 @@ const TOPIC_BUTTONS = 'B'
const TOPIC_CURSOR = 'C'
const TOPIC_INTERNAL = 'D'
const TOPIC_BELL = '!'
const TOPIC_BACKDROP = 'W'
const OPT_CURSOR_VISIBLE = (1 << 0)
const OPT_DEBUGBAR = (1 << 1)
@ -39,17 +51,6 @@ const OPT_CRLF_MODE = (1 << 12)
const OPT_BRACKETED_PASTE = (1 << 13)
const OPT_REVERSE_VIDEO = (1 << 14)
const ATTR_FG = (1 << 0) // 1 if not using default background color (ignore cell bg) - color extension bit
const ATTR_BG = (1 << 1) // 1 if not using default foreground color (ignore cell fg) - color extension bit
const ATTR_BOLD = (1 << 2) // Bold font
const ATTR_UNDERLINE = (1 << 3) // Underline decoration
const ATTR_INVERSE = (1 << 4) // Invert colors - this is useful so we can clear then with SGR manipulation commands
const ATTR_BLINK = (1 << 5) // Blinking
const ATTR_ITALIC = (1 << 6) // Italic font
const ATTR_STRIKE = (1 << 7) // Strike-through decoration
const ATTR_OVERLINE = (1 << 8) // Over-line decoration
const ATTR_FAINT = (1 << 9) // Faint foreground color (reduced alpha)
const ATTR_FRAKTUR = (1 << 10) // Fraktur font (unicode substitution)
/* eslint-enable no-multi-spaces */
module.exports = class ScreenParser {
@ -65,7 +66,9 @@ module.exports = class ScreenParser {
*/
hideLoadFailedMsg () {
if (!this.contentLoaded) {
let scr = qs('#screen')
let errmsg = qs('#load-failed')
if (scr) scr.classList.remove('failed')
if (errmsg) errmsg.parentNode.removeChild(errmsg)
this.contentLoaded = true
}
@ -82,6 +85,20 @@ module.exports = class ScreenParser {
const topics = du(strArray[ci++])
// this.screen.cursor.hanging = !!(attributes & (1 << 1))
let collectOneTerminatedString = () => {
// TODO optimize this
text = ''
while (ci < strArray.length) {
let c = strArray[ci++]
if (c !== '\x01') {
text += c
} else {
break
}
}
return text
}
while (ci < strArray.length) {
const topic = strArray[ci++]
@ -177,17 +194,7 @@ module.exports = class ScreenParser {
this.screen.renderer.scheduleDraw('cursor-moved')
} else if (topic === TOPIC_TITLE) {
// TODO optimize this
text = ''
while (ci < strArray.length) {
let c = strArray[ci++]
if (c !== '\x01') {
text += c
} else {
break
}
}
text = collectOneTerminatedString()
qs('#screen-title').textContent = text
if (text.length === 0) text = 'Terminal'
qs('title').textContent = `${text} :: ESPTerm`
@ -197,16 +204,16 @@ module.exports = class ScreenParser {
let labels = []
for (let j = 0; j < count; j++) {
text = ''
while (ci < strArray.length) {
let c = strArray[ci++]
if (c === '\x01') break
text += c
}
text = collectOneTerminatedString()
labels.push(text)
}
this.screen.emit('button-labels', labels)
} else if (topic === TOPIC_BACKDROP) {
text = collectOneTerminatedString()
this.screen.backgroundImage = text
} else if (topic === TOPIC_BELL) {
this.screen.beep()

@ -1,4 +1,22 @@
const { themes, buildColorTable, SELECTION_FG, SELECTION_BG } = require('./themes')
const {
themes,
buildColorTable,
SELECTION_FG, SELECTION_BG
} = require('./themes')
const {
ATTR_FG,
ATTR_BG,
ATTR_BOLD,
ATTR_UNDERLINE,
ATTR_INVERSE,
ATTR_BLINK,
ATTR_ITALIC,
ATTR_STRIKE,
ATTR_OVERLINE,
ATTR_FAINT,
ATTR_FRAKTUR
} = require('./screen_attr_bits')
// Some non-bold Fraktur symbols are outside the contiguous block
const frakturExceptions = {
@ -9,21 +27,6 @@ const frakturExceptions = {
'Z': '\u2128'
}
// TODO do not repeat - this is also defined in screen_parser ...
/* eslint-disable no-multi-spaces */
const ATTR_FG = (1 << 0) // 1 if not using default background color (ignore cell bg) - color extension bit
const ATTR_BG = (1 << 1) // 1 if not using default foreground color (ignore cell fg) - color extension bit
const ATTR_BOLD = (1 << 2) // Bold font
const ATTR_UNDERLINE = (1 << 3) // Underline decoration
const ATTR_INVERSE = (1 << 4) // Invert colors - this is useful so we can clear then with SGR manipulation commands
const ATTR_BLINK = (1 << 5) // Blinking
const ATTR_ITALIC = (1 << 6) // Italic font
const ATTR_STRIKE = (1 << 7) // Strike-through decoration
const ATTR_OVERLINE = (1 << 8) // Over-line decoration
const ATTR_FAINT = (1 << 9) // Faint foreground color (reduced alpha)
const ATTR_FRAKTUR = (1 << 10) // Fraktur font (unicode substitution)
/* eslint-enable no-multi-spaces */
module.exports = class ScreenRenderer {
constructor (screen) {
this.screen = screen
@ -89,6 +92,9 @@ module.exports = class ScreenRenderer {
this.defaultFgNum = fg
this.defaultBgNum = bg
this.scheduleDraw('default-colors')
// full bg with default color (goes behind the image)
this.screen.canvas.style.backgroundColor = this.getColor(bg)
}
}
@ -180,8 +186,9 @@ module.exports = class ScreenRenderer {
* @param {number} options.cellWidth - cell width in pixels
* @param {number} options.cellHeight - cell height in pixels
* @param {number} options.bg - the background color
* @param {number} options.isDefaultBG - if true, will draw image background if available
*/
drawBackground ({ x, y, cellWidth, cellHeight, bg }) {
drawBackground ({ x, y, cellWidth, cellHeight, bg, isDefaultBG }) {
const ctx = this.ctx
const { width, height } = this.screen.window
const padding = Math.round(this.screen._padding)
@ -189,6 +196,8 @@ module.exports = class ScreenRenderer {
let screenX = x * cellWidth + padding
let screenY = y * cellHeight + padding
let isBorderCell = x === 0 || y === 0 || x === width - 1 || y === height - 1
let fillX, fillY, fillWidth, fillHeight
if (isBorderCell) {
let left = screenX
let top = screenY
@ -198,11 +207,22 @@ module.exports = class ScreenRenderer {
else if (x === width - 1) right += padding
if (y === 0) top -= padding
else if (y === height - 1) bottom += padding
ctx.clearRect(left, top, right - left, bottom - top)
ctx.fillRect(left, top, right - left, bottom - top)
fillX = left
fillY = top
fillWidth = right - left
fillHeight = bottom - top
} else {
ctx.clearRect(screenX, screenY, cellWidth, cellHeight)
ctx.fillRect(screenX, screenY, cellWidth, cellHeight)
fillX = screenX
fillY = screenY
fillWidth = cellWidth
fillHeight = cellHeight
}
ctx.clearRect(fillX, fillY, fillWidth, fillHeight)
if (!isDefaultBG || bg < 0 || !this.backgroundImage) {
ctx.fillRect(fillX, fillY, fillWidth, fillHeight)
}
}
@ -497,8 +517,13 @@ module.exports = class ScreenRenderer {
let bg = this.screen.screenBG[cell] | 0
let attrs = this.screen.screenAttrs[cell] | 0
let isDefaultBG = false
if (!(attrs & ATTR_FG)) fg = this.defaultFgNum
if (!(attrs & ATTR_BG)) bg = this.defaultBgNum
if (!(attrs & ATTR_BG)) {
bg = this.defaultBgNum
isDefaultBG = true
}
if (attrs & ATTR_INVERSE) [fg, bg] = [bg, fg] // swap - reversed character colors
if (this.screen.reverseVideo) [fg, bg] = [bg, fg] // swap - reversed all screen
@ -525,7 +550,7 @@ module.exports = class ScreenRenderer {
let font = attrs & FONT_MASK
if (!fontGroups.has(font)) fontGroups.set(font, [])
fontGroups.get(font).push({ cell, x, y, text, fg, bg, attrs, isCursor, inSelection })
fontGroups.get(font).push({ cell, x, y, text, fg, bg, attrs, isCursor, inSelection, isDefaultBG })
updateMap.set(cell, didUpdate)
}
@ -595,10 +620,10 @@ module.exports = class ScreenRenderer {
// pass 1: backgrounds
for (let font of fontGroups.keys()) {
for (let data of fontGroups.get(font)) {
let { cell, x, y, text, bg } = data
let { cell, x, y, text, bg, isDefaultBG } = data
if (redrawMap.get(cell)) {
this.drawBackground({ x, y, cellWidth, cellHeight, bg })
this.drawBackground({ x, y, cellWidth, cellHeight, bg, isDefaultBG })
if (this.screen.window.debug && this.screen._debug) {
// set cell flags

@ -4,7 +4,7 @@ return [
'menu.cfg_wifi' => 'Nastavení WiFi',
'menu.cfg_network' => 'Nastavení sítě',
'menu.cfg_term' => 'Nastavení terminalu',
'menu.about' => 'About',
'menu.about' => 'O programu',
'menu.help' => 'Nápověda',
'menu.term' => 'Zpět k terminálu',
'menu.cfg_system' => 'Nastavení systému',
@ -80,6 +80,7 @@ return [
'term.colors_preview' => '',
'term.debugbar' => 'Rozšířené ladění',
'term.ascii_debug' => 'Ladění vstupních dat',
'term.backdrop' => 'URL obrázku na pozadí',
'cursor.block_blink' => 'Blok, blikající',
'cursor.block_steady' => 'Blok, stálý',

@ -79,6 +79,7 @@ return [
'term.colors_preview' => '',
'term.debugbar' => 'Debug internal state',
'term.ascii_debug' => 'Display control codes',
'term.backdrop' => 'Background image URL',
'cursor.block_blink' => 'Block, blinking',
'cursor.block_steady' => 'Block, steady',

@ -172,6 +172,11 @@
<input class="tiny" type="text" name="bm5" id="bm5" value="%h:bm5%">
</div>
<div class="Row">
<label for="backdrop"><?= tr('term.backdrop') ?></label>
<input type="text" name="backdrop" id="backdrop" value="%h:backdrop%" required>
</div>
<div class="Row checkbox" >
<label><?= tr('term.crlf_mode') ?></label><!--
--><span class="box" tabindex=0 role=checkbox></span>
@ -282,7 +287,6 @@
<input type="hidden" id="ascii_debug" name="ascii_debug" value="%ascii_debug%">
</div>
<div class="Row checkbox" >
<label><?= tr('term.fn_alt_mode') ?></label><!--
--><span class="box" tabindex=0 role=checkbox></span>

@ -21,6 +21,8 @@
<li>`A` - UK ASCII: # replaced with £</li>
<li>`0` - Symbols and basic line drawing (standard DEC alternate character set)</li>
<li>`1` - Symbols and advanced line drawing (based on DOS codepage 437, ESPTerm specific)</li>
<li>`2` - Block characters and thick line drawing (ESPTerm specific)</li>
<li>`3` - Extra line drawing (ESPTerm specific)</li>
</ul>
<p>

@ -11,11 +11,28 @@
<table class="ansiref w100">
<thead><tr><th>Code</th><th>Meaning</th></tr></thead>
<tbody>
<tr>
<td>_CAN_ (24)</td>
<td>
This ASCII code is sent by ESPTerm when it becomes ready to receive commands.
When this code is received on the UART, it means ESPTerm has restarted and is ready.
Use this to detect spontaneous restarts which require a full screen repaint.
As a control character sent to ESPTerm, CAN aborts any currently received commands
and clears the parser.
</td>
</tr>
<tr>
<td>_ENQ_ (5)</td>
<td>
ESPTerm responds to this control characters with an "answerback message".
This message contains the curretn version, unique ID, and the IP address if in Client mode.
</td>
</tr>
<tr>
<td>`\ec`</td>
<td>
Clear screen, reset attributes and cursor. This command also restores the default
screen size, title, button labels and messages.
screen size, title, button labels and messages and the background URL.
</td>
</tr>
<tr>
@ -29,14 +46,6 @@
Can be used to check if the terminal has booted up and is ready to receive commands.
</td>
</tr>
<tr>
<td>_CAN_ (24)</td>
<td>
This ASCII code is not a command, but is sent by ESPTerm when it becomes ready to receive commands.
When this code is received on the UART, it means ESPTerm has restarted and is ready. Use this to detect
spontaneous restarts which require a full screen repaint.
</td>
</tr>
<tr>
<td>`\e[<i>n</i> q`</td>
<td>
@ -50,6 +59,16 @@
<td>`\e]0;<i>t</i>\a`</td>
<td>Set screen title to _t_ (this is a standard OSC command)</td>
</tr>
<tr>
<td>`\e]70;<i>u</i>\a`</td>
<td>
Set background image to URL _u_ (including protocol)
that can be resolved by the user's browser. The image will be scaled
to fit the screen, preserving aspect ratio. A certain border must be added
to account for the screen margins. Use empty string to disable the image feature.
Note that this *won't work for users connected to the built-in AP*.
</td>
</tr>
<tr>
<td>
<code>
@ -68,8 +87,8 @@
</code>
</td>
<td>
Set message for button 1-5 (code 91-95) to _m_ - e.g.`\e]94;*\a`
sets the 3rd button to send "*" when pressed. The message can be up to
Set message for button 1-5 (code 91-95) to _m_ - e.g.`\e]94;+\a`
sets the 3rd button to send "+" when pressed. The message can be up to
10 bytes long.
</td>
</tr>

@ -16,6 +16,10 @@
can be selected in <a href="<?= url('cfg_term') ?>">Terminal Settings</a>.
</p>
<p>
Background image can be set using `\e]70;<i>url</i>\a` (see section System Functions).
</p>
<h3>Foreground colors</h3>
<div class="colorprev">

@ -25,6 +25,12 @@ body.term {
cursor: default;
canvas {
background-repeat: no-repeat;
background-position: center center;
background-size: contain;
}
canvas.selectable {
cursor: text;
}
@ -140,7 +146,13 @@ body.term {
color: red;
font-size: 18px;
font-weight: bold;
margin: 10px 5px 14px 5px;
margin: 20px 15px;
}
#screen.failed {
canvas, .screen-margin {
display: none;
}
}
#term-nav {

Loading…
Cancel
Save