Start removing references to screen in parser

pull/1/head
cpsdqs 7 years ago
parent d154df6360
commit 735ee58340
Signed by untrusted user: cpsdqs
GPG Key ID: 3F59586BB7448DD1
  1. 77
      js/term/screen.js
  2. 415
      js/term/screen_parser.js

@ -472,6 +472,15 @@ module.exports = class TermScreen extends EventEmitter {
} }
} }
resetScreen () {
const { width, height } = this.window
this.blinkingCellCount = 0
this.screen.screen = new Array(width * height).fill(' ')
this.screen.screenFG = new Array(width * height).fill(0)
this.screen.screenBG = new Array(width * height).fill(0)
this.screen.screenAttrs = new Array(width * height).fill(0)
}
/** /**
* Returns a normalized version of the current selection, such that `start` * Returns a normalized version of the current selection, such that `start`
* is always before `end`. * is always before `end`.
@ -628,6 +637,72 @@ module.exports = class TermScreen extends EventEmitter {
} }
load (...args) { load (...args) {
this.parser.load(...args) const updates = this.parser.parse(...args)
for (let update of updates) {
switch (update.topic) {
case 'screen-opts':
if (update.width !== this.window.width || update.height !== this.window.height) {
this.window.width = update.width
this.window.height = update.height
this.resetScreen()
}
this.renderer.loadTheme(update.theme)
this.renderer.setDefaultColors(update.defFG, update.defBG)
this.cursor.visible = update.cursorVisible
this.input.setAlts(...update.inputAlts)
this.mouseMode.clicks = update.trackMouseClicks
this.mouseMode.movement = update.trackMouseMovement
this.input.setMouseMode(update.trackMouseClicks, update.trackMouseMovement)
this.selection.setSelectable(!update.trackMouseClicks && !update.trackMouseMovement)
if (this.cursor.blinking !== update.cursorBlinking) {
this.cursor.blinking = update.cursorBlinking
this.renderer.resetCursorBlink()
}
this.cursor.style = update.cursorStyle
this.bracketedPaste = update.bracketedPaste
this.reverseVideo = update.reverseVideo
this.window.debug &= 0b01
this.window.debug |= (+update.debugEnabled << 1)
this.emit('TEMP:show-buttons', update.showButtons)
this.emit('TEMP:show-links', update.showConfigLinks)
break
case 'cursor':
if (this.cursor.x !== update.x || this.cursor.y !== update.y) {
this.cursor.x = update.x
this.cursor.y = update.y
this.cursor.hanging = update.hanging
this.renderer.resetCursorBlink()
this.emit('cursor-moved')
this.renderer.scheduleDraw('cursor-moved')
}
break
case 'title':
this.emit('TEMP:update-title', update.title)
break
case 'button-labels':
this.emit('button-labels', update.labels)
break
case 'bell':
this.beep()
break
case 'internal':
this.emit('internal', update)
break
case 'content':
update.tempDoNotCommitUpstream()
break
default:
console.log('Unhandled update', update)
}
}
} }
} }

@ -19,6 +19,7 @@ const SEQ_SET_BG = 6
const SEQ_SET_ATTR_0 = 7 const SEQ_SET_ATTR_0 = 7
function du (str) { function du (str) {
if (!str) return NaN
let num = str.codePointAt(0) let num = str.codePointAt(0)
if (num > 0xDFFF) num -= 0x800 if (num > 0xDFFF) num -= 0x800
return num - 1 return num - 1
@ -52,7 +53,15 @@ const OPT_REVERSE_VIDEO = (1 << 14)
module.exports = class ScreenParser { module.exports = class ScreenParser {
constructor (screen) { constructor (screen) {
this.screen = screen // this.screen = screen
let didWarn = false
Object.defineProperty(this, 'screen', {
get () {
if (!didWarn) console.warn('Deprecated get ScreenParser#screen')
didWarn = true
return screen
}
})
// true if TermScreen#load was called at least once // true if TermScreen#load was called at least once
this.contentLoaded = false this.contentLoaded = false
@ -68,14 +77,13 @@ module.exports = class ScreenParser {
} }
} }
loadUpdate (str) { parseUpdate (str) {
// console.log(`update ${str}`) // console.log(`update ${str}`)
// current index // current index
let ci = 0 let ci = 0
let strArray = Array.from ? Array.from(str) : str.split('') let strArray = Array.from ? Array.from(str) : str.split('')
let text let text
let resized = false
const topics = du(strArray[ci++]) const topics = du(strArray[ci++])
// this.screen.cursor.hanging = !!(attributes & (1 << 1)) // this.screen.cursor.hanging = !!(attributes & (1 << 1))
@ -93,35 +101,30 @@ module.exports = class ScreenParser {
return text return text
} }
const updates = []
while (ci < strArray.length) { while (ci < strArray.length) {
const topic = strArray[ci++] const topic = strArray[ci++]
if (topic === TOPIC_SCREEN_OPTS) { if (topic === TOPIC_SCREEN_OPTS) {
const newHeight = du(strArray[ci++]) const height = du(strArray[ci++])
const newWidth = du(strArray[ci++]) const width = du(strArray[ci++])
const theme = du(strArray[ci++]) const theme = du(strArray[ci++])
const defFg = (du(strArray[ci++]) & 0xFFFF) | ((du(strArray[ci++]) & 0xFFFF) << 16) const defFG = (du(strArray[ci++]) & 0xFFFF) | ((du(strArray[ci++]) & 0xFFFF) << 16)
const defBg = (du(strArray[ci++]) & 0xFFFF) | ((du(strArray[ci++]) & 0xFFFF) << 16) const defBG = (du(strArray[ci++]) & 0xFFFF) | ((du(strArray[ci++]) & 0xFFFF) << 16)
const attributes = du(strArray[ci++])
// theming
this.screen.renderer.loadTheme(theme)
this.screen.renderer.setDefaultColors(defFg, defBg)
// apply size
resized = (this.screen.window.height !== newHeight) || (this.screen.window.width !== newWidth)
this.screen.window.height = newHeight
this.screen.window.width = newWidth
// process attributes // process attributes
this.screen.cursor.visible = !!(attributes & OPT_CURSOR_VISIBLE) const attributes = du(strArray[ci++])
this.screen.input.setAlts( const cursorVisible = !!(attributes & OPT_CURSOR_VISIBLE)
// HACK: input alts are formatted as arguments for Input#setAlts
const inputAlts = [
!!(attributes & OPT_CURSORS_ALT_MODE), !!(attributes & OPT_CURSORS_ALT_MODE),
!!(attributes & OPT_NUMPAD_ALT_MODE), !!(attributes & OPT_NUMPAD_ALT_MODE),
!!(attributes & OPT_FN_ALT_MODE), !!(attributes & OPT_FN_ALT_MODE),
!!(attributes & OPT_CRLF_MODE) !!(attributes & OPT_CRLF_MODE)
) ]
const trackMouseClicks = !!(attributes & OPT_CLICK_TRACKING) const trackMouseClicks = !!(attributes & OPT_CLICK_TRACKING)
const trackMouseMovement = !!(attributes & OPT_MOVE_TRACKING) const trackMouseMovement = !!(attributes & OPT_MOVE_TRACKING)
@ -133,60 +136,58 @@ module.exports = class ScreenParser {
// if it's not zero, decrement such that the two most significant bits // if it's not zero, decrement such that the two most significant bits
// are the type and the least significant bit is the blink state // are the type and the least significant bit is the blink state
if (cursorShape > 0) cursorShape-- if (cursorShape > 0) cursorShape--
const cursorStyle = cursorShape >> 1 let cursorStyle = cursorShape >> 1
const cursorBlinking = !(cursorShape & 1) const cursorBlinking = !(cursorShape & 1)
if (cursorStyle === 0) this.screen.cursor.style = 'block' if (cursorStyle === 0) cursorStyle = 'block'
else if (cursorStyle === 1) this.screen.cursor.style = 'line' else if (cursorStyle === 1) cursorStyle = 'line'
else if (cursorStyle === 2) this.screen.cursor.style = 'bar' else cursorStyle = 'bar'
if (this.screen.cursor.blinking !== cursorBlinking) { if (this.screen.cursor.blinking !== cursorBlinking) {
this.screen.cursor.blinking = cursorBlinking this.screen.cursor.blinking = cursorBlinking
this.screen.renderer.resetCursorBlink() this.screen.renderer.resetCursorBlink()
} }
this.screen.input.setMouseMode(trackMouseClicks, trackMouseMovement)
this.screen.selection.setSelectable(!trackMouseClicks && !trackMouseMovement)
this.screen.mouseMode = {
clicks: trackMouseClicks,
movement: trackMouseMovement
}
const showButtons = !!(attributes & OPT_SHOW_BUTTONS) const showButtons = !!(attributes & OPT_SHOW_BUTTONS)
const showConfigLinks = !!(attributes & OPT_SHOW_CONFIG_LINKS) const showConfigLinks = !!(attributes & OPT_SHOW_CONFIG_LINKS)
this.screen.emit('TEMP:show-config-links', showConfigLinks) const bracketedPaste = !!(attributes & OPT_BRACKETED_PASTE)
this.screen.emit('TEMP:show-buttons', showButtons) const reverseVideo = !!(attributes & OPT_REVERSE_VIDEO)
this.screen.bracketedPaste = !!(attributes & OPT_BRACKETED_PASTE) const debugEnabled = !!(attributes & OPT_DEBUGBAR)
this.screen.reverseVideo = !!(attributes & OPT_REVERSE_VIDEO)
updates.push({
const debugbar = !!(attributes & OPT_DEBUGBAR) topic: 'screen-opts',
width,
this.screen.window.debug &= 0b01 | (+debugbar < 1) height,
theme,
defFG,
defBG,
cursorVisible,
cursorBlinking,
cursorStyle,
inputAlts,
trackMouseClicks,
trackMouseMovement,
showButtons,
showConfigLinks,
bracketedPaste,
reverseVideo,
debugEnabled
})
} else if (topic === TOPIC_CURSOR) { } else if (topic === TOPIC_CURSOR) {
// cursor position // cursor position
const cursorY = du(strArray[ci++]) const y = du(strArray[ci++])
const cursorX = du(strArray[ci++]) const x = du(strArray[ci++])
const hanging = du(strArray[ci++]) const hanging = !!du(strArray[ci++])
const cursorMoved = ( updates.push({
hanging !== this.screen.cursor.hanging || topic: 'cursor',
cursorX !== this.screen.cursor.x || x,
cursorY !== this.screen.cursor.y) y,
hanging
this.screen.cursor.x = cursorX })
this.screen.cursor.y = cursorY
this.screen.cursor.hanging = !!hanging
if (cursorMoved) {
this.screen.renderer.resetCursorBlink()
this.screen.emit('cursor-moved')
}
this.screen.renderer.scheduleDraw('cursor-moved')
} else if (topic === TOPIC_TITLE) { } else if (topic === TOPIC_TITLE) {
this.screen.emit('TEMP:update-title', collectOneTerminatedString()) updates.push({ topic: 'title', title: collectOneTerminatedString() })
} else if (topic === TOPIC_BUTTONS) { } else if (topic === TOPIC_BUTTONS) {
const count = du(strArray[ci++]) const count = du(strArray[ci++])
@ -196,16 +197,14 @@ module.exports = class ScreenParser {
labels.push(text) labels.push(text)
} }
this.screen.emit('button-labels', labels) updates.push({
topic: 'button-labels',
labels
})
} else if (topic === TOPIC_BACKDROP) { } else if (topic === TOPIC_BACKDROP) {
updates.push({ topic: 'backdrop', image: collectOneTerminatedString() })
text = collectOneTerminatedString()
this.screen.backgroundImage = text
} else if (topic === TOPIC_BELL) { } else if (topic === TOPIC_BELL) {
updates.push({ topic: 'bell' })
this.screen.beep()
} else if (topic === TOPIC_INTERNAL) { } else if (topic === TOPIC_INTERNAL) {
// debug info // debug info
@ -219,7 +218,8 @@ module.exports = class ScreenParser {
const freeHeap = du(strArray[ci++]) const freeHeap = du(strArray[ci++])
const clientCount = du(strArray[ci++]) const clientCount = du(strArray[ci++])
this.screen.emit('internal', { updates.push({
topic: 'internal',
flags, flags,
cursorAttrs, cursorAttrs,
regionStart, regionStart,
@ -232,163 +232,164 @@ module.exports = class ScreenParser {
}) })
} else if (topic === TOPIC_CONTENT) { } else if (topic === TOPIC_CONTENT) {
// set screen content // set screen content
let _ci = ci
const frameY = du(strArray[ci++]) let tempDoNotCommitUpstream = () => {
const frameX = du(strArray[ci++]) let ci = _ci
const frameHeight = du(strArray[ci++]) const frameY = du(strArray[ci++])
const frameWidth = du(strArray[ci++]) const frameX = du(strArray[ci++])
const frameHeight = du(strArray[ci++])
if (this.screen._debug && this.screen.window.debug) { const frameWidth = du(strArray[ci++])
this.screen._debug.pushFrame([frameX, frameY, frameWidth, frameHeight])
}
// content
let fg = 7
let bg = 0
let attrs = 0
let cell = 0 // cell index
let lastChar = ' '
let frameLength = frameWidth * frameHeight
let screenLength = this.screen.window.width * this.screen.window.height
if (resized) {
this.screen.updateSize()
this.screen.blinkingCellCount = 0
this.screen.screen = new Array(screenLength).fill(' ')
this.screen.screenFG = new Array(screenLength).fill(' ')
this.screen.screenBG = new Array(screenLength).fill(' ')
this.screen.screenAttrs = new Array(screenLength).fill(0)
}
const MASK_LINE_ATTR = ATTR_UNDERLINE | ATTR_OVERLINE | ATTR_STRIKE if (this.screen._debug && this.screen.window.debug) {
const MASK_BLINK = ATTR_BLINK this.screen._debug.pushFrame([frameX, frameY, frameWidth, frameHeight])
let pushCell = () => {
// Remove blink attribute if it wouldn't have any effect
let myAttrs = attrs
let hasFG = attrs & ATTR_FG
let hasBG = attrs & ATTR_BG
let cellFG = fg
let cellBG = bg
// use 0,0 if no fg/bg. this is to match back-end implementation
// and allow leaving out fg/bg setting for cells with none
if (!hasFG) cellFG = 0
if (!hasBG) cellBG = 0
if ((myAttrs & MASK_BLINK) !== 0 &&
((lastChar === ' ' && ((myAttrs & MASK_LINE_ATTR) === 0)) || // no line styles
(fg === bg && hasFG && hasBG) // invisible text
)
) {
myAttrs ^= MASK_BLINK
} }
// update blinking cells counter if blink state changed
if ((this.screen.screenAttrs[cell] & MASK_BLINK) !== (myAttrs & MASK_BLINK)) {
if (myAttrs & MASK_BLINK) this.screen.blinkingCellCount++
else this.screen.blinkingCellCount--
}
let cellXInFrame = cell % frameWidth
let cellYInFrame = Math.floor(cell / frameWidth)
let index = (frameY + cellYInFrame) * this.screen.window.width + frameX + cellXInFrame
// 8 dark system colors turn bright when bold // content
if ((myAttrs & ATTR_BOLD) && !(myAttrs & ATTR_FAINT) && hasFG && cellFG < 8) { let fg = 7
cellFG += 8 let bg = 0
let attrs = 0
let cell = 0 // cell index
let lastChar = ' '
let frameLength = frameWidth * frameHeight
const MASK_LINE_ATTR = ATTR_UNDERLINE | ATTR_OVERLINE | ATTR_STRIKE
const MASK_BLINK = ATTR_BLINK
let pushCell = () => {
// Remove blink attribute if it wouldn't have any effect
let myAttrs = attrs
let hasFG = attrs & ATTR_FG
let hasBG = attrs & ATTR_BG
let cellFG = fg
let cellBG = bg
// use 0,0 if no fg/bg. this is to match back-end implementation
// and allow leaving out fg/bg setting for cells with none
if (!hasFG) cellFG = 0
if (!hasBG) cellBG = 0
if ((myAttrs & MASK_BLINK) !== 0 &&
((lastChar === ' ' && ((myAttrs & MASK_LINE_ATTR) === 0)) || // no line styles
(fg === bg && hasFG && hasBG) // invisible text
)
) {
myAttrs ^= MASK_BLINK
}
// update blinking cells counter if blink state changed
if ((this.screen.screenAttrs[cell] & MASK_BLINK) !== (myAttrs & MASK_BLINK)) {
if (myAttrs & MASK_BLINK) this.screen.blinkingCellCount++
else this.screen.blinkingCellCount--
}
let cellXInFrame = cell % frameWidth
let cellYInFrame = Math.floor(cell / frameWidth)
let index = (frameY + cellYInFrame) * this.screen.window.width + frameX + cellXInFrame
// 8 dark system colors turn bright when bold
if ((myAttrs & ATTR_BOLD) && !(myAttrs & ATTR_FAINT) && hasFG && cellFG < 8) {
cellFG += 8
}
this.screen.screen[index] = lastChar
this.screen.screenFG[index] = cellFG
this.screen.screenBG[index] = cellBG
this.screen.screenAttrs[index] = myAttrs
} }
this.screen.screen[index] = lastChar while (ci < strArray.length && cell < frameLength) {
this.screen.screenFG[index] = cellFG let character = strArray[ci++]
this.screen.screenBG[index] = cellBG let charCode = character.codePointAt(0)
this.screen.screenAttrs[index] = myAttrs
} let data, count
switch (charCode) {
while (ci < strArray.length && cell < frameLength) { case SEQ_REPEAT:
let character = strArray[ci++] count = du(strArray[ci++])
let charCode = character.codePointAt(0) for (let j = 0; j < count; j++) {
pushCell()
let data, count if (++cell > frameLength) break
switch (charCode) { }
case SEQ_REPEAT: break
count = du(strArray[ci++])
for (let j = 0; j < count; j++) { case SEQ_SKIP:
cell += du(strArray[ci++])
break
case SEQ_SET_COLORS:
data = du(strArray[ci++])
fg = data & 0xFF
bg = (data >> 8) & 0xFF
break
case SEQ_SET_ATTRS:
data = du(strArray[ci++])
attrs = data & 0xFFFF
break
case SEQ_SET_ATTR_0:
attrs = 0
break
case SEQ_SET_FG:
data = du(strArray[ci++])
if (data & 0x10000) {
data &= 0xFFF
data |= (du(strArray[ci++]) & 0xFFF) << 12
data += 256
}
fg = data
break
case SEQ_SET_BG:
data = du(strArray[ci++])
if (data & 0x10000) {
data &= 0xFFF
data |= (du(strArray[ci++]) & 0xFFF) << 12
data += 256
}
bg = data
break
default:
if (charCode < 32) character = '\ufffd'
lastChar = character
pushCell() pushCell()
if (++cell > frameLength) break cell++
} }
break
case SEQ_SKIP:
cell += du(strArray[ci++])
break
case SEQ_SET_COLORS:
data = du(strArray[ci++])
fg = data & 0xFF
bg = (data >> 8) & 0xFF
break
case SEQ_SET_ATTRS:
data = du(strArray[ci++])
attrs = data & 0xFFFF
break
case SEQ_SET_ATTR_0:
attrs = 0
break
case SEQ_SET_FG:
data = du(strArray[ci++])
if (data & 0x10000) {
data &= 0xFFF
data |= (du(strArray[ci++]) & 0xFFF) << 12
data += 256
}
fg = data
break
case SEQ_SET_BG:
data = du(strArray[ci++])
if (data & 0x10000) {
data &= 0xFFF
data |= (du(strArray[ci++]) & 0xFFF) << 12
data += 256
}
bg = data
break
default:
if (charCode < 32) character = '\ufffd'
lastChar = character
pushCell()
cell++
} }
}
if (this.screen.window.debug) console.log(`Blinky cells: ${this.screen.blinkingCellCount}`) if (this.screen.window.debug) console.log(`Blinky cells: ${this.screen.blinkingCellCount}`)
this.screen.renderer.scheduleDraw('load', 16) this.screen.renderer.scheduleDraw('load', 16)
this.screen.conn.emit('load') this.screen.conn.emit('load')
}
updates.push({
topic: 'content',
tempDoNotCommitUpstream
})
} }
if ((topics & 0x3B) !== 0) this.hideLoadFailedMsg() if ((topics & 0x3B) !== 0) this.hideLoadFailedMsg()
} }
return updates
} }
/** /**
* Loads a message from the server, and optionally a theme. * Parses a message from the server
* @param {string} str - the message * @param {string} message - the message
*/ */
load (str) { parse (message) {
const content = str.substr(1) const content = message.substr(1)
const updates = []
// This is a good place for debugging the message // This is a good place for debugging the message
// console.log(str) // console.log(message)
switch (str[0]) { switch (message[0]) {
case 'U': case 'U':
this.loadUpdate(content) updates.push(...this.parseUpdate(content))
break break
case 'G': case 'G':
@ -396,7 +397,9 @@ module.exports = class ScreenParser {
break break
default: default:
console.warn(`Bad data message type; ignoring.\n${JSON.stringify(str)}`) console.warn(`Bad data message type; ignoring.\n${JSON.stringify(message)}`)
} }
return updates
} }
} }

Loading…
Cancel
Save