|
|
|
@ -1,11 +1,18 @@ |
|
|
|
<!DOCTYPE html> |
|
|
|
<!DOCTYPE html> |
|
|
|
<html lang="en"> |
|
|
|
<html lang="en"> |
|
|
|
|
|
|
|
|
|
|
|
<head> |
|
|
|
<head> |
|
|
|
<meta charset="UTF-8" /> |
|
|
|
<meta charset="UTF-8" /> |
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
|
|
|
<title>MonoDisplay Test</title> |
|
|
|
<title>MonoDisplay Test</title> |
|
|
|
<style> |
|
|
|
<style> |
|
|
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } |
|
|
|
*, |
|
|
|
|
|
|
|
*::before, |
|
|
|
|
|
|
|
*::after { |
|
|
|
|
|
|
|
box-sizing: border-box; |
|
|
|
|
|
|
|
margin: 0; |
|
|
|
|
|
|
|
padding: 0; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
body { |
|
|
|
body { |
|
|
|
background: #111; |
|
|
|
background: #111; |
|
|
|
@ -49,7 +56,8 @@ |
|
|
|
height: 100%; |
|
|
|
height: 100%; |
|
|
|
display: block; |
|
|
|
display: block; |
|
|
|
image-rendering: pixelated; |
|
|
|
image-rendering: pixelated; |
|
|
|
image-rendering: crisp-edges; /* Firefox */ |
|
|
|
image-rendering: crisp-edges; |
|
|
|
|
|
|
|
/* Firefox */ |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#resolution { |
|
|
|
#resolution { |
|
|
|
@ -80,12 +88,25 @@ |
|
|
|
transition: background 0.1s, color 0.1s; |
|
|
|
transition: background 0.1s, color 0.1s; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#controls button:hover { background: #222; color: #bbb; } |
|
|
|
#controls button:hover { |
|
|
|
#controls button.active { background: #1e3a1e; color: #33ff66; border-color: #33ff66; } |
|
|
|
background: #222; |
|
|
|
|
|
|
|
color: #bbb; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#status { font-size: 11px; color: #444; min-height: 1em; } |
|
|
|
#controls button.active { |
|
|
|
|
|
|
|
background: #1e3a1e; |
|
|
|
|
|
|
|
color: #33ff66; |
|
|
|
|
|
|
|
border-color: #33ff66; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#status { |
|
|
|
|
|
|
|
font-size: 11px; |
|
|
|
|
|
|
|
color: #444; |
|
|
|
|
|
|
|
min-height: 1em; |
|
|
|
|
|
|
|
} |
|
|
|
</style> |
|
|
|
</style> |
|
|
|
</head> |
|
|
|
</head> |
|
|
|
|
|
|
|
|
|
|
|
<body> |
|
|
|
<body> |
|
|
|
|
|
|
|
|
|
|
|
<h1>MonoDisplay — 160 × 80</h1> |
|
|
|
<h1>MonoDisplay — 160 × 80</h1> |
|
|
|
@ -106,120 +127,146 @@ |
|
|
|
<script src="./public/mono-display.js"></script> |
|
|
|
<script src="./public/mono-display.js"></script> |
|
|
|
|
|
|
|
|
|
|
|
<script> |
|
|
|
<script> |
|
|
|
(function() { |
|
|
|
(function () { |
|
|
|
const { MonoDisplayDriver, MonoDisplayFile, ElementType } = window.MonoDisplay; |
|
|
|
const { MonoDisplayDriver, MonoDisplayFile, ElementType } = window.MonoDisplay; |
|
|
|
|
|
|
|
|
|
|
|
const driver = new MonoDisplayDriver("canvas_root", { |
|
|
|
const driver = new MonoDisplayDriver("canvas_root", { |
|
|
|
onColor: "#EC0", |
|
|
|
onColor: "#EC0", |
|
|
|
offColor: "#000", |
|
|
|
offColor: "#000", |
|
|
|
fps: 25, |
|
|
|
fps: 25, |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
const W = 160, H = 80; |
|
|
|
const W = 160, H = 80; |
|
|
|
|
|
|
|
|
|
|
|
const demos = [ |
|
|
|
const demos = [ |
|
|
|
{ |
|
|
|
{ |
|
|
|
label: "Checkerboard (static)", |
|
|
|
label: "Checkerboard (static)", |
|
|
|
make() { |
|
|
|
make() { |
|
|
|
const pixels = new Uint8Array(W * H); |
|
|
|
const pixels = new Uint8Array(W * H); |
|
|
|
for (let y = 0; y < H; y++) |
|
|
|
for (let y = 0; y < H; y++) |
|
|
|
for (let x = 0; x < W; x++) |
|
|
|
for (let x = 0; x < W; x++) |
|
|
|
pixels[y * W + x] = (x + y) % 2; |
|
|
|
pixels[y * W + x] = (x + y) % 2; |
|
|
|
return new MonoDisplayFile({ |
|
|
|
return new MonoDisplayFile({ |
|
|
|
width: W, height: H, |
|
|
|
width: W, height: H, |
|
|
|
elements_always: [{ type: ElementType.Image2D, pixels, width: W, height: H }], |
|
|
|
elements_always: [{ type: ElementType.Image2D, pixels, width: W, height: H }], |
|
|
|
}).toBuffer(); |
|
|
|
}).toBuffer(); |
|
|
|
|
|
|
|
}, |
|
|
|
}, |
|
|
|
}, |
|
|
|
}, |
|
|
|
{ |
|
|
|
{ |
|
|
|
label: "Blink (animation)", |
|
|
|
label: "Blink (animation)", |
|
|
|
make() { |
|
|
|
make() { |
|
|
|
return new MonoDisplayFile({ |
|
|
|
return new MonoDisplayFile({ |
|
|
|
elements_always: { |
|
|
|
elements_always: { |
|
|
|
flags: { drawFront: true, clearBuffer: true }, |
|
|
|
flags: { drawFront: true, clearBuffer: true }, |
|
|
|
elements: [{ |
|
|
|
elements: [{ |
|
|
|
type: ElementType.Animation, |
|
|
|
type: ElementType.Animation, |
|
|
|
width: W, height: H, |
|
|
|
width: W, height: H, |
|
|
|
updateInterval: 12, // every 13 ticks ≈ 500ms per frame at 25fps |
|
|
|
updateInterval: 12, // every 13 ticks ≈ 500ms per frame at 25fps |
|
|
|
frames: [ |
|
|
|
frames: [ |
|
|
|
{ pixels: new Uint8Array(W * H).fill(1) }, |
|
|
|
{ pixels: new Uint8Array(W * H).fill(1) }, |
|
|
|
{ pixels: new Uint8Array(W * H).fill(0) }, |
|
|
|
{ pixels: new Uint8Array(W * H).fill(0) }, |
|
|
|
], |
|
|
|
], |
|
|
|
}], |
|
|
|
}], |
|
|
|
}, |
|
|
|
}, |
|
|
|
}).toBuffer(); |
|
|
|
}).toBuffer(); |
|
|
|
}, |
|
|
|
}, |
|
|
|
}, |
|
|
|
}, |
|
|
|
{ |
|
|
|
{ |
|
|
|
label: "Text (centered)", |
|
|
|
label: "Text (centered)", |
|
|
|
make() { |
|
|
|
make() { |
|
|
|
return new MonoDisplayFile({ |
|
|
|
return new MonoDisplayFile({ |
|
|
|
elements_always: { |
|
|
|
elements_always: { |
|
|
|
flags: { drawFront: true, clearBuffer: true }, |
|
|
|
flags: { drawFront: true, clearBuffer: true }, |
|
|
|
elements: [{ |
|
|
|
elements: [{ |
|
|
|
type: ElementType.ClippedText, |
|
|
|
type: ElementType.ClippedText, |
|
|
|
text: "Hello, World!", |
|
|
|
text: "Hello, World!", |
|
|
|
xOffset: 0, yOffset: 32, |
|
|
|
xOffset: 0, yOffset: 32, |
|
|
|
width: W, height: 16, |
|
|
|
width: W, height: 16, |
|
|
|
}], |
|
|
|
}], |
|
|
|
}, |
|
|
|
}, |
|
|
|
}).toBuffer(); |
|
|
|
}).toBuffer(); |
|
|
|
}, |
|
|
|
}, |
|
|
|
}, |
|
|
|
}, |
|
|
|
{ |
|
|
|
{ |
|
|
|
label: "Scrolltext (left)", |
|
|
|
label: "Scrolltext (left)", |
|
|
|
make() { |
|
|
|
make() { |
|
|
|
return new MonoDisplayFile({ |
|
|
|
return new MonoDisplayFile({ |
|
|
|
elements_always: { |
|
|
|
elements_always: { |
|
|
|
flags: { drawFront: true, clearBuffer: true }, |
|
|
|
flags: { drawFront: true, clearBuffer: true }, |
|
|
|
elements: [{ |
|
|
|
elements: [{ |
|
|
|
type: ElementType.HScrollText, |
|
|
|
type: ElementType.HScrollText, |
|
|
|
text: "MONO DISPLAY — scrolling ticker — 🚀 ", |
|
|
|
text: "MONO DISPLAY — scrolling ticker — 🚀 ", |
|
|
|
xOffset: 0, yOffset: 32, |
|
|
|
xOffset: 0, yOffset: 32, |
|
|
|
width: W, height: 16, |
|
|
|
width: W, height: 16, |
|
|
|
scrollSpeed: 50, |
|
|
|
scrollSpeed: 50, |
|
|
|
flags: { endless: true, invertDirection: false }, |
|
|
|
flags: { endless: true, invertDirection: false }, |
|
|
|
}], |
|
|
|
}], |
|
|
|
}, |
|
|
|
}, |
|
|
|
}).toBuffer(); |
|
|
|
}).toBuffer(); |
|
|
|
}, |
|
|
|
}, |
|
|
|
}, |
|
|
|
}, |
|
|
|
{ |
|
|
|
{ |
|
|
|
label: "Time", |
|
|
|
label: "Time", |
|
|
|
make() { |
|
|
|
make() { |
|
|
|
return new MonoDisplayFile({ |
|
|
|
return new MonoDisplayFile({ |
|
|
|
elements_always: { |
|
|
|
elements_always: { |
|
|
|
flags: { drawFront: true, clearBuffer: true }, |
|
|
|
flags: { drawFront: true, clearBuffer: true }, |
|
|
|
elements: [{ |
|
|
|
elements: [{ |
|
|
|
flags: {}, |
|
|
|
type: ElementType.CurrentTime, |
|
|
|
type: ElementType.CurrentTime, |
|
|
|
xOffset: 0, yOffset: 32, |
|
|
|
xOffset: 0, yOffset: 8, |
|
|
|
width: W, height: 16, |
|
|
|
width: W, height: 16, |
|
|
|
utcOffsetMinutes: 120, |
|
|
|
utcOffsetMinutes: 120, |
|
|
|
}], |
|
|
|
}, { |
|
|
|
}, |
|
|
|
flags: { clock12h: true }, |
|
|
|
}).toBuffer(); |
|
|
|
type: ElementType.CurrentTime, |
|
|
|
|
|
|
|
xOffset: 40, yOffset: 16, |
|
|
|
|
|
|
|
width: W, height: 16, |
|
|
|
|
|
|
|
utcOffsetMinutes: 120, |
|
|
|
|
|
|
|
}, { |
|
|
|
|
|
|
|
flags: { clock12h: true, showHours: true }, |
|
|
|
|
|
|
|
type: ElementType.CurrentTime, |
|
|
|
|
|
|
|
xOffset: 0, yOffset: 24, |
|
|
|
|
|
|
|
width: W, height: 16, |
|
|
|
|
|
|
|
utcOffsetMinutes: 120, |
|
|
|
|
|
|
|
}, { |
|
|
|
|
|
|
|
flags: { clock12h: true, showHours: false }, |
|
|
|
|
|
|
|
type: ElementType.CurrentTime, |
|
|
|
|
|
|
|
xOffset: 40, yOffset: 32, |
|
|
|
|
|
|
|
width: W, height: 16, |
|
|
|
|
|
|
|
utcOffsetMinutes: 120, |
|
|
|
|
|
|
|
}, { |
|
|
|
|
|
|
|
flags: { clock12h: true, showSeconds: true }, |
|
|
|
|
|
|
|
type: ElementType.CurrentTime, |
|
|
|
|
|
|
|
xOffset: 120, yOffset: 32, |
|
|
|
|
|
|
|
width: W, height: 16, |
|
|
|
|
|
|
|
utcOffsetMinutes: 120, |
|
|
|
|
|
|
|
}], |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
}).toBuffer(); |
|
|
|
|
|
|
|
}, |
|
|
|
}, |
|
|
|
}, |
|
|
|
}, |
|
|
|
]; |
|
|
|
]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const controls = document.getElementById("controls"); |
|
|
|
|
|
|
|
let activeBtn = null; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (const demo of demos) { |
|
|
|
|
|
|
|
const btn = document.createElement("button"); |
|
|
|
|
|
|
|
btn.textContent = demo.label; |
|
|
|
|
|
|
|
btn.onclick = () => { |
|
|
|
|
|
|
|
if (activeBtn) activeBtn.classList.remove("active"); |
|
|
|
|
|
|
|
btn.classList.add("active"); |
|
|
|
|
|
|
|
activeBtn = btn; |
|
|
|
|
|
|
|
driver.load(() => Promise.resolve(demo.make())); |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
controls.appendChild(btn); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
controls.firstElementChild.click(); |
|
|
|
const controls = document.getElementById("controls"); |
|
|
|
|
|
|
|
let activeBtn = null; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (const demo of demos) { |
|
|
|
|
|
|
|
const btn = document.createElement("button"); |
|
|
|
|
|
|
|
btn.textContent = demo.label; |
|
|
|
|
|
|
|
btn.onclick = () => { |
|
|
|
|
|
|
|
if (activeBtn) activeBtn.classList.remove("active"); |
|
|
|
|
|
|
|
btn.classList.add("active"); |
|
|
|
|
|
|
|
activeBtn = btn; |
|
|
|
|
|
|
|
driver.load(() => Promise.resolve(demo.make())); |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
controls.appendChild(btn); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
controls.firstElementChild.click(); |
|
|
|
})(); |
|
|
|
})(); |
|
|
|
</script> |
|
|
|
</script> |
|
|
|
|
|
|
|
|
|
|
|
</body> |
|
|
|
</body> |
|
|
|
|
|
|
|
|
|
|
|
</html> |
|
|
|
</html> |