Files
RP412/MUNGA_L4/L4MFDVIEW.h
T
CydandClaude Opus 5 a52fec80a9 mfd_layout.cfg: ,noframe takes a window's title bar off
Append it to any line - "RPL4=240,120,1000,620,noframe" - and that
window comes up with no caption and no border. For the game window that
is a cockpit filling a monitor edge to edge at a rect you chose, which
-fit could only do by taking the whole screen; for an exploded pane it
is a display photographed without chrome.

Per line rather than global, so the shell can go bare while the panes
keep their captions, or the other way round.

The flag is an instruction rather than something measured off the
window, so Save carries it back out - otherwise the first finished drag
would rewrite the file and quietly drop it. Windows are always built
framed and Load only ever strips, so deleting the flag is all it takes
to get the frame back; there is no un-strip path to get wrong.

A bare window's rect IS its client rect, so the client area is what
survives: a window that had a size in the file keeps it as the client,
and a position-only pane keeps whatever client it had. That also makes
the round trip stable - once bare, what Save records is already the
client, so load-save-load does not creep.

WS_SYSMENU stays on. It draws nothing without a caption, but without it
DefWindowProc will not honour Alt+F4, and a window with no title bar and
no way to close it is a trap. Nothing else can be dragged either, hence
the note in the file header and environ.ini: place it first, add the
flag after.

Verified in both views. Cockpit: the same 240,120 1000x620 line with and
without the flag, CAPTION|THICKFRAME and a 984x581 client becoming POPUP
with a 1000x620 one, and a screenshot showing the displays hard against
all four edges where the framed shot had them inside a letterbox. Save
mode with the flag set, nudged with WM_EXITSIZEMOVE, rewrote the line
with ",noframe" intact. Exploded: Map bare at 777,333 with its 500x640
client preserved while the shell beside it kept its caption.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 09:21:54 -05:00

232 lines
7.1 KiB
C++

#pragma once
#include "..\munga\style.h"
//########################################################################
//############################ MFDSplitView ##############################
//########################################################################
//
// One desktop window showing a single cockpit display, used by SVGA16's
// split mode (L4MFDSPLIT=1). The pod hardware packed the five monochrome
// MFDs into the color channels of two video outputs and mounted the map
// display rotated; on a desktop each display gets its own window instead.
//
// Each window also carries its display's physical button bank, exactly
// as mounted in the pod (geometry per vRIO's CockpitLayout):
// - MFDs: a 4x2 cluster - 4 red buttons above the glass, 4 below,
// RIO addresses descending from the cluster anchor (top-left = hi).
// - The map ("secondary screen"): 6 amber buttons down each side -
// Secondary 0x10-0x15 on the left, Screen 0x18-0x1D on the right
// (the remaining column addresses are Tesla relays, not buttons).
// Buttons light from the lamp state the game commands (via PadRIO) and
// press/release RIO units with the mouse.
//
// SVGA16 fills Pixels() (32bpp top-down, sourceWidth x sourceHeight as
// passed - already swapped by the caller for the rotated map) and calls
// Repaint(). Closing a view window just hides it.
//
class MFDSplitView
{
public:
enum ButtonStyle
{
NoButtons = 0,
MFDStrips, // 4 above + 4 below, addresses descend from anchorA
SideColumns // 6 per side; anchorA = left base, anchorB = right base
};
// With a parent, the view is created as a chrome-less child window
// inside the single cockpit window (x/y in parent client coords);
// without one it is its own draggable desktop window.
MFDSplitView(
const char *title,
int source_width,
int source_height,
int display_width,
int display_height,
int x,
int y,
ButtonStyle button_style = NoButtons,
int anchorA = 0,
int anchorB = 0,
void *parent = 0
);
int
ClientWidth() const
{ return clientWidth; }
int
ClientHeight() const
{ return clientHeight; }
// Move the pane (child mode: parent client coords). Panes overlap
// the viewscreen like the pod's bezels, so exact placement happens
// after construction from the measured client sizes.
void
SetPosition(int x, int y);
// Re-scale the glass (cockpit resized): the source buffer is
// unchanged, only the destination size and the button geometry
// derived from it.
void
Resize(int display_width, int display_height);
// Take the pane off screen. Used for the Winners Circle, which wants
// the whole viewscreen: the panes overlap it like the pod's bezels,
// so hiding them uncovers the full canvas underneath.
void
Hide();
// caption and HWND, for the sticky-placement file
const char *
Title() const
{ return paneTitle; }
void *
Window()
{ return window; }
~MFDSplitView();
Logical
TestInstance() const;
unsigned long *
Pixels()
{ return pixels; }
void
Repaint();
// Window-procedure callbacks (public for the registered WndProc)
void
Paint();
void
MouseDown(int x, int y);
void
MouseUp();
protected:
void
LayoutButtons(
ButtonStyle button_style,
int anchorA,
int anchorB,
int display_width,
int display_height,
int *client_width,
int *client_height
);
struct ScreenButton
{
int unit;
int x, y, w, h;
int amber; // 0 = red family, 1 = amber family
};
enum { maxButtons = 20 };
void
*window; // HWND
void
*blitHeader; // BITMAPINFOHEADER for StretchDIBits
unsigned long
*pixels;
int
sourceWidth,
sourceHeight;
int
displayX, displayY,
displayW, displayH;
int
clientWidth, clientHeight;
ScreenButton
buttons[maxButtons];
int
buttonCount;
int
pressedIndex;
// kept so the bank can be rebuilt when the cockpit re-scales
ButtonStyle
buttonStyle;
int
buttonAnchorA,
buttonAnchorB;
// the window caption, and the key this pane is saved under
const char
*paneTitle;
// True for a pane of its own on the desktop (the exploded view); the
// composited cockpit's panes are chrome-less children and cannot be
// dragged, so there is nothing to remember for them.
Logical
ownWindow;
};
//########################################################################
// Sticky window placement.
//
// The game window and, in the exploded view, each display pane are all
// draggable, but their placement is recomputed on every launch - so
// putting a window somewhere useful never survived the menu-race-menu
// loop. RP412MFDLAYOUT remembers it, in mfd_layout.cfg beside
// bindings.txt:
//
// off / 0 / unset computed placement only, no file (default)
// load / restore restore saved placement at startup, never write
// save / adjust restore, then rewrite on each finished drag and
// on teardown - the round trip
//
// One "<title>=x,y,w,h" line per window, plus an optional ",noframe"
// that takes that window's title bar and border off - for a cockpit that
// fills a monitor edge to edge without -fit's all-or-nothing, or an
// exploded pane photographed without chrome. A bare window has nothing
// to drag by, so place it first and add the flag after; Save carries the
// flag back out, since it is an instruction rather than something
// measured off the window. Windows are always built framed, so deleting
// the flag is all it takes to get the frame back.
//
// Whether the size comes back depends on the window, which is why
// Register takes it as a flag:
//
// display panes position only. A pane's size follows its content and
// its button banks, so an old size from a different
// build must not distort it.
// the game window position, and size in the cockpit view. Nothing
// derives that size - the cockpit fits itself to
// whatever client area it is given - so a window sized
// to suit a monitor should come back that way, and
// half-restoring it would be the strange behaviour. In
// the exploded view its size IS the render resolution
// -res asked for, so there only the position returns.
//
// A placement that lands on none of the monitors currently plugged in is
// ignored rather than applied - restoring the game window off screen
// would leave nothing to drag back.
//
// Lives here because the display panes were the first to need it; the
// game window joined later rather than grow a second copy of the same
// file format.
//
// Ported from BT411's BT_GLASS_LAYOUT.
//########################################################################
// Take part in the saved layout. title is the key in the file and must
// outlive the window (the callers pass string literals).
void
RPWindowLayout_Register(void *window, const char *title, Logical with_size);
void
RPWindowLayout_Forget(void *window);
// Apply the saved placement over the computed one. Call once everything
// has been built and placed.
void
RPWindowLayout_Load();
// Write every registered window's placement. No-op unless mode is save.
void
RPWindowLayout_Save();