emulator: in-fork plasma display (serial2=plasma) window in the explode layout
Bring the pod's 128x32 plasma readout in-fork, completing the self-contained
glass cockpit -- no vPLASMA process, no pipe.
- serialplasma.{cpp,h}: CSerialPlasma, a serial<n>=plasma backend that parses
the game's ESC P graphics stream (L4PLASMA streams whole changed rows;
receive-only, no replies) into a 128x32 1bpp framebuffer. Transcribed from
vPLASMA's PlasmaProtocol + VPlasmaDevice (graphics path); the factory
text-mode/fonts aren't used by the game and aren't rendered, but their escape
operands are still consumed so the parser can't desync. Seam PLASMA_GetFrame()
for the renderer.
- vpxlog.cpp: draw the framebuffer as an amber-on-black window in the explode
layout, lazily created once a plasma port exists, parked centered under the
lower-left MFD (VPX_PLASMA="x,y,w,h" overrides); WS_EX_NOACTIVATE so it can't
steal DOSBox focus.
- Register SERIAL_TYPE_PLASMA (serialport.h/.cpp, dosbox.cpp -- documented in
the vpx-device README step 3d, since those stock files live in the git-ignored
src tree).
- Confs: serial2 in the _rio confs + deploy templates switches from
namedpipe pipe:vplasma to the in-fork plasma.
Live-validated 2026-07-24. Real pods keep directserial realport:COM2; the
standalone vPLASMA app keeps namedpipe pipe:vplasma.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1728,6 +1728,30 @@ static void pal_draw(HDC dc, int g, int cw, int ch, bool dump) {
|
||||
SwapBuffers(dc);
|
||||
}
|
||||
|
||||
/* ---- in-fork plasma display (serial=plasma) --------------------------------
|
||||
* Render serialplasma's 128x32 1bpp framebuffer (16 bytes/row, MSB=leftmost)
|
||||
* as an amber-on-black window in the explode layout. Seam = PLASMA_GetFrame
|
||||
* (returns false when no serial=plasma port -> no plasma window). */
|
||||
extern bool PLASMA_GetFrame(uint8_t out[512]);
|
||||
|
||||
static void draw_plasma(HDC dc, int cw, int ch, const uint8_t *fb) {
|
||||
static unsigned char img[128 * 32 * 3];
|
||||
for (int y = 0; y < 32; y++)
|
||||
for (int x = 0; x < 128; x++) {
|
||||
int lit = (fb[y * 16 + (x >> 3)] >> (7 - (x & 7))) & 1;
|
||||
unsigned char *d = &img[(y * 128 + x) * 3];
|
||||
if (lit) { d[0] = 255; d[1] = 150; d[2] = 30; } /* amber plasma glow */
|
||||
else { d[0] = 8; d[1] = 4; d[2] = 0; } /* near-black glass */
|
||||
}
|
||||
glViewport(0, 0, cw, ch);
|
||||
glClearColor(0, 0, 0, 1); glClear(GL_COLOR_BUFFER_BIT);
|
||||
glDisable(GL_DEPTH_TEST); glDisable(GL_TEXTURE_2D); glDisable(GL_LIGHTING);
|
||||
glPixelZoom((float)cw / 128.0f, -(float)ch / 32.0f); /* scale + flip y */
|
||||
glRasterPos2f(-1.0f, 1.0f);
|
||||
glDrawPixels(128, 32, GL_RGB, GL_UNSIGNED_BYTE, img);
|
||||
SwapBuffers(dc);
|
||||
}
|
||||
|
||||
/* ---- click-to-press: map a click in a head window to a RIO button ----------
|
||||
* Mirrors the bezel geometry (draw_mfd_bezel / draw_radar_bezel): returns the
|
||||
* RIO address under (mx,my) in client coords, or -1 for a non-button click.
|
||||
@@ -1913,6 +1937,7 @@ static DWORD WINAPI rt_main(LPVOID) {
|
||||
static const int ex_y[10] = { 560, 0,0,0,0, 560, 560, 20, 20, 20 };
|
||||
HWND pwnd[10]; HDC pdc[10]; HGLRC pgl[10];
|
||||
bool phave[10];
|
||||
HWND pl_wnd = NULL; HDC pl_dc = NULL; HGLRC pl_gl = NULL; bool pl_have = false; /* plasma window */
|
||||
int slot = 0; /* debug-grid position counter */
|
||||
for (int g = 0; g < NWIN; g++) {
|
||||
phave[g] = false;
|
||||
@@ -1979,6 +2004,32 @@ static DWORD WINAPI rt_main(LPVOID) {
|
||||
RECT cr; GetClientRect(pwnd[g], &cr);
|
||||
pal_draw(pdc[g], g, cr.right, cr.bottom, dump);
|
||||
}
|
||||
/* in-fork plasma window (explode only): appears once a serial=plasma
|
||||
* port exists; lazily created below the layout, then rendered live. */
|
||||
if (explode) {
|
||||
uint8_t pfb[512];
|
||||
if (PLASMA_GetFrame(pfb)) {
|
||||
if (!pl_have) {
|
||||
int pw = 512, ph = 128, px, py;
|
||||
if (phave[5]) { /* park centered just under the lower-left MFD (g5) */
|
||||
RECT r; GetWindowRect(pwnd[5], &r);
|
||||
px = r.left + ((r.right - r.left) - pw) / 2;
|
||||
py = r.bottom + 6;
|
||||
} else { px = 84; py = 1070; } /* explode-layout fallback */
|
||||
env_rect("VPX_PLASMA", &px, &py, &pw, &ph);
|
||||
pl_have = make_gl_window("Plasma display (128x32)", pw, ph, px, py,
|
||||
false, &pl_wnd, &pl_dc, &pl_gl);
|
||||
if (pl_have) /* don't steal foreground from DOSBox */
|
||||
SetWindowLongPtrA(pl_wnd, GWL_EXSTYLE,
|
||||
GetWindowLongPtrA(pl_wnd, GWL_EXSTYLE) | WS_EX_NOACTIVATE);
|
||||
}
|
||||
if (pl_have) {
|
||||
wglMakeCurrent(pl_dc, pl_gl);
|
||||
RECT cr; GetClientRect(pl_wnd, &cr);
|
||||
draw_plasma(pl_dc, cr.right, cr.bottom, pfb);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* explode mode: once the DOSBox main screen exists, park it
|
||||
* centered under the Division window (one-shot; user can move it
|
||||
* afterwards). */
|
||||
|
||||
Reference in New Issue
Block a user