|
|
|
const { mk } = require('../utils')
|
|
|
|
|
|
|
|
module.exports = function attachDebugScreen (screen) {
|
|
|
|
const debugCanvas = mk('canvas')
|
|
|
|
const ctx = debugCanvas.getContext('2d')
|
|
|
|
|
|
|
|
debugCanvas.classList.add('debug-canvas')
|
|
|
|
|
|
|
|
let addCanvas = function () {
|
|
|
|
if (!debugCanvas.parentNode) screen.canvas.parentNode.appendChild(debugCanvas)
|
|
|
|
}
|
|
|
|
let removeCanvas = function () {
|
|
|
|
if (debugCanvas.parentNode) debugCanvas.parentNode.removeChild(debugCanvas)
|
|
|
|
}
|
|
|
|
let updateCanvasSize = function () {
|
|
|
|
let { width, height, devicePixelRatio } = screen.window
|
|
|
|
let cellSize = screen.getCellSize()
|
|
|
|
debugCanvas.width = width * cellSize.width * devicePixelRatio
|
|
|
|
debugCanvas.height = height * cellSize.height * devicePixelRatio
|
|
|
|
debugCanvas.style.width = `${width * cellSize.width}px`
|
|
|
|
debugCanvas.style.height = `${height * cellSize.height}px`
|
|
|
|
}
|
|
|
|
|
|
|
|
let startTime, endTime, lastReason
|
|
|
|
let cells = new Map()
|
|
|
|
let clippedRects = []
|
|
|
|
|
|
|
|
let startDrawing
|
|
|
|
|
|
|
|
screen._debug = {
|
|
|
|
drawStart (reason) {
|
|
|
|
lastReason = reason
|
|
|
|
startTime = Date.now()
|
|
|
|
clippedRects = []
|
|
|
|
},
|
|
|
|
drawEnd () {
|
|
|
|
endTime = Date.now()
|
|
|
|
console.log(`Draw: ${lastReason} (${(endTime - startTime)} ms) with fancy graphics: ${screen.window.graphics}`)
|
|
|
|
startDrawing()
|
|
|
|
},
|
|
|
|
setCell (cell, flags) {
|
|
|
|
cells.set(cell, [flags, Date.now()])
|
|
|
|
},
|
|
|
|
clipRect (...args) {
|
|
|
|
clippedRects.push(args)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let clipPattern
|
|
|
|
{
|
|
|
|
let patternCanvas = document.createElement('canvas')
|
|
|
|
patternCanvas.width = patternCanvas.height = 12
|
|
|
|
let pctx = patternCanvas.getContext('2d')
|
|
|
|
pctx.lineWidth = 1
|
|
|
|
pctx.strokeStyle = '#00f'
|
|
|
|
pctx.beginPath()
|
|
|
|
pctx.moveTo(0, 0)
|
|
|
|
pctx.lineTo(0 - 4, 12)
|
|
|
|
pctx.moveTo(4, 0)
|
|
|
|
pctx.lineTo(4 - 4, 12)
|
|
|
|
pctx.moveTo(8, 0)
|
|
|
|
pctx.lineTo(8 - 4, 12)
|
|
|
|
pctx.moveTo(12, 0)
|
|
|
|
pctx.lineTo(12 - 4, 12)
|
|
|
|
pctx.moveTo(16, 0)
|
|
|
|
pctx.lineTo(16 - 4, 12)
|
|
|
|
pctx.stroke()
|
|
|
|
clipPattern = ctx.createPattern(patternCanvas, 'repeat')
|
|
|
|
}
|
|
|
|
|
|
|
|
let isDrawing = false
|
|
|
|
|
|
|
|
let drawLoop = function () {
|
|
|
|
if (isDrawing) window.requestAnimationFrame(drawLoop)
|
|
|
|
|
|
|
|
let { devicePixelRatio, width, height } = screen.window
|
|
|
|
let { width: cellWidth, height: cellHeight } = screen.getCellSize()
|
|
|
|
let screenLength = width * height
|
|
|
|
let now = Date.now()
|
|
|
|
|
|
|
|
ctx.setTransform(devicePixelRatio, 0, 0, devicePixelRatio, 0, 0)
|
|
|
|
ctx.clearRect(0, 0, width * cellWidth, height * cellHeight)
|
|
|
|
|
|
|
|
let activeCells = 0
|
|
|
|
for (let cell = 0; cell < screenLength; cell++) {
|
|
|
|
if (!cells.has(cell) || cells.get(cell)[0] === 0) continue
|
|
|
|
|
|
|
|
let [flags, timestamp] = cells.get(cell)
|
|
|
|
let elapsedTime = (now - timestamp) / 1000
|
|
|
|
|
|
|
|
if (elapsedTime > 1) continue
|
|
|
|
|
|
|
|
activeCells++
|
|
|
|
ctx.globalAlpha = 0.5 * Math.max(0, 1 - elapsedTime)
|
|
|
|
|
|
|
|
let x = cell % width
|
|
|
|
let y = Math.floor(cell / width)
|
|
|
|
|
|
|
|
if (flags & 1) {
|
|
|
|
// redrawn
|
|
|
|
ctx.fillStyle = '#f0f'
|
|
|
|
}
|
|
|
|
if (flags & 2) {
|
|
|
|
// updated
|
|
|
|
ctx.fillStyle = '#0f0'
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx.fillRect(x * cellWidth, y * cellHeight, cellWidth, cellHeight)
|
|
|
|
|
|
|
|
if (flags & 4) {
|
|
|
|
// wide cell
|
|
|
|
ctx.lineWidth = 2
|
|
|
|
ctx.strokeStyle = '#f00'
|
|
|
|
ctx.strokeRect(x * cellWidth, y * cellHeight, cellWidth, cellHeight)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (clippedRects.length) {
|
|
|
|
ctx.globalAlpha = 0.5
|
|
|
|
ctx.beginPath()
|
|
|
|
|
|
|
|
for (let rect of clippedRects) {
|
|
|
|
ctx.rect(...rect)
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx.fillStyle = clipPattern
|
|
|
|
ctx.fill()
|
|
|
|
}
|
|
|
|
|
|
|
|
if (activeCells === 0) {
|
|
|
|
isDrawing = false
|
|
|
|
removeCanvas()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
startDrawing = function () {
|
|
|
|
if (isDrawing) return
|
|
|
|
addCanvas()
|
|
|
|
updateCanvasSize()
|
|
|
|
isDrawing = true
|
|
|
|
drawLoop()
|
|
|
|
}
|
|
|
|
|
|
|
|
// debug toolbar
|
|
|
|
const toolbar = mk('div')
|
|
|
|
toolbar.classList.add('debug-toolbar')
|
|
|
|
let toolbarAttached = false
|
|
|
|
|
|
|
|
const attachToolbar = function () {
|
|
|
|
screen.canvas.parentNode.appendChild(toolbar)
|
|
|
|
}
|
|
|
|
const detachToolbar = function () {
|
|
|
|
toolbar.parentNode.removeChild(toolbar)
|
|
|
|
}
|
|
|
|
|
|
|
|
screen.on('update-window:debug', debug => {
|
|
|
|
if (debug !== toolbarAttached) {
|
|
|
|
toolbarAttached = debug
|
|
|
|
if (debug) attachToolbar()
|
|
|
|
else detachToolbar()
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
screen.on('draw', () => {
|
|
|
|
if (!toolbarAttached) return
|
|
|
|
let cursorCell = screen.cursor.y * screen.window.width + screen.cursor.x
|
|
|
|
let cellFG = screen.screenFG[cursorCell]
|
|
|
|
let cellBG = screen.screenBG[cursorCell]
|
|
|
|
let cellCode = (screen.screen[cursorCell] || '').codePointAt(0)
|
|
|
|
let cellAttrs = screen.screenAttrs[cursorCell]
|
|
|
|
let hexcode = cellCode.toString(16).toUpperCase()
|
|
|
|
if (hexcode.length < 4) hexcode = `0000${hexcode}`.substr(-4)
|
|
|
|
hexcode = `U+${hexcode}`
|
|
|
|
toolbar.textContent = `Cursor cell (${cursorCell}): ${hexcode} FG: ${cellFG} BG: ${cellBG} Attrs: ${cellAttrs.toString(2)}`
|
|
|
|
})
|
|
|
|
}
|