Make Term Conn an EventEmitter, add status screen

cpsdqs/unified-input
cpsdqs 7 years ago
parent b33695e543
commit 92c4c2ff98
Signed by untrusted user: cpsdqs
GPG Key ID: 3F59586BB7448DD1
  1. 2
      js/term.js
  2. 125
      js/term_conn.js
  3. 79
      js/term_screen.js

@ -1,7 +1,7 @@
/** Init the terminal sub-module - called from HTML */
window.termInit = function ({ labels, theme, allFn }) {
const screen = new TermScreen()
const conn = Conn(screen)
const conn = new Conn(screen)
const input = Input(conn)
const termUpload = TermUpl(conn, input, screen)
screen.input = input

@ -1,31 +1,38 @@
/** Handle connections */
window.Conn = function (screen) {
let ws
let heartbeatTout
let pingIv
let xoff = false
let autoXoffTout
let reconTout
let pageShown = false
window.Conn = class TermConnection extends EventEmitter {
constructor (screen) {
super()
this.screen = screen
this.ws = null
this.heartbeatTimeout = null
this.pingInterval = null
this.xoff = false
this.autoXoffTimeout = null
this.reconTimeout = null
this.pageShown = false
}
function onOpen (evt) {
onWSOpen (evt) {
console.log('CONNECTED')
heartbeat()
doSend('i')
this.heartbeat()
this.send('i')
this.emit('open')
}
function onClose (evt) {
onWSClose (evt) {
console.warn('SOCKET CLOSED, code ' + evt.code + '. Reconnecting...')
clearTimeout(reconTout)
reconTout = setTimeout(function () {
init()
}, 2000)
clearTimeout(this.reconTimeout)
this.reconTimeout = setTimeout(() => this.init(), 2000)
// this happens when the buffer gets fucked up via invalid unicode.
// we basically use polling instead of socket then
this.emit('close', evt.code)
}
function onMessage (evt) {
onWSMessage (evt) {
try {
// . = heartbeat
switch (evt.data.charAt(0)) {
@ -35,66 +42,66 @@ window.Conn = function (screen) {
case '-':
// console.log('xoff');
xoff = true
autoXoffTout = setTimeout(function () {
xoff = false
this.xoff = true
this.autoXoffTimeout = setTimeout(() => {
this.xoff = false
}, 250)
break
case '+':
// console.log('xon');
xoff = false
clearTimeout(autoXoffTout)
this.xoff = false
clearTimeout(this.autoXoffTimeout)
break
default:
screen.load(evt.data)
if (!pageShown) {
this.screen.load(evt.data)
if (!this.pageShown) {
showPage()
pageShown = true
this.pageShown = true
}
break
}
heartbeat()
this.heartbeat()
} catch (e) {
console.error(e)
}
}
function canSend () {
return !xoff
canSend () {
return !this.xoff
}
function doSend (message) {
if (_demo) {
if (typeof demoInterface !== 'undefined') {
send (message) {
if (window._demo) {
if (typeof window.demoInterface !== 'undefined') {
demoInterface.input(message)
} else {
console.log(`TX: ${JSON.stringify(message)}`)
}
return true // Simulate success
}
if (xoff) {
if (this.xoff) {
// TODO queue
console.log("Can't send, flood control.")
return false
}
if (!ws) return false // for dry testing
if (ws.readyState !== 1) {
if (!this.ws) return false // for dry testing
if (this.ws.readyState !== 1) {
console.error('Socket not ready')
return false
}
if (typeof message != 'string') {
message = JSON.stringify(message)
}
ws.send(message)
this.ws.send(message)
return true
}
function init () {
init () {
if (window._demo) {
if (typeof demoInterface === 'undefined') {
if (typeof window.demoInterface === 'undefined') {
alert('Demoing non-demo demo!') // this will catch mistakes when deploying to the website
} else {
demoInterface.init(screen)
@ -103,42 +110,40 @@ window.Conn = function (screen) {
return
}
clearTimeout(reconTout)
clearTimeout(heartbeatTout)
clearTimeout(this.reconTimeout)
clearTimeout(this.heartbeatTimeout)
ws = new WebSocket('ws://' + _root + '/term/update.ws')
ws.onopen = onOpen
ws.onclose = onClose
ws.onmessage = onMessage
this.ws = new WebSocket('ws://' + _root + '/term/update.ws')
this.ws.addEventListener('open', (...args) => this.onWSOpen(...args))
this.ws.addEventListener('close', (...args) => this.onWSClose(...args))
this.ws.addEventListener('message', (...args) => this.onWSMessage(...args))
console.log('Opening socket.')
heartbeat()
this.heartbeat()
this.emit('connect')
}
function heartbeat () {
clearTimeout(heartbeatTout)
heartbeatTout = setTimeout(heartbeatFail, 2000)
heartbeat () {
clearTimeout(this.heartbeatTimeout)
this.heartbeatTimeout = setTimeout(() => this.onHeartbeatFail(), 2000)
}
function heartbeatFail () {
onHeartbeatFail () {
console.error('Heartbeat lost, probing server...')
pingIv = setInterval(function () {
clearInterval(this.pingInterval)
this.pingInterval = setInterval(() => {
console.log('> ping')
$.get('http://' + _root + '/system/ping', function (resp, status) {
this.emit('ping')
$.get('http://' + _root + '/system/ping', (resp, status) => {
if (status === 200) {
clearInterval(pingIv)
clearInterval(this.pingInterval)
console.info('Server ready, reloading page...')
this.emit('ping-success')
location.reload()
}
} else this.emit('ping-fail', status)
}, {
timeout: 100
})
}, 1000)
}
return {
ws: null,
init,
send: doSend,
canSend // check flood control
}
}

@ -114,7 +114,8 @@ window.TermScreen = class TermScreen extends EventEmitter {
fitIntoWidth: 0,
fitIntoHeight: 0,
debug: false,
graphics: 0
graphics: 0,
statusScreen: null
}
// scaling caused by fitIntoWidth/fitIntoHeight
@ -879,9 +880,17 @@ window.TermScreen = class TermScreen extends EventEmitter {
height,
devicePixelRatio,
gridScaleX,
gridScaleY
gridScaleY,
statusScreen
} = this.window
if (statusScreen) {
// draw status screen instead
this.drawStatus(statusScreen)
this.startDrawLoop()
return
} else this.stopDrawLoop()
const charSize = this.getCharSize()
const { width: cellWidth, height: cellHeight } = this.getCellSize()
const screenWidth = width * cellWidth
@ -1085,6 +1094,72 @@ window.TermScreen = class TermScreen extends EventEmitter {
if (this.window.debug && this._debug) this._debug.drawEnd()
}
drawStatus (statusScreen) {
const ctx = this.ctx
const {
fontFamily,
width,
height
} = this.window
// reset drawnScreen to force redraw when statusScreen is disabled
this.drawnScreen = []
const cellSize = this.getCellSize()
const screenWidth = width * cellSize.width
const screenHeight = height * cellSize.height
ctx.setTransform(devicePixelRatio, 0, 0, devicePixelRatio, 0, 0)
ctx.clearRect(0, 0, screenWidth, screenHeight)
ctx.font = `40px ${fontFamily}`
ctx.fillStyle = '#fff'
ctx.textAlign = 'center'
ctx.textBaseline = 'middle'
ctx.fillText(statusScreen.title || '', screenWidth / 2, screenHeight / 2 - 20)
if (statusScreen.loading) {
// show loading spinner
ctx.save()
ctx.translate(screenWidth / 2, screenHeight / 2 + 50)
ctx.strokeStyle = '#fff'
ctx.lineWidth = 5
ctx.lineCap = 'round'
let t = Date.now() / 1000
for (let i = 0; i < 12; i++) {
ctx.rotate(Math.PI / 6)
let offset = ((t * 12) - i) % 12
ctx.globalAlpha = Math.max(0.2, 1 - offset / 3)
ctx.beginPath()
ctx.moveTo(0, 15)
ctx.lineTo(0, 30)
ctx.stroke()
}
ctx.restore()
}
}
startDrawLoop () {
if (this._drawTimerThread) return
let threadID = Math.random().toString(36)
this._drawTimerThread = threadID
this.drawTimerLoop(threadID)
}
stopDrawLoop () {
this._drawTimerThread = null
}
drawTimerLoop (threadID) {
if (!threadID || threadID !== this._drawTimerThread) return
requestAnimationFrame(() => this.drawTimerLoop(threadID))
this.draw('draw-loop')
}
/**
* Parses the content of an `S` message and schedules a draw
* @param {string} str - the message content

Loading…
Cancel
Save