Files
RP412/MUNGA_L4/L4APP.cpp
T
CydandClaude Fable 5 8dc6605a07 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>
2026-07-25 14:05:02 -05:00

1060 lines
28 KiB
C++

#include "mungal4.h"
#pragma hdrstop
#include "..\munga\camship.h"
#include "..\munga\appmsg.h"
#include "l4app.h"
#include "l4ctrl.h"
#include "l4icom.h"
#include "l4video.h"
#include "l4audrnd.h"
#include "l4grend.h"
#include "l4net.h"
#include "l4mppr.h"
#include "..\munga\player.h"
#include "..\munga\mission.h"
//
// Minimal stand-in for ATL's <atlconv.h> USES_CONVERSION / W2A macros.
// VC++ Express and the Windows SDK do not ship ATL, and the only ATL feature
// this file used was wide-to-ANSI conversion of command-line tokens below.
// L4WideToAnsi mirrors ATL's W2A semantics: each use produces an independent
// buffer that stays valid until the end of the enclosing full-expression.
//
namespace
{
class L4WideToAnsi
{
public:
explicit L4WideToAnsi(const wchar_t* wide) : mBuffer(0)
{
int needed = (wide != 0)
? ::WideCharToMultiByte(CP_ACP, 0, wide, -1, 0, 0, 0, 0)
: 0;
if (needed <= 0)
{
needed = 1;
}
mBuffer = new char[needed];
if (wide == 0 ||
::WideCharToMultiByte(CP_ACP, 0, wide, -1, mBuffer, needed, 0, 0) <= 0)
{
mBuffer[0] = '\0';
}
}
~L4WideToAnsi() { delete [] mBuffer; }
operator char*() { return mBuffer; }
private:
char* mBuffer;
L4WideToAnsi(const L4WideToAnsi&); // non-copyable
L4WideToAnsi& operator=(const L4WideToAnsi&);
};
}
#define USES_CONVERSION ((void)0)
#define W2A(w) (static_cast<char*>(L4WideToAnsi(w)))
L4Application*&
l4_application = (L4Application*&)application;
const Receiver::HandlerEntry
L4Application::MessageHandlerEntries[]=
{
MESSAGE_ENTRY(L4Application, RunMission),
MESSAGE_ENTRY(L4Application, StopMission),
MESSAGE_ENTRY(L4Application, LightsOut),
MESSAGE_ENTRY(L4Application, KeyCommand)
};
Receiver::MessageHandlerSet& L4Application::GetMessageHandlers()
{
static Receiver::MessageHandlerSet messageHandlers(ELEMENTS(L4Application::MessageHandlerEntries), L4Application::MessageHandlerEntries, Application::GetMessageHandlers());
return messageHandlers;
}
//#############################################################################
// Virtual Data support
//
Derivation* L4Application::GetClassDerivations()
{
static Derivation classDerivations(Application::GetClassDerivations(), "L4Application");
return &classDerivations;
}
L4Application::SharedData
L4Application::DefaultData(
L4Application::GetClassDerivations(),
L4Application::GetMessageHandlers()
);
//##########################################################################
//################## L4Application Static Data #######################
//##########################################################################
HINSTANCE L4Application::mhInstance = NULL;
unsigned int L4Application::mScreenWidth = 800;
unsigned int L4Application::mScreenHeight = 600;
bool L4Application::mFullscreen = true;
bool L4Application::mFitDisplay = false;
bool L4Application::mResExplicit = false;
Logical L4Application::seeSolids = False;
CString L4Application::eggNotationFileName;
CString L4Application::spoolFileName;
unsigned long L4Application::networkCommonFlatAddress;
Logical L4Application::missionReviewMode = False;
CString L4Application::rioPlaybackFileName;
CString L4Application::rioRecordingFileName;
//
//#############################################################################
// ParseCommandLine
//#############################################################################
//
Logical
L4Application::ParseToken(
int *arguement,
int argc,
LPWSTR argv[]
)
{
USES_CONVERSION;
Check_Pointer(argv);
if (!stricmp(W2A(argv[*arguement]), "-egg"))
{
if ((*arguement+1) < argc && W2A(argv[*arguement+1])[0] != '-')
{
eggNotationFileName = W2A(argv[++(*arguement)]);
}
else
{
DEBUG_STREAM << "\nEgg file not specified after -egg\n" << std::flush << std::flush;
return False;
}
}
else if (!stricmp(W2A(argv[*arguement]), "-spool"))
{
if ((*arguement+1) < argc && W2A(argv[*arguement+1])[0] != '-')
{
spoolFileName = W2A(argv[++(*arguement)]);
}
else
{
DEBUG_STREAM << "\nSpool file not specified after -spool\n" << std::flush << std::flush;
return False;
}
}
else if (!stricmp(W2A(argv[*arguement]), "-net"))
{
if ((*arguement+1) < argc && W2A(argv[*arguement+1])[0] != '-')
{
networkCommonFlatAddress = atol(W2A(argv[++(*arguement)]));
}
else
{
DEBUG_STREAM << "\nAddress not specified after -net\n" << std::flush;
return False;
}
}
else if (!stricmp(W2A(argv[*arguement]), "-r"))
{
if ((*arguement+1) < argc && W2A(argv[*arguement+1])[0] != '-')
{
rioRecordingFileName = W2A(argv[++(*arguement)]);
CString playback = GetRIOPlaybackFileName();
if (playback && strlen(playback))
{
DEBUG_STREAM << "\nPlayback already specified!\n" << std::flush;
return False;
}
}
else
{
DEBUG_STREAM << "\nRecording file not specified after -r\n" << std::flush;
return False;
}
}
else if (!stricmp(W2A(argv[*arguement]), "-p"))
{
if ((*arguement+1) < argc && W2A(argv[*arguement+1])[0] != '-')
{
rioPlaybackFileName = W2A(argv[++(*arguement)]);
CString recording = GetRIORecordingFileName();
if (recording && strlen(recording))
{
DEBUG_STREAM << "\nRecording already specified!\n" << std::flush;
return False;
}
}
else
{
DEBUG_STREAM << "\nPlayback file not specified after -egg\n" << std::flush;
return False;
}
}
else if (!stricmp(W2A(argv[*arguement]), "-solids"))
{
seeSolids = True;
}
else if (!stricmp(W2A(argv[*arguement]), "-res"))
{
if ((*arguement+2) < argc && W2A(argv[*arguement+1])[0] != '-' && W2A(argv[*arguement+2])[0] != '-')
{
mScreenWidth = atoi(W2A(argv[++(*arguement)]));
mScreenHeight = atoi(W2A(argv[++(*arguement)]));
mResExplicit = true;
}
else
{
DEBUG_STREAM << "\nScreen width and height not specified after -res\n" << std::flush;
return False;
}
}
else if (!stricmp(W2A(argv[*arguement]), "-windowed"))
{
mFullscreen = false;
}
//-------------------------------------------------------------------
// -fit: borderless window over the whole monitor, and a render size
// picked to match. Spelled out as -windowed-fullscreen too, since
// that is what the rest of the world calls it.
//-------------------------------------------------------------------
else if (
!stricmp(W2A(argv[*arguement]), "-fit") ||
!stricmp(W2A(argv[*arguement]), "-windowed-fullscreen")
)
{
mFullscreen = false;
mFitDisplay = true;
}
else if (
!stricmp(W2A(argv[*arguement]), "-h") || !stricmp(W2A(argv[*arguement]), "-help")
)
{
DEBUG_STREAM << "\n" << argv[0] <<
" -egg <filename> -net <memory_address> -solids -h -help\n"
" -windowed windowed, title bar and all\n"
" -fit borderless over the whole monitor,\n"
" render size chosen to match\n"
" (long form: -windowed-fullscreen)\n"
" -res <width> <height> explicit render size; overrides the\n"
" size -fit would have picked\n";
return False;
}
return True;
}
//
//#############################################################################
// ChooseFitResolution
//#############################################################################
//
// The cockpit presents the 3D into a viewscreen that fills its 1920x1080
// canvas, and that canvas is fitted to the window at one uniform scale.
// So the render size that lands 1:1 on screen is the canvas at the same
// scale the cockpit will choose - work it out with identical arithmetic
// here and the stretch becomes a copy.
//
void
L4Application::ChooseFitResolution()
{
int monitor_w = GetSystemMetrics(SM_CXSCREEN);
int monitor_h = GetSystemMetrics(SM_CYSCREEN);
if (monitor_w <= 0 || monitor_h <= 0)
{
return;
}
const int canvas_w = 1920;
const int canvas_h = 1080;
int fit_w = (monitor_w * 100) / canvas_w;
int fit_h = (monitor_h * 100) / canvas_h;
int scale = (fit_w < fit_h) ? fit_w : fit_h;
if (scale < 25) scale = 25;
unsigned int width = (canvas_w * scale) / 100;
unsigned int height = (canvas_h * scale) / 100;
//-------------------------------------------------------------------
// A 1995 engine on a 5K panel would be asked for a back buffer well
// past anything it was built for; cap and let D3D scale the last bit.
//-------------------------------------------------------------------
if (width > 3840)
{
width = 3840;
height = 2160;
}
mScreenWidth = width;
mScreenHeight = height;
DEBUG_STREAM << "L4Application: -fit chose -res " << width << " "
<< height << " (" << scale << "% canvas) for the "
<< monitor_w << "x" << monitor_h << " monitor\n" << std::flush;
}
//
//#############################################################################
// ParseCommandLine
//#############################################################################
//
Logical
L4Application::ParseCommandLine(
int argc,
LPWSTR argv[],
TokenParser parser
)
{
USES_CONVERSION;
Check_Pointer(argv);
//
// Process options
//
seeSolids = False;
eggNotationFileName = "";
networkCommonFlatAddress = NULL;
missionReviewMode = 0;
suppressGauges = FALSE;
if (argc > 1)
{
for (int i = 1; i < argc; ++i)
{
if (!stricmp(W2A(argv[i]), "-lc"))
{
suppressGauges = TRUE;
} else if (!stricmp(W2A(argv[i]), "-mr"))
{
suppressGauges = TRUE;
missionReviewMode = 1;
} else if (!(*parser)(&i, argc, argv))
{
return False;
}
}
}
// after the whole line, so -res wins from either side of -fit
if (mFitDisplay && !mResExplicit)
{
ChooseFitResolution();
}
return True;
}
//
//#############################################################################
// TestInstance
//#############################################################################
//
Logical
L4Application::TestInstance() const
{
if (!IsDerivedFrom(*GetClassDerivations()))
{
return False;
}
return True;
}
//
//#############################################################################
// L4Application
//#############################################################################
//
L4Application::L4Application(
HINSTANCE hInstance,
HWND hWnd,
ResourceFile *resource_file,
ApplicationID application_ID,
ClassID class_ID,
SharedData &shared_data
):
Application(resource_file, application_ID, class_ID, shared_data),
mIsDead(false)
{
mhInstance = hInstance; // this is a static member, the app instance should never change during execution
//ghWnd = hWnd;
divisionParameters = getenv("DPLARG");
}
//
//#############################################################################
// Initialize
//#############################################################################
//
void
L4Application::Initialize()
{
Check(this);
InitializeTillConsole();
//
// Create the console host and socket
//
L4NetworkManager *net_mgr = GetNetworkManager();
net_mgr->CreateConsoleHost();
}
//
//#############################################################################
// Initialize
//#############################################################################
//
void
L4Application::InitializeTillConsole()
{
Check(this);
Application::Initialize();
}
//
//#############################################################################
// MakeModeManager
//#############################################################################
//
ModeManager*
L4Application::MakeModeManager()
{
return new ModeManager((ModeMask)ModeManager::ModeAlwaysActive); // Normally called at top level!!
}
//
//#############################################################################
// MakeNetworkManager
//#############################################################################
//
NetworkManager*
L4Application::MakeNetworkManager()
{
return new L4NetworkManager;
}
//
//#############################################################################
// MakeControlsManager
//#############################################################################
//
ControlsManager*
L4Application::MakeControlsManager()
{
LBE4ControlsManager *manager = new LBE4ControlsManager();
Check(manager);
manager->keyboardGroup[LBE4ControlsManager::KeyboardPC].Add(ModeManager::ModeAlwaysActive, this, KeyCommandMessageID, this);
return manager;
}
//
//#############################################################################
// MakeIntercomManager
//#############################################################################
//
IcomManager*
L4Application::MakeIntercomManager()
{
L4IcomManager
*manager = new L4IcomManager();
Check(manager);
return manager;
}
//
//#############################################################################
// MakeVideoRenderer
//#############################################################################
//
VideoRenderer*
L4Application::MakeVideoRenderer()
{
if (divisionParameters)
{
return
new DPLRenderer(
ghWnd,
mScreenWidth,
mScreenHeight,
mFullscreen,
VisualInterestType,
DefaultInterestDepth
);
}
return NULL;
}
//
//#############################################################################
// MakeAudioRenderer
//#############################################################################
//
AudioRenderer*
L4Application::MakeAudioRenderer()
{
/* char *blaster1, *blaster2;
blaster1 = getenv(FRONT_CARD_ENV_VAR);
blaster2 = getenv(REAR_CARD_ENV_VAR);
if (blaster1 != NULL && blaster2 != NULL)
{
return new L4AudioRenderer(DefaultRendererRate, False);
}
return NULL;*/
return new L4AudioRenderer(DefaultRendererRate, False);
}
//
//#############################################################################
// MakeGaugeRenderer
//#############################################################################
//
GaugeRenderer*
L4Application::MakeGaugeRenderer(int *secondaryIndex, int *aux1Index, int *aux2Index)
{
char *mode_string = getenv("L4GAUGE");
if (mode_string != NULL)
{
return new L4GaugeRenderer(!mFullscreen, secondaryIndex, aux1Index, aux2Index);
}
return NULL;
}
//
//#############################################################################
// Terminate
//#############################################################################
//
void
L4Application::Terminate()
{
Check(this);
//
//--------------------------------------------------------------------------
// Turn off illumination
//--------------------------------------------------------------------------
//
PilotIllumination(False);
//
// Call inherited method
//
Application::Terminate();
}
//
//#############################################################################
// ~L4Application
//#############################################################################
//
L4Application::~L4Application()
{
}
//
//#############################################################################
// MakeAndLinkViewpointEntity
//#############################################################################
//
Entity*
L4Application::MakeAndLinkViewpointEntity(Entity::MakeMessage* message)
{
Check(this);
Check(message);
Application::MakeAndLinkViewpointEntity(message);
//
//--------------------------------------------------------------------------
// Turn on lights for pilot
// (turned off by RunMissionMessageHandler)
//--------------------------------------------------------------------------
//
PilotIllumination(True);
//
//--------------------------------------------------------------------------
// Return viewpoint entity
//--------------------------------------------------------------------------
//
return GetViewpointEntity();
}
//
//#############################################################################
// MakeViewpointEntity
//#############################################################################
//
Entity*
L4Application::MakeViewpointEntity(Entity::MakeMessage *message)
{
Entity *viewing_entity;
viewing_entity = NULL;
switch(message->classToCreate)
{
case CameraShipClassID:
viewing_entity = CameraShip::Make((CameraShip::MakeMessage*)message);
Check(viewing_entity);
CameraShip *viewing_camera = Cast_Object(CameraShip*, viewing_entity);
Check(viewing_camera);
//
// Create controls mapping object
//
CameraControlsMapper
*camera_mapper;
Verify(GetControlsManager() != NULL);
CameraControlsMapper::SubsystemResource control_subsystem_resource;
Str_Copy(
control_subsystem_resource.subsystemName,
"ControlsMapper",
sizeof(control_subsystem_resource.subsystemName)
);
control_subsystem_resource.classID = TrivialSubsystemClassID;
control_subsystem_resource.subsystemModelSize =
sizeof(control_subsystem_resource);
LBE4ControlsManager* controls = GetControlsManager();
Check(controls);
switch (controls->primaryControlType)
{
case LBE4ControlsManager::PrimaryThrustMaster:
Tell("L4Application::MakeViewpointEntity, using ThrustMaster\n");
camera_mapper =
new CameraThrustmasterMapper(
viewing_camera,
CameraShip::ControlsMapperSubsystem,
&control_subsystem_resource
);
Register_Object(camera_mapper);
viewing_camera->SetMappingSubsystem(camera_mapper);
break;
case LBE4ControlsManager::PrimaryRIO:
Tell("L4Application::MakeViewpointEntity, using RIO\n");
camera_mapper =
new CameraRIOMapper(
viewing_camera,
CameraShip::ControlsMapperSubsystem,
&control_subsystem_resource
);
Register_Object(camera_mapper);
viewing_camera->SetMappingSubsystem(camera_mapper);
break;
default:
Fail("L4Application::MakeViewpointEntity, *** NO MAPPER! ***\n");
break;
}
//
//-------------------------------------------
// Create gauges for this entity
//-------------------------------------------
//
if (GetGaugeRenderer() != NULL)
{
L4GaugeRenderer *gauge_renderer = GetGaugeRenderer();
Check(gauge_renderer);
gauge_renderer->ConfigureForModel(
"Init",
viewing_entity
);
}
break;
}
return viewing_entity;
}
//
//#############################################################################
// RunMissionMessageHandler
//#############################################################################
//
void
L4Application::RunMissionMessageHandler(RunMissionMessage *message)
{
Check(this);
Verify(message->messageID == RunMissionMessageID);
//
//--------------------------------------------------------------------------
// If the application is already running then ignore this message
//--------------------------------------------------------------------------
//
switch (GetApplicationState())
{
case RunningMission:
break;
//
//-------------------------------------
// Start the playback if it is required
//-------------------------------------
//
case LaunchingMission:
{
LBE4ControlsManager *controls = GetControlsManager();
Check(controls);
if (controls->IsRecording())
{
controls->StartRecording();
}
else if (controls->IsPlayingBack())
{
controls->StartPlayback();
}
}
break;
case WaitingForLaunch:
PilotIllumination(False);
break;
default:
Fail("Application::RunMissionMessageHandler - Not ready to run!\n");
break;
}
//
//--------------------------------------------------------------------------
// Call previous handler
//--------------------------------------------------------------------------
//
Application::RunMissionMessageHandler(message);
}
//
//#############################################################################
// StopMissionMessageHandler
//#############################################################################
//
void
L4Application::StopMissionMessageHandler(StopMissionMessage *message)
{
Check(this);
Verify(message->messageID == StopMissionMessageID);
//
//--------------------------------------------------------------------------
// Call inherited handler
//--------------------------------------------------------------------------
//
Application::StopMissionMessageHandler(message);
//
//--------------------------------------------------------------------------
// Turn on illumination
//--------------------------------------------------------------------------
//
PilotIllumination(True);
//
//--------------------------------------------------------------------------
// Post a delayed message to ourselves to turn off the illumination
//--------------------------------------------------------------------------
//
Receiver::Message
lights_out_message(LightsOutMessageID, sizeof(Receiver::Message));
Time
event_time;
event_time = Now();
event_time += 30.0f;
Post(
LowEventPriority, // priority
this, // target
&lights_out_message, // message
event_time // when
);
}
//
//#############################################################################
// KeyCommandMessageHandler
//#############################################################################
//
void
L4Application::KeyCommandMessageHandler(
ReceiverDataMessageOf<ControlsKey> *message
)
{
Check(this);
Check(message);
//
// The debug keys are for developers: RP412DEVKEYS=1 arms them.
// Players get exactly one chord - Alt+Q, the deliberate abort.
//
static int dev_keys = -1;
if (dev_keys < 0)
{
const char *dev_value = getenv("RP412DEVKEYS");
dev_keys = (dev_value != NULL && atoi(dev_value) != 0) ? 1 : 0;
}
switch (message->dataContents)
{
//----------------------------------------------------------------
// Abort the mission, deliberately. The engine's legacy abort char
// is '&' (0x26) - but WM_KEYUP feeds VIRTUAL-KEY codes into this
// channel and VK_UP is also 0x26, so the plain code is swallowed
// below and the abort answers only to this chord.
//----------------------------------------------------------------
case PCK_ALT_Q:
message->dataContents = '&';
Application::KeyCommandMessageHandler(message);
break;
//----------------------------------------------------------------
// Swallowed collisions: 0x26 is VK_UP (the hat look-up key!) as
// well as a typed ampersand; 'E' (0x45 = the right-pedal key) was
// the event-queue dump. Neither may reach the engine handler.
//----------------------------------------------------------------
case '&':
case 'E':
break;
//------------------------------------
// Report current event queue (moved
// off the pedal key)
//------------------------------------
case PCK_ALT_E:
{
if (!dev_keys) break;
Check(application);
application->DumpEventQueue();
break;
}
//--------------------------------------------
// FrameDump from Division card to Targa file
//--------------------------------------------
case PCK_ALT_F:
{
if (!dev_keys) break;
DPLRenderer *dpl_renderer = l4_application->GetVideoRenderer();
if (dpl_renderer)
{
Check(dpl_renderer);
dpl_renderer->DPLFrameDump(True); // True indicates antialiased
}
break;
}
//------------------------------------
// Report current free memory in card
//------------------------------------
case PCK_ALT_K:
{
if (!dev_keys) break;
//STUBBED: HEAP RB 1/20/07
/*DPLRenderer *dpl_renderer = l4_application->GetVideoRenderer();
if (dpl_renderer)
{
Check(dpl_renderer);
dpl_renderer->DPLReportFreeMemory(DEBUG_STREAM) << std::flush;
}
DEBUG_STREAM << UserHeap::MainStorage.GetTotalHeapLeft() << '('
<< UserHeap::MainStorage.GetBiggestHeapLeft()
<< " contiguous) bytes of MUNGA core left\n"
<< UserHeap::MainStorage.GetHeapUsed() << " bytes of heap used\n";
#if defined(USE_MEMORY_ANALYSIS)
DEBUG_STREAM << UserHeap::MainStorage.GetLowestTotalHeapLeft()
<< " bytes minimum available so far\n"
<< UserHeap::MainStorage.GetMostHeapUsed()
<< " bytes maximum used so far\n";
#endif
MemoryBlockBase::UsageReport();*/
break;
}
//---------------------------------------
// Report performance statistics (Alt-?)
//---------------------------------------
case PCK_ALT_SLASH:
{
if (!dev_keys) break;
DPLRenderer *dpl_renderer = l4_application->GetVideoRenderer();
if (dpl_renderer)
{
Check(dpl_renderer);
dpl_renderer->DPLReportPerfStats(DEBUG_STREAM);
}
break;
}
//--------------------------
// Toggle Wireframe display
//--------------------------
case PCK_ALT_W:
{
if (!dev_keys) break;
DPLRenderer *dpl_renderer = l4_application->GetVideoRenderer();
if (dpl_renderer)
{
Check(dpl_renderer);
dpl_renderer->DPLToggleWireframe();
}
break;
}
//--------------------------
// Toggle "Predator-vision"
//--------------------------
case PCK_ALT_V:
{
if (!dev_keys) break;
DPLRenderer *dpl_renderer = l4_application->GetVideoRenderer();
if (dpl_renderer)
{
Check(dpl_renderer);
dpl_renderer->DPLTogglePVision();
}
break;
}
//--------------------------------------------------------------
// Toggle transparency dither pattern between random and static
//--------------------------------------------------------------
case PCK_ALT_R:
//-----------------------------
// Report position of eyepoint
//-----------------------------
case PCK_ALT_P:
DEBUG_STREAM << "Function net yet enabled.\n" << std::flush;
break;
//------------------------------
// Pass event on to Application
//------------------------------
default:
Application::KeyCommandMessageHandler(message);
break;
}
}
//
//#############################################################################
// LightsOutMessageHandler
//#############################################################################
//
#if DEBUG_LEVEL == 0
void
L4Application::LightsOutMessageHandler(Receiver::Message */*message*/)
{
#else
void
L4Application::LightsOutMessageHandler(Receiver::Message *message)
{
Check(this);
Verify(message->messageID == LightsOutMessageID);
#endif
//
//--------------------------------------------------------------------------
// Turn off illumination
//--------------------------------------------------------------------------
//
PilotIllumination(False);
}
//
//#############################################################################
// TeslaCoil
//#############################################################################
//
void
L4Application::TeslaCoil(Logical light_on)
{
Check(controlsManager);
if (light_on)
{
((LBE4ControlsManager *) controlsManager)->SetLamp(
LBE4ControlsManager::LampTesla1,
RIO::solid + RIO::state1Bright + RIO::state2Bright
);
((LBE4ControlsManager *) controlsManager)->SetLamp(
LBE4ControlsManager::LampTesla2,
RIO::solid + RIO::state1Bright + RIO::state2Bright
);
((LBE4ControlsManager *) controlsManager)->SetLamp(
LBE4ControlsManager::LampTesla3,
RIO::solid + RIO::state1Bright + RIO::state2Bright
);
}
else
{
((LBE4ControlsManager *) controlsManager)->SetLamp(LBE4ControlsManager::LampTesla1, RIO::solid + RIO::state1Off + RIO::state2Off);
((LBE4ControlsManager *) controlsManager)->SetLamp(LBE4ControlsManager::LampTesla2, RIO::solid + RIO::state1Off + RIO::state2Off);
((LBE4ControlsManager *) controlsManager)->SetLamp(LBE4ControlsManager::LampTesla3, RIO::solid + RIO::state1Off + RIO::state2Off);
}
}
//
//#############################################################################
// PilotIllumination
//#############################################################################
//
void
L4Application::PilotIllumination(Logical light_on)
{
Check(controlsManager);
TeslaCoil(light_on);
if (light_on)
{
//--------------------------------------------
// Turn on floor light
//--------------------------------------------
((LBE4ControlsManager *) controlsManager)->SetLamp(
LBE4ControlsManager::LampFloor,
RIO::solid + RIO::state1Bright + RIO::state2Bright
);
//--------------------------------------------
// Ramp all aux screens to white
//--------------------------------------------
L4GaugeRenderer *gauge_renderer = GetGaugeRenderer();
if (gauge_renderer != NULL)
{
Check(gauge_renderer);
gauge_renderer->FadeToWhite(1.5);
}
}
else
{
//--------------------------------------------
// Turn off floor light
//--------------------------------------------
((LBE4ControlsManager *) controlsManager)->SetLamp(
LBE4ControlsManager::LampFloor,
RIO::solid + RIO::state1Off + RIO::state2Off
);
//--------------------------------------------
// Ramp all aux screens to normal
//--------------------------------------------
L4GaugeRenderer *gauge_renderer = GetGaugeRenderer();
if (gauge_renderer != NULL)
{
Check(gauge_renderer);
gauge_renderer->FadeToNormal(1.5);
}
}
}
#ifdef TEST_CLASS
# include "l4app.tcp"
#endif