Un-pack the 7-display cockpit in-engine (L4MFDSPLIT=1)
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>
This commit is contained in:
@@ -0,0 +1,168 @@
|
||||
#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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user