#include "mungal4.h" #pragma hdrstop //########################################################################### // L4PLASMAWIN -- the desktop plasma display (BT_GLASS only; this TU is only // in the build when the gate is on -- see CMakeLists.txt). // Design: L4PLASMAWIN.h. //########################################################################### #include "l4plasmawin.h" #include #include #include static LRESULT CALLBACK PlasmaWndProc(HWND window, UINT message, WPARAM wparam, LPARAM lparam) { switch (message) { case WM_CLOSE: ShowWindow(window, SW_HIDE); // hide only; the renderer owns it return 0; } return DefWindowProcW(window, message, wparam, lparam); } //########################################################################### PlasmaWindow::PlasmaWindow(): Video8BitBuffered(plasmaWidth, plasmaHeight), window(NULL), blitBuffer(NULL) { scale = 4; const char *scale_string = getenv("L4PLASMASCALE"); if (scale_string != NULL) { int v = atoi(scale_string); if (v >= 1 && v <= 16) { scale = v; } } blitBuffer = new unsigned long[plasmaWidth * plasmaHeight]; memset(blitBuffer, 0, plasmaWidth * plasmaHeight * sizeof(unsigned long)); DEBUG_STREAM << "[plasmawin] desktop plasma display up (scale x" << scale << ")\n" << std::flush; } PlasmaWindow::~PlasmaWindow() { if (window != NULL) { DestroyWindow((HWND)window); window = NULL; } delete[] blitBuffer; blitBuffer = NULL; } void PlasmaWindow::EnsureWindow() { if (window != NULL) { return; } WNDCLASSW window_class; memset(&window_class, 0, sizeof(window_class)); window_class.lpfnWndProc = PlasmaWndProc; window_class.hInstance = GetModuleHandleW(NULL); window_class.hCursor = LoadCursorW(NULL, (LPCWSTR)IDC_ARROW); window_class.lpszClassName = L"BTPlasmaWnd"; RegisterClassW(&window_class); RECT frame = { 0, 0, plasmaWidth * scale, plasmaHeight * scale }; DWORD style = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX; AdjustWindowRect(&frame, style, FALSE); // // Bottom-right, TOPMOST -- under the (topmost, right-parked) button // panel, clear of the game window (which would otherwise bury it). // int frame_width = frame.right - frame.left; int frame_height = frame.bottom - frame.top; int plasma_x = GetSystemMetrics(SM_CXSCREEN) - frame_width - 12; int plasma_y = GetSystemMetrics(SM_CYSCREEN) - frame_height - 60; if (plasma_x < 0) plasma_x = 0; if (plasma_y < 0) plasma_y = 0; window = CreateWindowExW( WS_EX_TOPMOST, L"BTPlasmaWnd", L"BattleTech - Plasma", style, plasma_x, plasma_y, frame_width, frame_height, NULL, NULL, GetModuleHandleW(NULL), NULL); if (window == NULL) { DEBUG_STREAM << "[plasmawin] CreateWindow failed\n" << std::flush; return; } ShowWindow((HWND)window, SW_SHOWNOACTIVATE); } // // The per-frame external-display flush (the gauge renderer calls this // where the serial path streams changed lines out the COM port). Blits // the whole 8-bit buffer as orange-on-black. Returns False = done (no // multi-call transfer in progress, unlike the serial path). // Logical PlasmaWindow::Update(Logical force_all) { (void)force_all; EnsureWindow(); if (window == NULL || pixelBuffer == NULL || pixelBuffer->Data.MapPointer == NULL) { return False; } // // 8-bit index -> BGRA: any lit index renders plasma orange, intensity // stepped a little by index so shaded glyphs keep their shape. The // gauge renderer writes the buffer TOP-DOWN (user-verified 2026-07-17: // a bottom-up flip rendered the marquee upside down) -- copy straight. // const Byte *source = pixelBuffer->Data.MapPointer; for (int y = 0; y < plasmaHeight; ++y) { const Byte *line = source + y * plasmaWidth; unsigned long *destination = blitBuffer + y * plasmaWidth; for (int x = 0; x < plasmaWidth; ++x) { Byte index = line[x]; if (index == 0) { destination[x] = 0x00180800; // near-black } else { unsigned long intensity = 160 + (index % 6) * 19; // 160..255 destination[x] = (intensity << 16) | // R ((intensity * 140 / 255) << 8); // G (amber) } } } BITMAPINFO info; memset(&info, 0, sizeof(info)); info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); info.bmiHeader.biWidth = plasmaWidth; info.bmiHeader.biHeight = -plasmaHeight; // top-down info.bmiHeader.biPlanes = 1; info.bmiHeader.biBitCount = 32; info.bmiHeader.biCompression = BI_RGB; HDC dc = GetDC((HWND)window); if (dc != NULL) { RECT client; GetClientRect((HWND)window, &client); StretchDIBits(dc, 0, 0, client.right, client.bottom, 0, 0, plasmaWidth, plasmaHeight, blitBuffer, &info, DIB_RGB_COLORS, SRCCOPY); ReleaseDC((HWND)window, dc); } return False; } void PlasmaWindow::WaitForUpdate() { }