The pod drove five monochrome MFDs from the color channels of two video outputs - SVGA16 packs bit-slices of the shared gauge canvas into R/G/B of gauge window 3 (upper MFDs) and R/G of window 4 (lower MFDs), with the map palettized on the secondary and physically mounted portrait. The desktop reconstruction previously required an external BitBlt-mirror wrapper. With L4MFDSPLIT=1, SVGA16 renders each display into its own window (MFDSplitView, plain GDI) straight from the canvas + port bit-masks: five green-screen MFD windows and the 90CW-rotated Map, tiled in the pod grid to the right of the main view (L4MFDSCALE percent, default 50). The packed D3D windows stay hidden but keep presenting off-screen, leaving the original path untouched. Handles spanning mode (2-window setups). Also: the plasma glass now opens directly below the main view (clamped to the work area; L4PLASMAPOS=x,y overrides) per playtest feedback. Verified: window grid comes up as main + 5 MFDs + Map + plasma with the packed windows hidden; screenshots confirm a green MFD score readout and the portrait tactical map rendering correctly. dist packer and BUILD.md updated; the launcher wrapper is obsolete for split-mode use. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
169 lines
3.5 KiB
C++
169 lines
3.5 KiB
C++
#include "mungal4.h"
|
|
#pragma hdrstop
|
|
|
|
#include "l4mfdview.h"
|
|
|
|
namespace
|
|
{
|
|
const char mfdViewClass[] = "RPMFDView";
|
|
const char mfdViewProp[] = "RPMFDViewBlit";
|
|
|
|
struct MFDViewBlit
|
|
{
|
|
BITMAPINFOHEADER header;
|
|
void *pixels;
|
|
int sourceWidth;
|
|
int sourceHeight;
|
|
};
|
|
|
|
LRESULT CALLBACK
|
|
MFDViewWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (message)
|
|
{
|
|
case WM_PAINT:
|
|
{
|
|
PAINTSTRUCT ps;
|
|
HDC hdc = BeginPaint(hwnd, &ps);
|
|
const MFDViewBlit *blit =
|
|
(const MFDViewBlit *) GetPropA(hwnd, mfdViewProp);
|
|
if (blit != NULL && blit->pixels != NULL)
|
|
{
|
|
RECT client;
|
|
GetClientRect(hwnd, &client);
|
|
SetStretchBltMode(hdc, COLORONCOLOR);
|
|
StretchDIBits(
|
|
hdc,
|
|
0, 0, client.right, client.bottom,
|
|
0, 0, blit->sourceWidth, blit->sourceHeight,
|
|
blit->pixels, (const BITMAPINFO *) &blit->header,
|
|
DIB_RGB_COLORS, SRCCOPY
|
|
);
|
|
}
|
|
EndPaint(hwnd, &ps);
|
|
}
|
|
return 0;
|
|
|
|
case WM_CLOSE:
|
|
// Part of the cockpit; just hide it.
|
|
ShowWindow(hwnd, SW_HIDE);
|
|
return 0;
|
|
|
|
case WM_ERASEBKGND:
|
|
return 1;
|
|
}
|
|
return DefWindowProcA(hwnd, message, wParam, lParam);
|
|
}
|
|
}
|
|
|
|
MFDSplitView::MFDSplitView(
|
|
const char *title,
|
|
int source_width,
|
|
int source_height,
|
|
int client_width,
|
|
int client_height,
|
|
int x,
|
|
int y
|
|
)
|
|
{
|
|
Check_Pointer(this);
|
|
|
|
sourceWidth = source_width;
|
|
sourceHeight = source_height;
|
|
|
|
pixels = new unsigned long[source_width * source_height];
|
|
memset(pixels, 0, source_width * source_height * sizeof(unsigned long));
|
|
|
|
MFDViewBlit *blit = new MFDViewBlit;
|
|
memset(blit, 0, sizeof(MFDViewBlit));
|
|
blit->header.biSize = sizeof(BITMAPINFOHEADER);
|
|
blit->header.biWidth = source_width;
|
|
blit->header.biHeight = -source_height; // top-down
|
|
blit->header.biPlanes = 1;
|
|
blit->header.biBitCount = 32;
|
|
blit->header.biCompression = BI_RGB;
|
|
blit->pixels = pixels;
|
|
blit->sourceWidth = source_width;
|
|
blit->sourceHeight = source_height;
|
|
blitInfo = blit;
|
|
|
|
HINSTANCE instance = GetModuleHandleA(NULL);
|
|
|
|
static Logical class_registered = False;
|
|
if (!class_registered)
|
|
{
|
|
class_registered = True;
|
|
|
|
WNDCLASSA window_class;
|
|
memset(&window_class, 0, sizeof(window_class));
|
|
window_class.style = CS_HREDRAW | CS_VREDRAW;
|
|
window_class.lpfnWndProc = MFDViewWndProc;
|
|
window_class.hInstance = instance;
|
|
window_class.hCursor = LoadCursor(NULL, IDC_ARROW);
|
|
window_class.hbrBackground = (HBRUSH) GetStockObject(BLACK_BRUSH);
|
|
window_class.lpszClassName = mfdViewClass;
|
|
RegisterClassA(&window_class);
|
|
}
|
|
|
|
DWORD style = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
|
|
RECT bounds;
|
|
bounds.left = 0;
|
|
bounds.top = 0;
|
|
bounds.right = client_width;
|
|
bounds.bottom = client_height;
|
|
AdjustWindowRect(&bounds, style, FALSE);
|
|
|
|
window = CreateWindowExA(
|
|
0,
|
|
mfdViewClass,
|
|
title,
|
|
style,
|
|
x, y,
|
|
bounds.right - bounds.left,
|
|
bounds.bottom - bounds.top,
|
|
NULL, NULL, instance, NULL
|
|
);
|
|
|
|
if (window != NULL)
|
|
{
|
|
SetPropA((HWND) window, mfdViewProp, blitInfo);
|
|
ShowWindow((HWND) window, SW_SHOWNOACTIVATE);
|
|
}
|
|
else
|
|
{
|
|
DEBUG_STREAM << "MFDSplitView: window creation failed (" << title << ")\n" << std::flush;
|
|
}
|
|
}
|
|
|
|
MFDSplitView::~MFDSplitView()
|
|
{
|
|
Check_Pointer(this);
|
|
|
|
if (window != NULL)
|
|
{
|
|
RemovePropA((HWND) window, mfdViewProp);
|
|
DestroyWindow((HWND) window);
|
|
window = NULL;
|
|
}
|
|
delete (MFDViewBlit *) blitInfo;
|
|
blitInfo = NULL;
|
|
delete [] pixels;
|
|
pixels = NULL;
|
|
}
|
|
|
|
Logical
|
|
MFDSplitView::TestInstance() const
|
|
{
|
|
return pixels != NULL;
|
|
}
|
|
|
|
void
|
|
MFDSplitView::Repaint()
|
|
{
|
|
Check_Pointer(this);
|
|
if (window != NULL)
|
|
{
|
|
InvalidateRect((HWND) window, NULL, FALSE);
|
|
}
|
|
}
|