UI: Start to add option to stop emulation

This commit is contained in:
N3xoX1 2026-01-25 13:40:42 +10:30
parent 489a1f90c5
commit 75f5e4827b
7 changed files with 180 additions and 86 deletions

2
external/sciterui vendored

@ -1 +1 @@
Subproject commit 53f5174e0e374bee89574e91ab8f026c5dd8acac
Subproject commit 4e5df9373ed5c7714e06dfcc0ca1250a038188d3

View File

@ -36,4 +36,63 @@
background: #31363b;
border: 1px solid #434242;
padding: 1em;
}
}
#LoadingPanel
{
display: none;
width: 100%%;
height: 100%%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background-color: black;
color: white;
overflow: hidden;
text-align: center;
}
#LoadingPanel .loading-text
{
font-size: 2em;
margin: 50%% 0;
}
#LoadingPanel .loading-text > text
{
display: inline-block;
}
#LoadingPanel .dot-pulse-container
{
display: inline-block;
justify-content: center;
align-items: center;
}
#LoadingPanel .dot-pulse
{
display: inline-block;
width: 0.3em;
height: 0.3em;
margin: 0 0;
border-radius: 50%;
background-color: white;
animation: dot-pulse 1.5s infinite ease-in-out;
}
#LoadingPanel .dot-pulse:nth-child(1) { animation-delay: 0s; }
#LoadingPanel .dot-pulse:nth-child(2) { animation-delay: 0.5s; }
#LoadingPanel .dot-pulse:nth-child(3) { animation-delay: 1s; }
@keyframes dot-pulse {
0%, 100% {
opacity: 0.2;
transform: scale(0.75);
}
50% {
opacity: 1;
transform: scale(1.25);
}
}

View File

@ -1,64 +0,0 @@
<style>
.loading-container {
width: 100%%;
height: 100%%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background-color: black;
color: white;
overflow: hidden;
text-align: center;
}
.loading-text {
font-size: 2em;
margin: 50%% 0;
}
.loading-text > text
{
display: inline-block;
}
.dot-pulse-container {
display: inline-block;
justify-content: center;
align-items: center;
}
.dot-pulse {
display: inline-block;
width: 0.3em;
height: 0.3em;
margin: 0 0;
border-radius: 50%;
background-color: white;
animation: dot-pulse 1.5s infinite ease-in-out;
}
.dot-pulse:nth-child(1) { animation-delay: 0s; }
.dot-pulse:nth-child(2) { animation-delay: 0.5s; }
.dot-pulse:nth-child(3) { animation-delay: 1s; }
@keyframes dot-pulse {
0%, 100% {
opacity: 0.2;
transform: scale(0.75);
}
50% {
opacity: 1;
transform: scale(1.25);
}
}
</style>
<div class="loading-container">
<div class="loading-text"><span class="text">Loading</span>
<div class="dot-pulse-container">
<div class="dot-pulse"></div>
<div class="dot-pulse"></div>
<div class="dot-pulse"></div>
</div>
</div>
</div>

View File

@ -13,11 +13,22 @@
</window-buttons>
</header>
<mainmenu id="MainMenu" />
<div id="MainContents" />
<div id="MainContents">
<div id="LoadingPanel">
<div class="loading-text">
<span class="text">Loading</span>
<div class="dot-pulse-container">
<div class="dot-pulse"></div>
<div class="dot-pulse"></div>
<div class="dot-pulse"></div>
</div>
</div>
</div>
</div>
<div id="StatusBar" ><span id="renderer" /><span id="volume" /></div>
</html>
<popup id="VolumePopup">
<div>
<input type="hslider" id="Volume" min="0" max="200" value="100" id="audioVolume" style="width: 19em" />
<input type="hslider" min="0" max="200" value="100" id="audioVolume" style="width: 19em" />
</div>
</popup>

View File

@ -114,6 +114,9 @@ void OSManager::EmulationStarting()
void OSManager::EmulationStopping(bool wait)
{
// Disable unlimited frame rate
Settings::values.use_speed_limit.SetValue(true);
if (m_emuThread)
{
m_emuThread->Stop();

View File

@ -20,6 +20,17 @@
#include <Windows.h>
namespace
{
enum
{
EVENT_EMULATION_LOADING = 0x2000,
EVENT_EMULATION_RUNNING = 0x2001,
EVENT_EMULATION_STOPPED = 0x2002,
EVENT_EMULATION_FIRST_FRAME = 0x2003,
};
}
SciterMainWindow::SciterMainWindow(ISciterUI & sciterUI, const char * windowTitle) :
m_sciterUI(sciterUI),
m_window(nullptr),
@ -79,6 +90,12 @@ void SciterMainWindow::ResetMenu()
fileMenu.push_back(MenuBarItem(ID_FILE_EXIT, "Exit"));
mainTitleMenu.push_back(MenuBarItem(MenuBarItem::SUB_MENU, "File", &fileMenu));
MenuBarItemList systemMenu;
if (m_emulationRunning)
{
systemMenu.push_back(MenuBarItem(ID_SYSTEM_STOP, "Stop"));
mainTitleMenu.push_back(MenuBarItem(MenuBarItem::SUB_MENU, "System", &systemMenu));
}
MenuBarItemList optionsMenu;
optionsMenu.push_back(MenuBarItem(ID_EMULATION_CONTROLLERS, "Controllers..."));
@ -103,6 +120,7 @@ bool SciterMainWindow::Show()
}
m_rootElement = m_window->GetRootElement();
m_sciterUI.AttachHandler(m_rootElement, IID_IKEYSINK, (IKeySink *)this);
m_sciterUI.AttachHandler(m_rootElement, IID_EVENTSINK, (IEventSink *)this);
m_window->OnDestroySinkAdd(this);
m_window->CenterWindow();
SetCaption(m_windowTitle);
@ -155,15 +173,6 @@ void SciterMainWindow::LoadGame(const char * path)
loader.LoadRom(path);
}
void SciterMainWindow::ShowLoadingScreen()
{
SciterElement mainContents(m_rootElement.GetElementByID("MainContents"));
if (mainContents.IsValid())
{
m_sciterUI.SetElementHtmlFromResource(mainContents, "loading.html");
}
}
void SciterMainWindow::UpdateStatusbar()
{
SettingsStore & settings = SettingsStore::GetInstance();
@ -271,7 +280,15 @@ void SciterMainWindow::EmulationStateChanged(const char * /*setting*/, void * us
if (state == EmulationState::LoadingRom)
{
impl->ShowLoadingScreen();
impl->m_rootElement.PostEvent(EVENT_EMULATION_LOADING);
}
else if (state == EmulationState::Running)
{
impl->m_rootElement.PostEvent(EVENT_EMULATION_RUNNING);
}
else if (state == EmulationState::Stopped)
{
impl->m_rootElement.PostEvent(EVENT_EMULATION_STOPPED);
}
}
@ -322,13 +339,26 @@ void SciterMainWindow::GameNameChanged(const char * /*setting*/, void * userData
void SciterMainWindow::DisplayedFramesChanged(const char * /*setting*/, void * userData)
{
SciterMainWindow * impl = (SciterMainWindow*)userData;
SciterElement mainContents(impl->m_rootElement.GetElementByID("MainContents"));
if (mainContents.IsValid())
{
mainContents.SetHTML((uint8_t *)"", 0, SciterElement::SIH_REPLACE_CONTENT);
}
ShowWindow((HWND)impl->m_renderWindow, SW_SHOW);
impl->m_rootElement.PostEvent(EVENT_EMULATION_FIRST_FRAME);
}
void SciterMainWindow::PreventOSSleep()
{
#ifdef _WIN32
SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED);
#elif defined(HAVE_SDL2)
SDL_DisableScreenSaver();
#endif
}
void SciterMainWindow::AllowOSSleep()
{
#ifdef _WIN32
SetThreadExecutionState(ES_CONTINUOUS);
#elif defined(HAVE_SDL2)
SDL_EnableScreenSaver();
#endif
}
void SciterMainWindow::OnOpenFile()
@ -345,6 +375,17 @@ void SciterMainWindow::OnFileExit()
m_sciterUI.Stop();
}
void SciterMainWindow::OnStopGame()
{
if (!m_emulationRunning)
{
return;
}
AllowOSSleep();
SettingsStore & settings = SettingsStore::GetInstance();
settings.SetBool(NXCoreSetting::EmulationRunning, false);
}
void SciterMainWindow::OnSystemConfig()
{
ShowConfig(nullptr);
@ -377,6 +418,7 @@ void SciterMainWindow::OnMenuItem(int32_t id, SCITER_ELEMENT /*item*/)
{
case ID_FILE_LOAD_FILE: OnOpenFile(); break;
case ID_FILE_EXIT: OnFileExit(); break;
case ID_SYSTEM_STOP: OnStopGame(); break;
case ID_EMULATION_CONTROLLERS: OnInputConfig(); break;
case ID_EMULATION_CONFIGURE: OnSystemConfig(); break;
default:
@ -527,3 +569,37 @@ bool SciterMainWindow::OnTimer(SCITER_ELEMENT /*element*/, uint32_t * timerId)
}
return true;
}
bool SciterMainWindow::OnEvent(SCITER_ELEMENT /*element*/, SCITER_ELEMENT /*source*/, uint32_t event_code, uint64_t /*reason*/)
{
if (event_code == EVENT_EMULATION_LOADING)
{
SciterElement LoadingPanel(m_rootElement.GetElementByID("LoadingPanel"));
if (LoadingPanel.IsValid())
{
LoadingPanel.SetStyleAttribute("display", "block");
}
}
else if (event_code == EVENT_EMULATION_RUNNING)
{
PreventOSSleep();
}
else if (event_code == EVENT_EMULATION_STOPPED)
{
ShowWindow((HWND)m_renderWindow, SW_HIDE);
SciterElement LoadingPanel(m_rootElement.GetElementByID("LoadingPanel"));
if (LoadingPanel.IsValid())
{
LoadingPanel.SetStyleAttribute("display", "none");
}
}
else if (event_code == EVENT_EMULATION_FIRST_FRAME)
{
SciterElement LoadingPanel(m_rootElement.GetElementByID("LoadingPanel"));
if (LoadingPanel.IsValid())
{
LoadingPanel.SetStyleAttribute("display", "none");
}
}
return false;
}

View File

@ -20,7 +20,8 @@ class SciterMainWindow :
public IClickSink,
public IMouseUpDownSink,
public IStateChangeSink,
public ITimerSink
public ITimerSink,
public IEventSink
{
enum MainMenuID
{
@ -28,6 +29,9 @@ class SciterMainWindow :
ID_FILE_LOAD_FILE = 1,
ID_FILE_EXIT,
// System Menu
ID_SYSTEM_STOP,
// Emulation Menu
ID_EMULATION_CONTROLLERS,
ID_EMULATION_CONFIGURE,
@ -67,13 +71,15 @@ private:
static void GameFileChanged(const char * setting, void * userData);
static void GameNameChanged(const char * setting, void * userData);
static void DisplayedFramesChanged(const char * setting, void * userData);
void ShowLoadingScreen();
void UpdateStatusbar();
void DismissvolumePopup(SCITER_ELEMENT source, int32_t x, int32_t y);
void UpdateInputDrivers();
void PreventOSSleep();
void AllowOSSleep();
void OnOpenFile();
void OnFileExit();
void OnStopGame();
void OnSystemConfig();
void OnInputConfig();
void OnRecetGame(uint32_t fileIndex);
@ -105,6 +111,9 @@ private:
// ITimerSink
bool OnTimer(SCITER_ELEMENT Element, uint32_t* TimerId) override;
// IEventSink
bool OnEvent(SCITER_ELEMENT element, SCITER_ELEMENT source, uint32_t event_code, uint64_t reason);
static void SettingChanged(const char* setting, void* userData);
ISciterUI & m_sciterUI;