From dcb59deec5ea55f6014316248707231c2cc8bd41 Mon Sep 17 00:00:00 2001 From: N3xoX1 Date: Tue, 10 Feb 2026 20:45:05 +1030 Subject: [PATCH] UI: Add status text --- lang/english/css/main_window.css | 9 ++ lang/english/html/main_window.html | 2 +- src/nxemu-os/os_settings.cpp | 81 +++++++++++++++- src/nxemu-os/os_settings_identifiers.h | 4 + .../user_interface/sciter_main_window.cpp | 92 ++++++++++++++++++- src/nxemu/user_interface/sciter_main_window.h | 8 +- 6 files changed, 188 insertions(+), 8 deletions(-) diff --git a/lang/english/css/main_window.css b/lang/english/css/main_window.css index 352748c6..cd03385e 100644 --- a/lang/english/css/main_window.css +++ b/lang/english/css/main_window.css @@ -17,6 +17,15 @@ height: 1.6rem; } +#StatusText +{ + width: *; + text-align: right; + display: inline-block; + height: 100%%; + padding: 0.2em; +} + #StatusBar #volume, #StatusBar #renderer { diff --git a/lang/english/html/main_window.html b/lang/english/html/main_window.html index afa5c0e6..752c672e 100644 --- a/lang/english/html/main_window.html +++ b/lang/english/html/main_window.html @@ -29,7 +29,7 @@ -
+
diff --git a/src/nxemu-os/os_settings.cpp b/src/nxemu-os/os_settings.cpp index 70a2c2c9..96f801ed 100644 --- a/src/nxemu-os/os_settings.cpp +++ b/src/nxemu-os/os_settings.cpp @@ -11,7 +11,21 @@ extern IModuleSettings * g_settings; namespace { - enum class SettingType { StringSetting, StringValue, AudioEngine, AudioMode, U8, BooleanSetting, BooleanValue, UnsignedInt, ControllerType }; + enum class SettingType + { + StringSetting, + StringValue, + AudioEngine, + AudioMode, + U8, + U16, + BooleanSwitchable, + BooleanSetting, + BooleanValue, + UnsignedInt, + Float, + ControllerType + }; class OsSetting { @@ -20,9 +34,12 @@ namespace OsSetting(const char * id, const char * path, Settings::SwitchableSetting * val); OsSetting(const char * id, const char * path, Settings::SwitchableSetting* val); OsSetting(const char * id, const char * path, Settings::SwitchableSetting * val); - OsSetting(const char * id, const char * path, Settings::Setting * val); + OsSetting(const char * id, const char * path, Settings::SwitchableSetting * val); + OsSetting(const char * id, const char * path, Settings::SwitchableSetting * val); + OsSetting(const char * id, const char * path, Settings::Setting * val); OsSetting(const char * id, const char * path, bool * val, bool defaultValue); OsSetting(const char * id, const char * path, int * val, int defaultValue); + OsSetting(const char * id, const char * path, float * val, float defaultValue); OsSetting(const char * id, const char * path, uint32_t * val, uint32_t defaultValue); OsSetting(const char * id, const char * path, std::string * val, const char * defaultValue); OsSetting(const char * id, const char * path, InputSettings::ControllerType * val, InputSettings::ControllerType defaultValue); @@ -36,9 +53,12 @@ namespace Settings::SwitchableSetting * audioEngine; Settings::SwitchableSetting * audioMode; Settings::SwitchableSetting * u8; + Settings::SwitchableSetting * u16; + Settings::SwitchableSetting * booleanSwitchable; Settings::Setting * booleanSetting; bool * boolValue; uint32_t * uint32Value; + float * floatValue; std::string * stringValue; InputSettings::ControllerType * controllerType; } setting; @@ -46,6 +66,7 @@ namespace { bool boolValue; uint32_t uint32Value; + float floatValue; const char * sringValue; InputSettings::ControllerType controllerType; } defaultValue; @@ -422,12 +443,16 @@ namespace {nullptr, "controller\\player_9\\ProfileName", &Settings::values.players.GetValue(true)[9].profile_name, ""}, {nullptr, "controller\\player_9\\Vibration\\UseSystem", &Settings::values.players.GetValue(true)[9].use_system_vibrator, false}, - {NXOsSetting::AudioSinkId, "audio\\sink_id", &Settings::values.sink_id}, + { NXOsSetting::AudioSinkId, "audio\\sink_id", &Settings::values.sink_id}, { NXOsSetting::AudioOutputDeviceId, "audio\\output_device_id", &Settings::values.audio_output_device_id }, { NXOsSetting::AudioInputDeviceId, "audio\\input_device_id", &Settings::values.audio_input_device_id }, { NXOsSetting::AudioMode, "audio\\mode", &Settings::values.sound_index }, { NXOsSetting::AudioVolume, "audio\\volume", &Settings::values.volume }, { NXOsSetting::AudioMuted, "audio\\muted", &Settings::values.audio_muted }, + { NXOsSetting::ResolutionUpFactor, "resolution\\up_factor", &Settings::values.resolution_info.up_factor, 1.0f }, + { NXOsSetting::SpeedLimit, "core\\speed_limit", &Settings::values.speed_limit }, + { NXOsSetting::UseMultiCore, "core\\use_multi_core", &Settings::values.use_multi_core }, + { NXOsSetting::UseSpeedLimit, "core\\use_speed_limit", &Settings::values.use_speed_limit }, }; template @@ -544,6 +569,12 @@ void SetupOsSetting(void) case SettingType::U8: osSetting.setting.u8->SetValue(osSetting.setting.u8->GetDefault()); break; + case SettingType::U16: + osSetting.setting.u16->SetValue(osSetting.setting.u16->GetDefault()); + break; + case SettingType::BooleanSwitchable: + osSetting.setting.booleanSetting->SetValue(osSetting.setting.booleanSwitchable->GetDefault()); + break; case SettingType::BooleanSetting: osSetting.setting.booleanSetting->SetValue(osSetting.setting.booleanSetting->GetDefault()); break; @@ -553,6 +584,9 @@ void SetupOsSetting(void) case SettingType::UnsignedInt: *osSetting.setting.uint32Value = osSetting.defaultValue.uint32Value; break; + case SettingType::Float: + *osSetting.setting.floatValue = osSetting.defaultValue.floatValue; + break; case SettingType::StringValue: *osSetting.setting.stringValue = osSetting.defaultValue.sringValue; break; @@ -660,6 +694,14 @@ void SetupOsSetting(void) g_settings->SetDefaultInt(osSetting.identifier, (int32_t)osSetting.setting.u8->GetDefault()); g_settings->SetInt(osSetting.identifier, (int32_t)osSetting.setting.u8->GetValue()); break; + case SettingType::U16: + g_settings->SetDefaultInt(osSetting.identifier, (int32_t)osSetting.setting.u16->GetDefault()); + g_settings->SetInt(osSetting.identifier, (int32_t)osSetting.setting.u16->GetValue()); + break; + case SettingType::BooleanSwitchable: + g_settings->SetDefaultBool(osSetting.identifier, osSetting.setting.booleanSwitchable->GetDefault() != 0); + g_settings->SetBool(osSetting.identifier, osSetting.setting.booleanSwitchable->GetValue() != 0); + break; case SettingType::BooleanSetting: g_settings->SetDefaultBool(osSetting.identifier, osSetting.setting.booleanSetting->GetDefault() != 0); g_settings->SetBool(osSetting.identifier, osSetting.setting.booleanSetting->GetValue() != 0); @@ -668,6 +710,10 @@ void SetupOsSetting(void) g_settings->SetDefaultBool(osSetting.identifier, osSetting.defaultValue.boolValue); g_settings->SetBool(osSetting.identifier, *osSetting.setting.boolValue); break; + case SettingType::Float: + g_settings->SetDefaultFloat(osSetting.identifier, osSetting.defaultValue.floatValue); + g_settings->SetFloat(osSetting.identifier, *osSetting.setting.floatValue); + break; default: UNIMPLEMENTED(); } @@ -777,8 +823,24 @@ namespace { setting.u8 = val; } - - OsSetting::OsSetting(const char* id, const char * path, Settings::Setting * val) : + + OsSetting::OsSetting(const char * id, const char * path, Settings::SwitchableSetting * val) : + identifier(id), + json_path(path), + settingType(SettingType::U16) + { + setting.u16 = val; + } + + OsSetting::OsSetting(const char * id, const char * path, Settings::SwitchableSetting * val) : + identifier(id), + json_path(path), + settingType(SettingType::BooleanSwitchable) + { + setting.booleanSwitchable = val; + } + + OsSetting::OsSetting(const char * id, const char * path, Settings::Setting * val) : identifier(id), json_path(path), settingType(SettingType::BooleanSetting) @@ -804,6 +866,15 @@ namespace defaultValue.uint32Value = defaultValue_; } + OsSetting::OsSetting(const char * id, const char * path, float * val, float defaultValue_) : + identifier(id), + json_path(path), + settingType(SettingType::Float) + { + setting.floatValue = val; + defaultValue.floatValue = defaultValue_; + } + OsSetting::OsSetting(const char * id, const char * path, uint32_t * val, uint32_t defaultValue_) : identifier(id), json_path(path), diff --git a/src/nxemu-os/os_settings_identifiers.h b/src/nxemu-os/os_settings_identifiers.h index 39ee00c2..2fcdcf31 100644 --- a/src/nxemu-os/os_settings_identifiers.h +++ b/src/nxemu-os/os_settings_identifiers.h @@ -8,5 +8,9 @@ namespace NXOsSetting constexpr const char * AudioMode = "nxos:AudioMode"; constexpr const char * AudioVolume = "nxos:AudioVolume"; constexpr const char * AudioMuted = "nxos:AudioMuted"; + constexpr const char * ResolutionUpFactor = "nxos:ResolutionUpScale"; + constexpr const char * SpeedLimit = "nxos:SpeedLimit"; + constexpr const char * UseMultiCore = "nxos:UseMultiCore"; + constexpr const char * UseSpeedLimit = "nxos:UseSpeedLimit"; } // namespace NXOsSetting \ No newline at end of file diff --git a/src/nxemu/user_interface/sciter_main_window.cpp b/src/nxemu/user_interface/sciter_main_window.cpp index 8c1e6630..8b80a6a8 100644 --- a/src/nxemu/user_interface/sciter_main_window.cpp +++ b/src/nxemu/user_interface/sciter_main_window.cpp @@ -46,6 +46,14 @@ SciterMainWindow::SciterMainWindow(ISciterUI & sciterUI, const char * windowTitl settings.RegisterCallback(NXCoreSetting::GameName, SciterMainWindow::GameNameChanged, this); settings.RegisterCallback(NXCoreSetting::DisplayedFrames, SciterMainWindow::DisplayedFramesChanged, this); settings.RegisterCallback(NXOsSetting::AudioVolume, SciterMainWindow::SettingChanged, this); + settings.RegisterCallback(NXOsSetting::ResolutionUpFactor, SciterMainWindow::SettingChanged, this); + settings.RegisterCallback(NXOsSetting::SpeedLimit, SciterMainWindow::SettingChanged, this); + settings.RegisterCallback(NXOsSetting::UseMultiCore, SciterMainWindow::SettingChanged, this); + settings.RegisterCallback(NXOsSetting::UseSpeedLimit, SciterMainWindow::SettingChanged, this); + + m_useMultiCore = settings.GetBool(NXOsSetting::UseMultiCore); + m_useSpeedLimit = settings.GetBool(NXOsSetting::UseSpeedLimit); + m_speedLimit = settings.GetBool(NXOsSetting::SpeedLimit); } SciterMainWindow::~SciterMainWindow() @@ -418,7 +426,68 @@ void SciterMainWindow::OnInputConfig() m_inputConfig.reset(new InputConfig(m_sciterUI, m_modules)); m_inputConfig->Display((void*)m_window->GetHandle()); } - + +void SciterMainWindow::UpdateStatusBar() +{ + SciterElement statusTextEl(m_rootElement.GetElementByID("StatusText")); + if (!statusTextEl.IsValid()) + { + return; + } + if (!m_emulationRunning) + { + m_rootElement.SetTimer(0, (uint32_t *)TIMER_UPDATE_STATUS); + statusTextEl.SetText(""); + return; + } + + if (!m_modules.IsValid()) + { + return; + } + IOperatingSystem & operatingSystem = m_modules.Modules().OperatingSystem(); + IVideo & video = m_modules.Modules().Video(); + std::string status; + + const int shaders_building = video.ShadersBuilding(); + + if (shaders_building > 0) + { + status += stdstr_f("Building: %d shader(s)", shaders_building); + } + + PerfStatsResults results = operatingSystem.GetAndResetPerfStats(); + + if (!status.empty()) + { + status += " | "; + } + status += stdstr_f("Scale: %.0fx", m_resolutionUpFactor); + if (!m_useMultiCore) + { + status += " | "; + if (m_useSpeedLimit) + { + if (results.emulation_speed > 0.999 && results.emulation_speed < 1.01) + { + results.emulation_speed = 100.0; + } + status += stdstr_f("Speed: %.0f / %d", results.emulation_speed * 100.0, m_speedLimit); + } + else + { + status += stdstr_f("Speed: %f", results.emulation_speed * 100.0); + } + } + if (!status.empty()) + { + status += " | "; + } + status += stdstr_f("Game: %.0f FPS%s", std::round(results.average_game_fps), m_useSpeedLimit ? "" : " (Unlocked)"); + status += stdstr_f(" | Frame: %.2f ms", std::isnan(results.frametime) ? 0.0 : (results.frametime * 1000.0)); + statusTextEl.SetText(status.c_str()); +} + void SciterMainWindow::OnRecetGame(uint32_t fileIndex) { Stringlist & recentFiles = uiSettings.recentFiles; @@ -580,6 +649,22 @@ void SciterMainWindow::SettingChanged(const char * setting, void * userData) { impl->UpdateStatusbar(); } + else if (strcmp(setting, NXOsSetting::ResolutionUpFactor) == 0) + { + impl->m_resolutionUpFactor = SettingsStore::GetInstance().GetFloat(NXOsSetting::ResolutionUpFactor); + } + else if (strcmp(setting, NXOsSetting::UseMultiCore) == 0) + { + impl->m_useMultiCore = SettingsStore::GetInstance().GetBool(NXOsSetting::UseMultiCore); + } + else if (strcmp(setting, NXOsSetting::UseSpeedLimit) == 0) + { + impl->m_useSpeedLimit = SettingsStore::GetInstance().GetBool(NXOsSetting::UseSpeedLimit); + } + else if (strcmp(setting, NXOsSetting::SpeedLimit) == 0) + { + impl->m_speedLimit = SettingsStore::GetInstance().GetInt(NXOsSetting::SpeedLimit); + } } bool SciterMainWindow::OnTimer(SCITER_ELEMENT /*element*/, uint32_t * timerId) @@ -598,6 +683,10 @@ bool SciterMainWindow::OnTimer(SCITER_ELEMENT /*element*/, uint32_t * timerId) { UpdateInputDrivers(); } + else if (timerId == (uint32_t *)TIMER_UPDATE_STATUS) + { + UpdateStatusBar(); + } return true; } @@ -620,6 +709,7 @@ bool SciterMainWindow::OnEvent(SCITER_ELEMENT /*element*/, SCITER_ELEMENT /*sour else if (event_code == EVENT_EMULATION_RUNNING) { PreventOSSleep(); + m_rootElement.SetTimer(500, (uint32_t *)TIMER_UPDATE_STATUS); } else if (event_code == EVENT_EMULATION_STOPPED) { diff --git a/src/nxemu/user_interface/sciter_main_window.h b/src/nxemu/user_interface/sciter_main_window.h index 02c8a703..b275eaea 100644 --- a/src/nxemu/user_interface/sciter_main_window.h +++ b/src/nxemu/user_interface/sciter_main_window.h @@ -45,7 +45,8 @@ class SciterMainWindow : enum { TIMER_UPDATE_UI = 5000, - TIMER_UPDATE_INPUT = 5001, + TIMER_UPDATE_INPUT, + TIMER_UPDATE_STATUS, }; public: @@ -85,6 +86,7 @@ private: void OnSystemConfig(); void OnInputConfig(); void OnRecetGame(uint32_t fileIndex); + void UpdateStatusBar(); // IWindowDestroySink void OnWindowDestroy(HWINDOW hWnd) override; @@ -129,6 +131,10 @@ private: std::string m_windowTitle; std::unique_ptr m_systemConfig; std::unique_ptr m_inputConfig; + float m_resolutionUpFactor; bool m_volumePopup; + bool m_useMultiCore; + bool m_useSpeedLimit; + uint32_t m_speedLimit; bool m_emulationRunning; };