Cockpit: buttons under the glass, -fit, and player display layout

Button banks
  The exploded diagnostic view was still display-only - it predates the
  button work - so it now builds the same banks as the cockpit, with
  the pod arrangement laid out from the panes measured sizes rather
  than a hardcoded 640/480 grid (the banks make each window bigger than
  its glass, and the bottom row hung off the work area otherwise).

  The map side columns were spread height/6 from the top, but the maps
  own legend grid is not sixths: measured off the bitmap it starts 13
  rows down with six 102-tall cells on a 105 pitch. Every button sat
  high of its label, worst at the bottom. Each buttons top and bottom
  now come off that grid separately and are subtracted - scaling a
  height directly would let rounding drift them back out of step on a
  resized cockpit.

  Depth 100 to 240: against the 480 glass the two banks meet in the
  middle bar the strips, so practically the whole display is a press
  target. This mattered most in the cockpit, where the panes are small
  enough that the halfway clamp governs - at 100 the MFDs had a 110px
  dead band straight through the middle of the glass.

-fit (also spelled -windowed-fullscreen)
  Borderless over the whole monitor, with the render size chosen to
  match. The cockpit presents the 3D into a viewscreen that fills its
  canvas, so the right -res is that canvas at the scale the cockpit
  will settle on; computing it with identical arithmetic makes the
  stretch a copy. On the 3440x1440 panel that is 133% and -res 2553
  1436, against 125% for the windowed path that pays for the taskbar.

  The pick runs after the whole command line, so an explicit -res wins
  from either side of -fit. Capped at 3840x2160. Cockpit mode only -
  mode 0 has to stay playable on real pod hardware and mode 2 is a dev
  view - so those get the resolution and keep their windows.

Display layout, in environ.ini
  L4MFDSCALE sizes all five MFDs, L4MFDSCALE_UL and friends override
  any one of them, L4RADARSCALE the radar, and L4RADARPOS puts the
  radar bottom centre, in either bottom corner, or halfway up either
  side. Scaling is applied in canvas units before the canvas is fitted
  to the window, so a number means the same thing on every monitor.

  Sizing each display separately let the clamps become exact rather
  than one conservative rule for all five: what limits a display is its
  actual neighbour. Which neighbour that is depends on the radar, so
  the clamps follow it - on the bottom edge it clears the one MFD above
  its column, but centred on a side it has one above AND below and
  grows from the middle both ways, so it must clear the taller twice
  over. Clamping shrinks uniformly; these are photographs of real
  instruments and a one-axis clamp would squash them.

Verified on the ultrawide: all five radar positions, per-display and
group scaling with the clamps biting, -fit with and without an explicit
-res, and the button geometry measured back off the screen.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Cyd
2026-07-25 14:05:02 -05:00
co-authored by Claude Fable 5
parent 6b43971d2d
commit 8dc6605a07
5 changed files with 687 additions and 68 deletions
+490 -58
View File
@@ -3844,6 +3844,323 @@ static LRESULT CALLBACK
SVGA16 *SVGA16::activeCockpit = NULL;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// The six secondary displays at their pod sizes, in canvas units.
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
static const int cockpitMfdBaseW = 320;
static const int cockpitMfdBaseH = 240;
// the radar reads best big: 1.35x the compact glass
// (1.5x proved a touch too large in playtest)
static const int cockpitRadarBaseW = 324;
static const int cockpitRadarBaseH = 432;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// A percentage from the environment, falling back to the given default
// when the variable is unset, unreadable or nonsense. That fallback is
// how a per-display setting inherits the group one.
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
static int
DisplayScalePercent(const char *variable, int fallback)
{
const char *text = getenv(variable);
if (text == NULL)
{
return fallback;
}
int percent = atoi(text);
if (percent <= 0)
{
return fallback;
}
if (percent < 25) percent = 25;
if (percent > 200) percent = 200;
return percent;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Shrink a display to fit a limit, uniformly. Clamping one axis alone
// would stretch the glass out of shape, and these are photographs of
// real instruments - they have to keep their proportions.
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
static void
ClampGlass(SVGA16::GlassSize *glass, int max_w, int max_h)
{
if (max_w < 1) max_w = 1;
if (max_h < 1) max_h = 1;
if (glass->w > max_w)
{
glass->h = (glass->h * max_w) / glass->w;
glass->w = max_w;
}
if (glass->h > max_h)
{
glass->w = (glass->w * max_h) / glass->h;
glass->h = max_h;
}
if (glass->w < 1) glass->w = 1;
if (glass->h < 1) glass->h = 1;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Where the radar sits. The pod had it dead centre under the
// viewscreen, which on a wide panel is exactly where the road is, so
// L4RADARPOS moves it out of the way: either bottom corner, or halfway
// up either side.
//
// On the bottom row it is one of three panes and the lower MFD whose
// corner it takes slides inboard beside it. Halfway up a side it leaves
// the bottom row altogether and sits between that side's two MFDs.
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
enum RadarPlacement
{
RadarBottomCenter = 0,
RadarBottomLeft,
RadarBottomRight,
RadarMidLeft,
RadarMidRight
};
static Logical
RadarOnASide(RadarPlacement placement)
{
return (placement == RadarMidLeft || placement == RadarMidRight)
? True : False;
}
static Logical
RadarOnTheLeft(RadarPlacement placement)
{
return (placement == RadarBottomLeft || placement == RadarMidLeft)
? True : False;
}
static RadarPlacement
RadarPosition()
{
static int cached = -1;
if (cached < 0)
{
//---------------------------------------------------------------
// Two spellings each: the short ones were here first, the long
// ones say which edge out loud now that there are five.
//---------------------------------------------------------------
static const struct
{
const char *name;
int placement;
}
names[] =
{
{ "CENTER", RadarBottomCenter },
{ "CENTRE", RadarBottomCenter },
{ "BOTTOM", RadarBottomCenter },
{ "LEFT", RadarBottomLeft },
{ "BOTTOMLEFT", RadarBottomLeft },
{ "RIGHT", RadarBottomRight },
{ "BOTTOMRIGHT", RadarBottomRight },
{ "MIDLEFT", RadarMidLeft },
{ "LEFTCENTER", RadarMidLeft },
{ "LEFTCENTRE", RadarMidLeft },
{ "MIDRIGHT", RadarMidRight },
{ "RIGHTCENTER", RadarMidRight },
{ "RIGHTCENTRE", RadarMidRight }
};
cached = RadarBottomCenter;
const char *text = getenv("L4RADARPOS");
if (text != NULL)
{
for (int i = 0; i < (int) (sizeof(names) / sizeof(names[0])); ++i)
{
if (!stricmp(text, names[i].name))
{
cached = names[i].placement;
break;
}
}
}
static const char *described[] =
{
"bottom centre",
"bottom left",
"bottom right",
"left side, centred",
"right side, centred"
};
DEBUG_STREAM << "SVGA16: radar on the " << described[cached]
<< "\n" << std::flush;
}
return (RadarPlacement) cached;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// The six secondary displays sized for a given canvas scale.
//
// The pod bolted these down at fixed sizes; a desktop panel has room to
// trade viewscreen for instrument, so the player scales them. Each has
// its own setting, falling back to the group one - L4MFDSCALE sets all
// five MFDs, L4MFDSCALE_UL and friends override individually, and
// L4RADARSCALE the radar.
//
// Scaling happens in canvas units, before the canvas is fitted to the
// window, so a given number means the same thing on every monitor.
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void
SVGA16::CockpitGlassSizes(int scale, GlassSize sizes[SplitViewCount])
{
static Logical settings_read = False;
static int percent[SplitViewCount];
if (!settings_read)
{
settings_read = True;
int group = DisplayScalePercent("L4MFDSCALE", 100);
percent[SplitMFDUpperLeft] =
DisplayScalePercent("L4MFDSCALE_UL", group);
percent[SplitMFDUpperCenter] =
DisplayScalePercent("L4MFDSCALE_UC", group);
percent[SplitMFDUpperRight] =
DisplayScalePercent("L4MFDSCALE_UR", group);
percent[SplitMFDLowerLeft] =
DisplayScalePercent("L4MFDSCALE_LL", group);
percent[SplitMFDLowerRight] =
DisplayScalePercent("L4MFDSCALE_LR", group);
percent[SplitMap] = DisplayScalePercent("L4RADARSCALE", 100);
DEBUG_STREAM << "SVGA16: secondary displays at UL "
<< percent[SplitMFDUpperLeft] << "% UC "
<< percent[SplitMFDUpperCenter] << "% UR "
<< percent[SplitMFDUpperRight] << "% LL "
<< percent[SplitMFDLowerLeft] << "% LR "
<< percent[SplitMFDLowerRight] << "% radar "
<< percent[SplitMap] << "%\n" << std::flush;
}
GlassSize glass[SplitViewCount];
for (int view = 0; view < SplitViewCount; ++view)
{
int base_w = (view == SplitMap) ? cockpitRadarBaseW : cockpitMfdBaseW;
int base_h = (view == SplitMap) ? cockpitRadarBaseH : cockpitMfdBaseH;
glass[view].w = (base_w * percent[view]) / 100;
glass[view].h = (base_h * percent[view]) / 100;
}
//-------------------------------------------------------------------
// Keep the pod arrangement legal in canvas units. Sizing each
// display on its own means these limits can be exact rather than one
// conservative rule covering all five: what constrains a display is
// its actual neighbour, not the largest a neighbour might have been.
//
// The panes overlap the viewscreen by design, as the pod's bezels
// did - each other, never.
//-------------------------------------------------------------------
const int canvas_w = 1920;
const int canvas_h = 1080;
const int half_w = canvas_w / 2;
// nothing may exceed the canvas on its own
for (int view = 0; view < SplitViewCount; ++view)
{
ClampGlass(&glass[view], canvas_w, canvas_h);
}
//-------------------------------------------------------------------
// The radar shares its column with the MFDs above and below it, so
// their heights share the canvas. Which MFDs those are, and how much
// room they leave, depends on where the radar was put. It yields,
// being the one that grows into the view.
//-------------------------------------------------------------------
RadarPlacement placement = RadarPosition();
if (RadarOnASide(placement))
{
//---------------------------------------------------------------
// Halfway up a side it has an MFD above AND below, and it grows
// from the middle in both directions - so it must clear the
// taller of the two twice over. That is a real constraint: two
// big MFDs on one side leave a centred radar very little.
//---------------------------------------------------------------
Logical left = RadarOnTheLeft(placement);
int upper = left
? glass[SplitMFDUpperLeft].h : glass[SplitMFDUpperRight].h;
int lower = left
? glass[SplitMFDLowerLeft].h : glass[SplitMFDLowerRight].h;
int tallest = (upper > lower) ? upper : lower;
ClampGlass(&glass[SplitMap], canvas_w, canvas_h - 2 * tallest);
}
else
{
//---------------------------------------------------------------
// On the bottom edge it only has to clear the MFD hanging from
// the top of its column.
//---------------------------------------------------------------
int above =
(placement == RadarBottomLeft) ? glass[SplitMFDUpperLeft].h :
(placement == RadarBottomRight) ? glass[SplitMFDUpperRight].h :
glass[SplitMFDUpperCenter].h;
ClampGlass(&glass[SplitMap], canvas_w, canvas_h - above);
}
//-------------------------------------------------------------------
// The top row is a centred display with one anchored to either edge,
// so a side display gets whatever the centre one leaves of its half.
//-------------------------------------------------------------------
int top_side = half_w - glass[SplitMFDUpperCenter].w / 2;
ClampGlass(&glass[SplitMFDUpperLeft], top_side, canvas_h);
ClampGlass(&glass[SplitMFDUpperRight], top_side, canvas_h);
//-------------------------------------------------------------------
// What the bottom row looks like depends on the radar as well:
// centred it is the middle of that row, in a corner it is a third
// pane along it, and on a side it has left the row entirely and the
// two MFDs have the whole bottom edge to share.
//-------------------------------------------------------------------
if (RadarOnASide(placement))
{
ClampGlass(&glass[SplitMFDLowerLeft], half_w, canvas_h);
ClampGlass(&glass[SplitMFDLowerRight], half_w, canvas_h);
}
else if (placement == RadarBottomCenter)
{
int bottom_side = half_w - glass[SplitMap].w / 2;
ClampGlass(&glass[SplitMFDLowerLeft], bottom_side, canvas_h);
ClampGlass(&glass[SplitMFDLowerRight], bottom_side, canvas_h);
}
else
{
int bottom_share = (canvas_w - glass[SplitMap].w) / 2;
ClampGlass(&glass[SplitMFDLowerLeft], bottom_share, canvas_h);
ClampGlass(&glass[SplitMFDLowerRight], bottom_share, canvas_h);
}
//-------------------------------------------------------------------
// An upper and a lower MFD share a column, anchored to opposite
// edges, so their heights must not sum past the canvas. The lower
// one yields - it is the one the radar sits beside.
//-------------------------------------------------------------------
ClampGlass(&glass[SplitMFDLowerLeft], canvas_w,
canvas_h - glass[SplitMFDUpperLeft].h);
ClampGlass(&glass[SplitMFDLowerRight], canvas_w,
canvas_h - glass[SplitMFDUpperRight].h);
//-------------------------------------------------------------------
// Only now to device pixels: clamping in canvas units keeps every
// limit above independent of the monitor.
//-------------------------------------------------------------------
for (int view = 0; view < SplitViewCount; ++view)
{
sizes[view].w = (glass[view].w * scale) / 100;
sizes[view].h = (glass[view].h * scale) / 100;
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Fit the 1920x1080 cockpit canvas into the given client area: one
// uniform scale (so the canvas never stretches - a wider desktop just
@@ -3886,11 +4203,8 @@ void
SWP_NOZORDER | SWP_NOACTIVATE);
}
int top_glass_w = (320 * scale) / 100;
int top_glass_h = (240 * scale) / 100;
// the radar reads best big: 1.35x the compact glass
int map_glass_w = (324 * scale) / 100;
int map_glass_h = (432 * scale) / 100;
GlassSize glass[SplitViewCount];
CockpitGlassSizes(scale, glass);
for (int view = 0; view < SplitViewCount; ++view)
{
@@ -3898,14 +4212,7 @@ void
{
continue;
}
if (view == SplitMap)
{
splitView[view]->Resize(map_glass_w, map_glass_h);
}
else
{
splitView[view]->Resize(top_glass_w, top_glass_h);
}
splitView[view]->Resize(glass[view].w, glass[view].h);
}
//---------------------------------------------------------------
@@ -3928,24 +4235,67 @@ void
origin_x + view_w - splitView[SplitMFDUpperRight]->ClientWidth(),
origin_y);
}
//---------------------------------------------------------------
// The radar: bottom centre, either bottom corner, or halfway up
// either side. Only when it takes a bottom corner does a lower MFD
// have to move - on a side it is out of that row's way already.
//---------------------------------------------------------------
RadarPlacement placement = RadarPosition();
int radar_w = (splitView[SplitMap] != NULL)
? splitView[SplitMap]->ClientWidth() : 0;
if (splitView[SplitMap] != NULL)
{
int radar_h = splitView[SplitMap]->ClientHeight();
int radar_x, radar_y;
switch (placement)
{
case RadarBottomLeft:
radar_x = origin_x;
radar_y = origin_y + view_h - radar_h;
break;
case RadarBottomRight:
radar_x = origin_x + view_w - radar_w;
radar_y = origin_y + view_h - radar_h;
break;
case RadarMidLeft:
radar_x = origin_x;
radar_y = origin_y + (view_h - radar_h) / 2;
break;
case RadarMidRight:
radar_x = origin_x + view_w - radar_w;
radar_y = origin_y + (view_h - radar_h) / 2;
break;
default:
radar_x = origin_x + (view_w - radar_w) / 2;
radar_y = origin_y + view_h - radar_h;
break;
}
splitView[SplitMap]->SetPosition(radar_x, radar_y);
}
int slide_left = (placement == RadarBottomLeft) ? radar_w : 0;
int slide_right = (placement == RadarBottomRight) ? radar_w : 0;
if (splitView[SplitMFDLowerLeft] != NULL)
{
splitView[SplitMFDLowerLeft]->SetPosition(
origin_x,
origin_x + slide_left,
origin_y + view_h - splitView[SplitMFDLowerLeft]->ClientHeight());
}
if (splitView[SplitMFDLowerRight] != NULL)
{
splitView[SplitMFDLowerRight]->SetPosition(
origin_x + view_w - splitView[SplitMFDLowerRight]->ClientWidth(),
origin_x + view_w - splitView[SplitMFDLowerRight]->ClientWidth()
- slide_right,
origin_y + view_h - splitView[SplitMFDLowerRight]->ClientHeight());
}
if (splitView[SplitMap] != NULL)
{
splitView[SplitMap]->SetPosition(
origin_x + (view_w - splitView[SplitMap]->ClientWidth()) / 2,
origin_y + view_h - splitView[SplitMap]->ClientHeight());
}
// The panes go OVER the main display (pod bezel occlusion): pin
// the viewscreen to the bottom of the sibling z-order so the 3D
@@ -4023,35 +4373,67 @@ SVGA16::SVGA16(
int work_w = work.right - work.left;
int work_h = work.bottom - work.top;
int mid_x = (work_w - init_width) / 2; if (mid_x < 0) mid_x = 0;
int right_x = work_w - init_width; if (right_x < 0) right_x = 0;
int bottom_y = work_h - init_height; if (bottom_y < 0) bottom_y = 0;
//---------------------------------------------------------------
// Each display carries its own button bank here too, at native
// size: the banks reach under the glass with their indicator
// strips clearing the edge, exactly as in the composited
// cockpit - so the buttons can be read and pressed full size.
//---------------------------------------------------------------
splitView[SplitMFDUpperLeft] = new MFDSplitView(
"MFD upper left", init_width, init_height,
init_width, init_height, work.left, work.top,
MFDSplitView::NoButtons, 0, 0, 0);
MFDSplitView::MFDStrips, 0x2F, 0, 0);
splitView[SplitMFDUpperCenter] = new MFDSplitView(
"MFD upper center", init_width, init_height,
init_width, init_height, work.left + mid_x, work.top,
MFDSplitView::NoButtons, 0, 0, 0);
init_width, init_height, work.left, work.top,
MFDSplitView::MFDStrips, 0x27, 0, 0);
splitView[SplitMFDUpperRight] = new MFDSplitView(
"MFD upper right", init_width, init_height,
init_width, init_height, work.left + right_x, work.top,
MFDSplitView::NoButtons, 0, 0, 0);
init_width, init_height, work.left, work.top,
MFDSplitView::MFDStrips, 0x37, 0, 0);
splitView[SplitMFDLowerLeft] = new MFDSplitView(
"MFD lower left", init_width, init_height,
init_width, init_height, work.left, work.top + bottom_y,
MFDSplitView::NoButtons, 0, 0, 0);
init_width, init_height, work.left, work.top,
MFDSplitView::MFDStrips, 0x0F, 0, 0);
splitView[SplitMFDLowerRight] = new MFDSplitView(
"MFD lower right", init_width, init_height,
init_width, init_height, work.left + right_x, work.top + bottom_y,
MFDSplitView::NoButtons, 0, 0, 0);
init_width, init_height, work.left, work.top,
MFDSplitView::MFDStrips, 0x07, 0, 0);
// map is portrait-mounted: source rotated to 480x640
splitView[SplitMap] = new MFDSplitView(
"Map", init_height, init_width,
init_height, init_width, work.left + mid_x, work.top + bottom_y,
MFDSplitView::NoButtons, 0, 0, 0);
init_height, init_width, work.left, work.top,
MFDSplitView::SideColumns, 0x10, 0x18, 0);
//---------------------------------------------------------------
// Lay them out in the pod arrangement from their measured sizes
// (the banks make each window bigger than its glass).
//---------------------------------------------------------------
RECT probe;
probe.left = 0; probe.top = 0; probe.right = 100; probe.bottom = 100;
AdjustWindowRect(&probe,
WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, FALSE);
int chrome_w = (probe.right - probe.left) - 100;
int chrome_h = (probe.bottom - probe.top) - 100;
int mfd_w = splitView[SplitMFDUpperLeft]->ClientWidth() + chrome_w;
int mfd_h = splitView[SplitMFDUpperLeft]->ClientHeight() + chrome_h;
int map_w = splitView[SplitMap]->ClientWidth() + chrome_w;
int map_h = splitView[SplitMap]->ClientHeight() + chrome_h;
int mid_x = (work_w - mfd_w) / 2; if (mid_x < 0) mid_x = 0;
int right_x = work_w - mfd_w; if (right_x < 0) right_x = 0;
int bottom_y = work_h - mfd_h; if (bottom_y < 0) bottom_y = 0;
int map_x = (work_w - map_w) / 2; if (map_x < 0) map_x = 0;
int map_y = work_h - map_h; if (map_y < 0) map_y = 0;
splitView[SplitMFDUpperLeft]->SetPosition(work.left, work.top);
splitView[SplitMFDUpperCenter]->SetPosition(work.left + mid_x, work.top);
splitView[SplitMFDUpperRight]->SetPosition(work.left + right_x, work.top);
splitView[SplitMFDLowerLeft]->SetPosition(work.left, work.top + bottom_y);
splitView[SplitMFDLowerRight]->SetPosition(
work.left + right_x, work.top + bottom_y);
splitView[SplitMap]->SetPosition(work.left + map_x, work.top + map_y);
}
else if (splitViews)
{
@@ -4073,8 +4455,20 @@ SVGA16::SVGA16(
HWND cockpit = ApplicationManager::GetCurrentManager()->GetHWnd();
//---------------------------------------------------------------
// -fit takes the whole monitor rather than the work area, and
// pays no chrome: the shell goes borderless below. Otherwise
// stay inside the work area so the taskbar is still reachable.
//---------------------------------------------------------------
bool fit_display = L4Application::GetFitDisplay();
int chrome_w = 16, chrome_h = 40;
if (cockpit != NULL)
if (fit_display)
{
chrome_w = 0;
chrome_h = 0;
}
else if (cockpit != NULL)
{
RECT outer, inner;
GetWindowRect(cockpit, &outer);
@@ -4085,7 +4479,28 @@ SVGA16::SVGA16(
RECT work;
work.left = 0; work.top = 0; work.right = 1920; work.bottom = 1080;
SystemParametersInfoA(SPI_GETWORKAREA, 0, &work, 0);
if (fit_display)
{
MONITORINFO monitor;
memset(&monitor, 0, sizeof(monitor));
monitor.cbSize = sizeof(monitor);
HMONITOR handle = MonitorFromWindow(
(cockpit != NULL) ? cockpit : GetDesktopWindow(),
MONITOR_DEFAULTTOPRIMARY);
if (GetMonitorInfoA(handle, &monitor))
{
work = monitor.rcMonitor;
}
else
{
work.right = GetSystemMetrics(SM_CXSCREEN);
work.bottom = GetSystemMetrics(SM_CYSCREEN);
}
}
else
{
SystemParametersInfoA(SPI_GETWORKAREA, 0, &work, 0);
}
int avail_w = (work.right - work.left) - chrome_w;
int avail_h = (work.bottom - work.top) - chrome_h;
@@ -4115,12 +4530,35 @@ SVGA16::SVGA16(
//---------------------------------------------------------------
if (cockpit != NULL)
{
SetWindowLongPtrA(cockpit, GWL_STYLE,
GetWindowLongPtrA(cockpit, GWL_STYLE) | WS_CLIPCHILDREN);
LONG_PTR style = GetWindowLongPtrA(cockpit, GWL_STYLE);
SetWindowPos(cockpit, NULL, work.left, work.top,
client_w + chrome_w, client_h + chrome_h,
SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
if (fit_display)
{
//-------------------------------------------------------
// Borderless over the whole monitor. The window is the
// full panel, wider than 16:9 or not - LayoutCockpit
// centres the canvas inside it and the leftover stays
// black, so this letterboxes exactly like a resize.
//-------------------------------------------------------
style &= ~(WS_CAPTION | WS_THICKFRAME | WS_SYSMENU |
WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_BORDER |
WS_DLGFRAME);
style |= WS_POPUP | WS_CLIPCHILDREN;
SetWindowLongPtrA(cockpit, GWL_STYLE, style);
SetWindowPos(cockpit, NULL, work.left, work.top,
work.right - work.left, work.bottom - work.top,
SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
}
else
{
SetWindowLongPtrA(cockpit, GWL_STYLE,
style | WS_CLIPCHILDREN);
SetWindowPos(cockpit, NULL, work.left, work.top,
client_w + chrome_w, client_h + chrome_h,
SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
}
}
//---------------------------------------------------------------
@@ -4161,40 +4599,34 @@ SVGA16::SVGA16(
// 0x37.. / lower left 0x0F.. / lower right 0x07.. and the map
// flanked by Secondary 0x10-0x15 / Screen 0x18-0x1D.
//---------------------------------------------------------------
// all five MFDs at the compact size, corners + top center
int top_glass_w = COCKPIT_CANVAS(320);
int top_glass_h = COCKPIT_CANVAS(240);
int bot_glass_w = COCKPIT_CANVAS(320);
int bot_glass_h = COCKPIT_CANVAS(240);
// the radar reads best big: 1.35x the compact glass
// (1.5x proved a touch too large in playtest)
int map_glass_w = COCKPIT_CANVAS(324);
int map_glass_h = COCKPIT_CANVAS(432);
// each display at whatever the player asked for it
GlassSize glass[SplitViewCount];
CockpitGlassSizes(scale_num, glass);
splitView[SplitMFDUpperLeft] = new MFDSplitView(
"MFD upper left", init_width, init_height,
top_glass_w, top_glass_h, 0, 0,
glass[SplitMFDUpperLeft].w, glass[SplitMFDUpperLeft].h, 0, 0,
MFDSplitView::MFDStrips, 0x2F, 0, cockpit);
splitView[SplitMFDUpperCenter] = new MFDSplitView(
"MFD upper center", init_width, init_height,
top_glass_w, top_glass_h, 0, 0,
glass[SplitMFDUpperCenter].w, glass[SplitMFDUpperCenter].h, 0, 0,
MFDSplitView::MFDStrips, 0x27, 0, cockpit);
splitView[SplitMFDUpperRight] = new MFDSplitView(
"MFD upper right", init_width, init_height,
top_glass_w, top_glass_h, 0, 0,
glass[SplitMFDUpperRight].w, glass[SplitMFDUpperRight].h, 0, 0,
MFDSplitView::MFDStrips, 0x37, 0, cockpit);
splitView[SplitMFDLowerLeft] = new MFDSplitView(
"MFD lower left", init_width, init_height,
bot_glass_w, bot_glass_h, 0, 0,
glass[SplitMFDLowerLeft].w, glass[SplitMFDLowerLeft].h, 0, 0,
MFDSplitView::MFDStrips, 0x0F, 0, cockpit);
splitView[SplitMFDLowerRight] = new MFDSplitView(
"MFD lower right", init_width, init_height,
bot_glass_w, bot_glass_h, 0, 0,
glass[SplitMFDLowerRight].w, glass[SplitMFDLowerRight].h, 0, 0,
MFDSplitView::MFDStrips, 0x07, 0, cockpit);
// map is portrait: source rotated 90 degrees clockwise
splitView[SplitMap] = new MFDSplitView(
"Map", init_height, init_width,
map_glass_w, map_glass_h, 0, 0,
glass[SplitMap].w, glass[SplitMap].h, 0, 0,
MFDSplitView::SideColumns, 0x10, 0x18, cockpit);
//---------------------------------------------------------------