Cockpit buttons on the split displays, lamp-lit and clickable
Each MFDSplitView window now carries its display's physical button bank, placed as in the pod (addresses per vRIO CockpitLayout): a 4x2 red cluster around each MFD glass (anchors 0x2F/0x27/0x37 upper, 0x0F/0x07 lower, addresses descending row-major) and 6 amber buttons down each side of the map - Secondary 0x10-0x15 left, Screen 0x18-0x1D right; the remaining column addresses are Tesla relays, per the pod wiring, so they get no buttons. Buttons light from the lamp state the game commands: PadRIO grows a static active-instance hook (SetScreenButton/GetLampState); mouse press/release feeds PadRIO's desired-state sampling alongside pad and keyboard, and paint decodes the lamp byte (state1/state2 brightness, solid/slow/med/fast flash animated by tick). With real serial hardware (no PadRIO) the buttons draw dark and inert. Verified: map flank buttons light per the game's preset lamps, aligned with the labels the glass draws at its edges; MFD clusters render 4+4. Roadmap: queued the vRIO Dynamic Lighting RGB-keyboard lamp mirror as a polish-pass item. dist repacked. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -126,6 +126,17 @@ Two new environment options remove the hardware dependency entirely:
|
||||
view; all windows are draggable. This replaces the external
|
||||
BitBlt-mirror launcher wrapper.
|
||||
|
||||
Each split window also carries its display's **physical button bank**
|
||||
(geometry per vRIO's `CockpitLayout`): 4 red buttons above and below
|
||||
each MFD glass (RIO addresses descending from the cluster anchor —
|
||||
upper left 0x2F, upper center 0x27, upper right 0x37, lower left 0x0F,
|
||||
lower right 0x07), and 6 amber buttons down each side of the map
|
||||
(Secondary 0x10–0x15 left, Screen 0x18–0x1D right; the columns' other
|
||||
addresses are Tesla relays, not buttons). They **light from the lamp
|
||||
states the game commands** (dim/bright, flash modes animate) and press
|
||||
the corresponding RIO unit with the mouse — active when `PadRIO` is the
|
||||
control device, dark and inert with real serial hardware.
|
||||
|
||||
Bindings (vRIO's default profile, condensed):
|
||||
|
||||
| Input | Pod control |
|
||||
|
||||
+257
-45
@@ -2,47 +2,83 @@
|
||||
#pragma hdrstop
|
||||
|
||||
#include "l4mfdview.h"
|
||||
#include "l4padrio.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
const char mfdViewClass[] = "RPMFDView";
|
||||
const char mfdViewProp[] = "RPMFDViewBlit";
|
||||
|
||||
struct MFDViewBlit
|
||||
const int buttonGap = 4;
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Button fill colors by lamp brightness (0 off / 1-2 dim / 3
|
||||
// bright), red family for MFD strips, amber for the side columns.
|
||||
//---------------------------------------------------------------
|
||||
COLORREF ButtonFill(int amber, int level)
|
||||
{
|
||||
BITMAPINFOHEADER header;
|
||||
void *pixels;
|
||||
int sourceWidth;
|
||||
int sourceHeight;
|
||||
};
|
||||
if (amber)
|
||||
{
|
||||
if (level >= 3) return RGB(255, 210, 48);
|
||||
if (level >= 1) return RGB(150, 124, 26);
|
||||
return RGB(64, 52, 10);
|
||||
}
|
||||
if (level >= 3) return RGB(255, 72, 44);
|
||||
if (level >= 1) return RGB(150, 44, 28);
|
||||
return RGB(64, 18, 12);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Lamp byte -> current brightness level, animating flash modes.
|
||||
// Low 2 bits: solid/slow/med/fast; bits 2-3: state 1 brightness;
|
||||
// bits 4-5: state 2 brightness (the flash alternate).
|
||||
//---------------------------------------------------------------
|
||||
int LampLevel(int lamp_state)
|
||||
{
|
||||
int mode = lamp_state & 0x03;
|
||||
int level1 = (lamp_state >> 2) & 0x03;
|
||||
int level2 = (lamp_state >> 4) & 0x03;
|
||||
|
||||
if (mode == 0)
|
||||
{
|
||||
return level1;
|
||||
}
|
||||
|
||||
static const int half_period[4] = { 0, 500, 250, 125 };
|
||||
return ((GetTickCount() / half_period[mode]) & 1) ? level2 : level1;
|
||||
}
|
||||
|
||||
LRESULT CALLBACK
|
||||
MFDViewWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
MFDSplitView *view =
|
||||
(MFDSplitView *) GetWindowLongPtrA(hwnd, GWLP_USERDATA);
|
||||
|
||||
switch (message)
|
||||
{
|
||||
case WM_PAINT:
|
||||
if (view != NULL)
|
||||
{
|
||||
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);
|
||||
}
|
||||
view->Paint();
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_LBUTTONDOWN:
|
||||
if (view != NULL)
|
||||
{
|
||||
view->MouseDown((int)(short) LOWORD(lParam), (int)(short) HIWORD(lParam));
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_LBUTTONUP:
|
||||
case WM_CAPTURECHANGED:
|
||||
if (view != NULL)
|
||||
{
|
||||
view->MouseUp();
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_CLOSE:
|
||||
// Part of the cockpit; just hide it.
|
||||
@@ -56,36 +92,47 @@ namespace
|
||||
}
|
||||
}
|
||||
|
||||
//########################################################################
|
||||
//############################ MFDSplitView ##############################
|
||||
//########################################################################
|
||||
|
||||
MFDSplitView::MFDSplitView(
|
||||
const char *title,
|
||||
int source_width,
|
||||
int source_height,
|
||||
int client_width,
|
||||
int client_height,
|
||||
int display_width,
|
||||
int display_height,
|
||||
int x,
|
||||
int y
|
||||
int y,
|
||||
ButtonStyle button_style,
|
||||
int anchorA,
|
||||
int anchorB
|
||||
)
|
||||
{
|
||||
Check_Pointer(this);
|
||||
|
||||
sourceWidth = source_width;
|
||||
sourceHeight = source_height;
|
||||
buttonCount = 0;
|
||||
pressedIndex = -1;
|
||||
|
||||
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;
|
||||
BITMAPINFOHEADER *header = new BITMAPINFOHEADER;
|
||||
memset(header, 0, sizeof(BITMAPINFOHEADER));
|
||||
header->biSize = sizeof(BITMAPINFOHEADER);
|
||||
header->biWidth = source_width;
|
||||
header->biHeight = -source_height; // top-down
|
||||
header->biPlanes = 1;
|
||||
header->biBitCount = 32;
|
||||
header->biCompression = BI_RGB;
|
||||
blitHeader = header;
|
||||
|
||||
int client_width = display_width;
|
||||
int client_height = display_height;
|
||||
LayoutButtons(button_style, anchorA, anchorB,
|
||||
display_width, display_height, &client_width, &client_height);
|
||||
|
||||
HINSTANCE instance = GetModuleHandleA(NULL);
|
||||
|
||||
@@ -126,7 +173,7 @@ MFDSplitView::MFDSplitView(
|
||||
|
||||
if (window != NULL)
|
||||
{
|
||||
SetPropA((HWND) window, mfdViewProp, blitInfo);
|
||||
SetWindowLongPtrA((HWND) window, GWLP_USERDATA, (LONG_PTR) this);
|
||||
ShowWindow((HWND) window, SW_SHOWNOACTIVATE);
|
||||
}
|
||||
else
|
||||
@@ -141,12 +188,12 @@ MFDSplitView::~MFDSplitView()
|
||||
|
||||
if (window != NULL)
|
||||
{
|
||||
RemovePropA((HWND) window, mfdViewProp);
|
||||
SetWindowLongPtrA((HWND) window, GWLP_USERDATA, 0);
|
||||
DestroyWindow((HWND) window);
|
||||
window = NULL;
|
||||
}
|
||||
delete (MFDViewBlit *) blitInfo;
|
||||
blitInfo = NULL;
|
||||
delete (BITMAPINFOHEADER *) blitHeader;
|
||||
blitHeader = NULL;
|
||||
delete [] pixels;
|
||||
pixels = NULL;
|
||||
}
|
||||
@@ -157,6 +204,171 @@ Logical
|
||||
return pixels != NULL;
|
||||
}
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Compute the display rectangle and the button rectangles, growing the
|
||||
// client area to make room for the strips.
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
void
|
||||
MFDSplitView::LayoutButtons(
|
||||
ButtonStyle button_style,
|
||||
int anchorA,
|
||||
int anchorB,
|
||||
int display_width,
|
||||
int display_height,
|
||||
int *client_width,
|
||||
int *client_height
|
||||
)
|
||||
{
|
||||
displayX = 0;
|
||||
displayY = 0;
|
||||
displayW = display_width;
|
||||
displayH = display_height;
|
||||
|
||||
if (button_style == MFDStrips)
|
||||
{
|
||||
//-----------------------------------------------------------
|
||||
// 4 buttons above the glass, 4 below; RIO addresses descend
|
||||
// from the anchor, row-major (vRIO CockpitLayout::Mfd).
|
||||
//-----------------------------------------------------------
|
||||
int strip_h = display_height / 8;
|
||||
if (strip_h < 18) strip_h = 18;
|
||||
if (strip_h > 40) strip_h = 40;
|
||||
|
||||
int button_w = (display_width - 5 * buttonGap) / 4;
|
||||
|
||||
displayY = strip_h + 2 * buttonGap;
|
||||
*client_height = display_height + 2 * (strip_h + 2 * buttonGap);
|
||||
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
int row = i / 4; // 0 = top strip, 1 = bottom strip
|
||||
int col = i % 4;
|
||||
|
||||
ScreenButton *button = &buttons[buttonCount++];
|
||||
button->unit = anchorA - i;
|
||||
button->amber = 0;
|
||||
button->x = buttonGap + col * (button_w + buttonGap);
|
||||
button->y = row ? (displayY + displayH + buttonGap) : buttonGap;
|
||||
button->w = button_w;
|
||||
button->h = strip_h;
|
||||
}
|
||||
}
|
||||
else if (button_style == SideColumns)
|
||||
{
|
||||
//-----------------------------------------------------------
|
||||
// 6 buttons down each side of the glass (the pod wires only
|
||||
// 6 of each column's 8 addresses to buttons; the rest are
|
||||
// Tesla relays). Left = anchorA ascending, right = anchorB.
|
||||
//-----------------------------------------------------------
|
||||
int col_w = display_width / 8;
|
||||
if (col_w < 20) col_w = 20;
|
||||
if (col_w > 40) col_w = 40;
|
||||
|
||||
int button_h = (display_height - 7 * buttonGap) / 6;
|
||||
|
||||
displayX = col_w + 2 * buttonGap;
|
||||
*client_width = display_width + 2 * (col_w + 2 * buttonGap);
|
||||
|
||||
for (int i = 0; i < 6; ++i)
|
||||
{
|
||||
ScreenButton *left = &buttons[buttonCount++];
|
||||
left->unit = anchorA + i;
|
||||
left->amber = 1;
|
||||
left->x = buttonGap;
|
||||
left->y = buttonGap + i * (button_h + buttonGap);
|
||||
left->w = col_w;
|
||||
left->h = button_h;
|
||||
|
||||
ScreenButton *right = &buttons[buttonCount++];
|
||||
right->unit = anchorB + i;
|
||||
right->amber = 1;
|
||||
right->x = displayX + displayW + buttonGap;
|
||||
right->y = left->y;
|
||||
right->w = col_w;
|
||||
right->h = button_h;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MFDSplitView::Paint()
|
||||
{
|
||||
PAINTSTRUCT ps;
|
||||
HDC hdc = BeginPaint((HWND) window, &ps);
|
||||
|
||||
RECT client;
|
||||
GetClientRect((HWND) window, &client);
|
||||
FillRect(hdc, &client, (HBRUSH) GetStockObject(BLACK_BRUSH));
|
||||
|
||||
SetStretchBltMode(hdc, COLORONCOLOR);
|
||||
StretchDIBits(
|
||||
hdc,
|
||||
displayX, displayY, displayW, displayH,
|
||||
0, 0, sourceWidth, sourceHeight,
|
||||
pixels, (const BITMAPINFO *) blitHeader,
|
||||
DIB_RGB_COLORS, SRCCOPY
|
||||
);
|
||||
|
||||
for (int i = 0; i < buttonCount; ++i)
|
||||
{
|
||||
const ScreenButton *button = &buttons[i];
|
||||
|
||||
int level = PadRIO::IsActive()
|
||||
? LampLevel(PadRIO::GetLampState(button->unit))
|
||||
: 0;
|
||||
|
||||
RECT rect;
|
||||
rect.left = button->x;
|
||||
rect.top = button->y;
|
||||
rect.right = button->x + button->w;
|
||||
rect.bottom = button->y + button->h;
|
||||
|
||||
HBRUSH fill = CreateSolidBrush(ButtonFill(button->amber, level));
|
||||
FillRect(hdc, &rect, fill);
|
||||
DeleteObject(fill);
|
||||
|
||||
HBRUSH frame = CreateSolidBrush(
|
||||
(i == pressedIndex) ? RGB(255, 255, 255) : RGB(48, 48, 48));
|
||||
FrameRect(hdc, &rect, frame);
|
||||
DeleteObject(frame);
|
||||
}
|
||||
|
||||
EndPaint((HWND) window, &ps);
|
||||
}
|
||||
|
||||
void
|
||||
MFDSplitView::MouseDown(int x, int y)
|
||||
{
|
||||
for (int i = 0; i < buttonCount; ++i)
|
||||
{
|
||||
const ScreenButton *button = &buttons[i];
|
||||
if (x >= button->x && x < button->x + button->w &&
|
||||
y >= button->y && y < button->y + button->h)
|
||||
{
|
||||
pressedIndex = i;
|
||||
SetCapture((HWND) window);
|
||||
PadRIO::SetScreenButton(button->unit, True);
|
||||
InvalidateRect((HWND) window, NULL, FALSE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MFDSplitView::MouseUp()
|
||||
{
|
||||
if (pressedIndex >= 0)
|
||||
{
|
||||
PadRIO::SetScreenButton(buttons[pressedIndex].unit, False);
|
||||
pressedIndex = -1;
|
||||
if (GetCapture() == (HWND) window)
|
||||
{
|
||||
ReleaseCapture();
|
||||
}
|
||||
InvalidateRect((HWND) window, NULL, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MFDSplitView::Repaint()
|
||||
{
|
||||
|
||||
+63
-4
@@ -11,6 +11,16 @@
|
||||
// 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.
|
||||
@@ -18,14 +28,24 @@
|
||||
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
|
||||
};
|
||||
|
||||
MFDSplitView(
|
||||
const char *title,
|
||||
int source_width,
|
||||
int source_height,
|
||||
int client_width,
|
||||
int client_height,
|
||||
int display_width,
|
||||
int display_height,
|
||||
int x,
|
||||
int y
|
||||
int y,
|
||||
ButtonStyle button_style = NoButtons,
|
||||
int anchorA = 0,
|
||||
int anchorB = 0
|
||||
);
|
||||
|
||||
~MFDSplitView();
|
||||
@@ -40,14 +60,53 @@ public:
|
||||
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
|
||||
*blitInfo; // MFDViewBlit (header + source dims), see .cpp
|
||||
*blitHeader; // BITMAPINFOHEADER for StretchDIBits
|
||||
unsigned long
|
||||
*pixels;
|
||||
int
|
||||
sourceWidth,
|
||||
sourceHeight;
|
||||
|
||||
int
|
||||
displayX, displayY,
|
||||
displayW, displayH;
|
||||
|
||||
ScreenButton
|
||||
buttons[maxButtons];
|
||||
int
|
||||
buttonCount;
|
||||
int
|
||||
pressedIndex;
|
||||
};
|
||||
|
||||
@@ -87,6 +87,27 @@ namespace
|
||||
//############################### PadRIO #################################
|
||||
//########################################################################
|
||||
|
||||
PadRIO *PadRIO::activeInstance = NULL;
|
||||
|
||||
void
|
||||
PadRIO::SetScreenButton(int unit, Logical pressed)
|
||||
{
|
||||
if (activeInstance != NULL && unit >= 0 && unit < buttonUnits)
|
||||
{
|
||||
activeInstance->screenButton[unit] = pressed ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
PadRIO::GetLampState(int unit)
|
||||
{
|
||||
if (activeInstance != NULL && unit >= 0 && unit < lampCount)
|
||||
{
|
||||
return activeInstance->lampState[unit];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
PadRIO::PadRIO()
|
||||
{
|
||||
Check_Pointer(this);
|
||||
@@ -105,6 +126,7 @@ PadRIO::PadRIO()
|
||||
|
||||
memset(buttonDown, 0, sizeof(buttonDown));
|
||||
memset(lampState, 0, sizeof(lampState));
|
||||
memset(screenButton, 0, sizeof(screenButton));
|
||||
|
||||
invertX = False;
|
||||
invertY = False;
|
||||
@@ -125,12 +147,18 @@ PadRIO::PadRIO()
|
||||
MajorRevision = 4;
|
||||
MinorRevision = 2;
|
||||
|
||||
activeInstance = this;
|
||||
|
||||
DEBUG_STREAM << "PadRIO: virtual RIO active (XInput pad + keyboard)\n" << std::flush;
|
||||
}
|
||||
|
||||
PadRIO::~PadRIO()
|
||||
{
|
||||
Check_Pointer(this);
|
||||
if (activeInstance == this)
|
||||
{
|
||||
activeInstance = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
Logical
|
||||
@@ -290,6 +318,13 @@ void
|
||||
desired[keyboardButtonMap[i].rioUnit] = 1;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < buttonUnits; ++i)
|
||||
{
|
||||
if (screenButton[i])
|
||||
{
|
||||
desired[i] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
for (int unit = 0; unit < buttonUnits; ++unit)
|
||||
{
|
||||
|
||||
+21
-1
@@ -50,11 +50,24 @@ public:
|
||||
SetLamp(int lampNumber, int state);
|
||||
|
||||
// Lamp state the game has commanded, indexed by RIO lamp number.
|
||||
// A future on-screen panel reads this to light its buttons.
|
||||
// The on-screen cockpit buttons read this to light themselves.
|
||||
enum { lampCount = 64 };
|
||||
unsigned char
|
||||
lampState[lampCount];
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// On-screen cockpit buttons (MFDSplitView strips) press RIO units
|
||||
// through these; they are no-ops when no PadRIO is active (e.g.
|
||||
// real serial hardware selected).
|
||||
//---------------------------------------------------------------
|
||||
static void
|
||||
SetScreenButton(int unit, Logical pressed);
|
||||
static int
|
||||
GetLampState(int unit);
|
||||
static Logical
|
||||
IsActive()
|
||||
{ return activeInstance != NULL; }
|
||||
|
||||
protected:
|
||||
void
|
||||
PollInputs();
|
||||
@@ -69,6 +82,13 @@ protected:
|
||||
int
|
||||
queueHead, queueTail;
|
||||
|
||||
static PadRIO
|
||||
*activeInstance;
|
||||
|
||||
// pressed state driven by the on-screen cockpit buttons
|
||||
unsigned char
|
||||
screenButton[0x48];
|
||||
|
||||
unsigned long
|
||||
lastPollTick,
|
||||
lastPadCheckTick;
|
||||
|
||||
+28
-8
@@ -3859,30 +3859,50 @@ SVGA16::SVGA16(
|
||||
int frame_h = GetSystemMetrics(SM_CYCAPTION) + 2 * GetSystemMetrics(SM_CYFIXEDFRAME);
|
||||
int frame_w = 2 * GetSystemMetrics(SM_CXFIXEDFRAME);
|
||||
int grid_x = init_width + frame_w;
|
||||
int row2_y = cell_h + frame_h;
|
||||
|
||||
// the MFD button strips grow each window (mirror of
|
||||
// MFDSplitView::LayoutButtons so the grid rows still tile)
|
||||
int strip_h = cell_h / 8;
|
||||
if (strip_h < 18) strip_h = 18;
|
||||
if (strip_h > 40) strip_h = 40;
|
||||
int mfd_extra_h = 2 * (strip_h + 8);
|
||||
int row2_y = cell_h + mfd_extra_h + frame_h;
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Button banks per display (addresses per vRIO CockpitLayout,
|
||||
// bank-to-display placement per the pod cockpit):
|
||||
// upper left MFD 0x2F.. / upper center 0x27.. / upper right
|
||||
// 0x37.. / lower left 0x0F.. / lower right 0x07.. and the map
|
||||
// flanked by Secondary 0x10-0x15 / Screen 0x18-0x1D.
|
||||
//---------------------------------------------------------------
|
||||
splitView[SplitMFDUpperLeft] = new MFDSplitView(
|
||||
"MFD upper left", init_width, init_height,
|
||||
cell_w, cell_h, grid_x, 0);
|
||||
cell_w, cell_h, grid_x, 0,
|
||||
MFDSplitView::MFDStrips, 0x2F);
|
||||
splitView[SplitMFDUpperCenter] = new MFDSplitView(
|
||||
"MFD upper center", init_width, init_height,
|
||||
cell_w, cell_h, grid_x + (cell_w + frame_w), 0);
|
||||
cell_w, cell_h, grid_x + (cell_w + frame_w), 0,
|
||||
MFDSplitView::MFDStrips, 0x27);
|
||||
splitView[SplitMFDUpperRight] = new MFDSplitView(
|
||||
"MFD upper right", init_width, init_height,
|
||||
cell_w, cell_h, grid_x + 2 * (cell_w + frame_w), 0);
|
||||
cell_w, cell_h, grid_x + 2 * (cell_w + frame_w), 0,
|
||||
MFDSplitView::MFDStrips, 0x37);
|
||||
splitView[SplitMFDLowerLeft] = new MFDSplitView(
|
||||
"MFD lower left", init_width, init_height,
|
||||
cell_w, cell_h, grid_x, row2_y);
|
||||
cell_w, cell_h, grid_x, row2_y,
|
||||
MFDSplitView::MFDStrips, 0x0F);
|
||||
splitView[SplitMFDLowerRight] = new MFDSplitView(
|
||||
"MFD lower right", init_width, init_height,
|
||||
cell_w, cell_h, grid_x + 2 * (cell_w + frame_w), row2_y);
|
||||
cell_w, cell_h, grid_x + 2 * (cell_w + frame_w), row2_y,
|
||||
MFDSplitView::MFDStrips, 0x07);
|
||||
// map is portrait: source rotated 90 degrees clockwise
|
||||
splitView[SplitMap] = new MFDSplitView(
|
||||
"Map", init_height, init_width,
|
||||
cell_h, cell_w,
|
||||
grid_x + (cell_w + frame_w) + (cell_w - cell_h) / 2, row2_y);
|
||||
grid_x + (cell_w + frame_w) + (cell_w - cell_h) / 2, row2_y,
|
||||
MFDSplitView::SideColumns, 0x10, 0x18);
|
||||
|
||||
DEBUG_STREAM << "SVGA16: split views active - 5 MFD windows + rotated map (scale "
|
||||
DEBUG_STREAM << "SVGA16: split views active - 5 MFD windows + rotated map, cockpit buttons on (scale "
|
||||
<< scale_percent << "%)\n" << std::flush;
|
||||
}
|
||||
//STUBBED: VIDEO RB 1/15/07
|
||||
|
||||
+14
-4
@@ -56,10 +56,20 @@ so press-feedback works like the real button field.
|
||||
90°-rotated map), rendered CPU-side from the shared gauge canvas — the
|
||||
external BitBlt-mirror wrapper is obsolete. Verified visually (MFD score
|
||||
readout, full tactical map).
|
||||
- Next in A: on-screen RIO panel fed by `PadRIO::lampState[]` (the
|
||||
cockpit-feel layout — vRIO button clusters around the displays); port the
|
||||
full vRIO bindings-file model (deflect/rate/deadzone per axis, rebinding);
|
||||
pilot keypad (numpad → KeyEvent); Steam Input once C starts.
|
||||
- ✅ **Cockpit buttons on the displays** (2026-07-12): each split window
|
||||
carries its physical button bank — 4+4 red buttons per MFD, 6 amber per
|
||||
map side (Secondary/Screen columns; addresses per vRIO `CockpitLayout`,
|
||||
placement per the pod). Mouse presses inject into `PadRIO`; the game's
|
||||
lamp commands light them (verified: PRESET lamps lit on the map flank,
|
||||
aligned with the glass's own edge labels).
|
||||
- Next in A: port the full vRIO bindings-file model (deflect/rate/deadzone
|
||||
per axis, rebinding); pilot keypad (numpad / on-screen 4×4 → KeyEvent);
|
||||
Steam Input once C starts.
|
||||
- Polish pass (queued): **vRIO's Dynamic Lighting RGB-keyboard mirror** —
|
||||
lamp states glow on the physical keyboard's keys (per-key or zone-lit),
|
||||
blinking with the flash modes. Needs package identity for background
|
||||
light control (vRIO's `pkg\Register-vRIO.ps1` pattern; see the vRIO
|
||||
README for the Settings → Dynamic Lighting steps).
|
||||
- vRIO itself stays useful as a dev harness against unmodified builds.
|
||||
|
||||
## Workstream B — In-game sessions (from TeslaConsole)
|
||||
|
||||
@@ -98,6 +98,9 @@ Controls (XInput controller and/or keyboard):
|
||||
The full pod cockpit comes up as separate windows: the main view, five
|
||||
green MFD screens, the portrait map, and the orange plasma glass below
|
||||
the main view. Drag them wherever you like; closing one just hides it.
|
||||
The red buttons around each MFD and the amber buttons flanking the map
|
||||
are the pod's real button banks: click them with the mouse, and they
|
||||
light up as the game commands their lamps.
|
||||
environ.ini options: L4PADFLIP=XY inverts the stick axes, L4MFDSCALE=n
|
||||
sizes the MFD/map windows (percent, default 50), L4PLASMASCALE=n sets
|
||||
the plasma pixel size (default 4), L4PLASMAPOS=x,y places the plasma.
|
||||
|
||||
Reference in New Issue
Block a user