dolphin/Source/Core/DolphinWX/Frame.cpp
Léo Lam f106a9637d Replace balanced Core::PauseAndLock calls with RunAsCPUThread
Core::PauseAndLock requires all calls to it to be balanced, like this:

    const bool was_unpaused = Core::PauseAndLock(true);
    // do stuff on the CPU thread
    Core::PauseAndLock(false, was_unpaused);

Aside from being a bit cumbersome, it turns out all callers really
don't need to know about was_unpaused at all. They just need to do
something on the CPU thread safely, including locking/unlocking.

So this commit replaces Core::PauseAndLock with a function that
makes both the purpose and the scope of what is being run on the
CPU thread visually clear. This makes it harder to accidentally run
something on the wrong thread, or forget the second call to
PauseAndLock to unpause, or forget that it needs to be passed
was_unpaused at the end.

We also don't need comments to indicate code X is being run on the
CPU thread anymore, as the function name makes it obvious.
2017-07-21 16:45:59 +08:00

1707 lines
47 KiB
C++

// Copyright 2008 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "DolphinWX/Frame.h"
#include <algorithm>
#include <atomic>
#include <cstddef>
#include <fstream>
#include <string>
#include <utility>
#include <vector>
#include <wx/aui/auibook.h>
#include <wx/aui/framemanager.h>
#include <wx/filename.h>
#include <wx/frame.h>
#include <wx/icon.h>
#include <wx/listbase.h>
#include <wx/menu.h>
#include <wx/msgdlg.h>
#include <wx/panel.h>
#include <wx/sizer.h>
#include <wx/statusbr.h>
#include <wx/textctrl.h>
#include <wx/thread.h>
#include <wx/toolbar.h>
#if defined(__unix__) || defined(__unix) || defined(__APPLE__)
#include <signal.h>
#endif
#ifdef _WIN32
#include <windows.h>
#endif
#include "AudioCommon/AudioCommon.h"
#include "Common/CommonTypes.h"
#include "Common/FileUtil.h"
#include "Common/Flag.h"
#include "Common/Logging/ConsoleListener.h"
#include "Common/StringUtil.h"
#include "Common/Thread.h"
#include "Core/Config/GraphicsSettings.h"
#include "Core/ConfigManager.h"
#include "Core/Core.h"
#include "Core/HW/DVD/DVDInterface.h"
#include "Core/HW/GCKeyboard.h"
#include "Core/HW/GCPad.h"
#include "Core/HW/Wiimote.h"
#include "Core/HW/WiimoteEmu/WiimoteEmu.h"
#include "Core/HotkeyManager.h"
#include "Core/IOS/IOS.h"
#include "Core/IOS/USB/Bluetooth/BTBase.h"
#include "Core/Movie.h"
#include "Core/State.h"
#include "DolphinWX/Config/ConfigMain.h"
#include "DolphinWX/Debugger/BreakpointDlg.h"
#include "DolphinWX/Debugger/CodeWindow.h"
#include "DolphinWX/Debugger/MemoryCheckDlg.h"
#include "DolphinWX/GameListCtrl.h"
#include "DolphinWX/Globals.h"
#include "DolphinWX/LogWindow.h"
#include "DolphinWX/Main.h"
#include "DolphinWX/TASInputDlg.h"
#include "DolphinWX/WxUtils.h"
#include "InputCommon/ControllerInterface/ControllerInterface.h"
#include "InputCommon/GCPadStatus.h"
#include "VideoCommon/OnScreenDisplay.h"
#include "VideoCommon/RenderBase.h"
#include "VideoCommon/VertexShaderManager.h"
#include "VideoCommon/VideoConfig.h"
#if defined(HAVE_X11) && HAVE_X11
#include <gdk/gdkx.h>
#include <gtk/gtk.h>
// X11Utils nastiness that's only used here
namespace X11Utils
{
Window XWindowFromHandle(void* Handle)
{
return GDK_WINDOW_XID(gtk_widget_get_window(GTK_WIDGET(Handle)));
}
Display* XDisplayFromHandle(void* Handle)
{
return GDK_WINDOW_XDISPLAY(gtk_widget_get_window(GTK_WIDGET(Handle)));
}
}
#endif
CRenderFrame::CRenderFrame(wxFrame* parent, wxWindowID id, const wxString& title,
const wxPoint& pos, const wxSize& size, long style)
: wxFrame(parent, id, title, pos, size, style)
{
// Give it an icon
SetIcons(WxUtils::GetDolphinIconBundle());
DragAcceptFiles(true);
Bind(wxEVT_DROP_FILES, &CRenderFrame::OnDropFiles, this);
}
void CRenderFrame::OnDropFiles(wxDropFilesEvent& event)
{
if (event.GetNumberOfFiles() != 1)
return;
if (File::IsDirectory(WxStrToStr(event.GetFiles()[0])))
return;
wxFileName file = event.GetFiles()[0];
const std::string filepath = WxStrToStr(file.GetFullPath());
if (file.GetExt() == "dtm")
{
if (Core::IsRunning())
return;
if (!Movie::IsReadOnly())
{
// let's make the read-only flag consistent at the start of a movie.
Movie::SetReadOnly(true);
main_frame->GetMenuBar()->FindItem(IDM_RECORD_READ_ONLY)->Check(true);
}
if (Movie::PlayInput(filepath))
main_frame->BootGame("");
}
else if (!Core::IsRunning())
{
main_frame->BootGame(filepath);
}
else if (IsValidSavestateDropped(filepath) && Core::IsRunning())
{
State::LoadAs(filepath);
}
else
{
DVDInterface::ChangeDiscAsHost(filepath);
}
}
bool CRenderFrame::IsValidSavestateDropped(const std::string& filepath)
{
const int game_id_length = 6;
std::ifstream file(filepath, std::ios::in | std::ios::binary);
if (!file)
return false;
std::string internal_game_id(game_id_length, ' ');
file.read(&internal_game_id[0], game_id_length);
return internal_game_id == SConfig::GetInstance().GetGameID();
}
#ifdef _WIN32
WXLRESULT CRenderFrame::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
{
switch (nMsg)
{
case WM_SYSCOMMAND:
switch (wParam)
{
case SC_SCREENSAVE:
case SC_MONITORPOWER:
if (Core::GetState() == Core::State::Running && SConfig::GetInstance().bDisableScreenSaver)
break;
default:
return wxFrame::MSWWindowProc(nMsg, wParam, lParam);
}
break;
case WM_USER:
switch (wParam)
{
case WM_USER_STOP:
main_frame->DoStop();
break;
case WM_USER_SETCURSOR:
if (SConfig::GetInstance().bHideCursor && main_frame->RendererHasFocus() &&
Core::GetState() == Core::State::Running)
SetCursor(wxCURSOR_BLANK);
else
SetCursor(wxNullCursor);
break;
}
break;
case WM_CLOSE:
// Let Core finish initializing before accepting any WM_CLOSE messages
if (!Core::IsRunning())
break;
// Use default action otherwise
default:
// By default let wxWidgets do what it normally does with this event
return wxFrame::MSWWindowProc(nMsg, wParam, lParam);
}
return 0;
}
#endif
bool CRenderFrame::ShowFullScreen(bool show, long style)
{
#if defined WIN32
if (show && !g_Config.bBorderlessFullscreen)
{
// OpenGL requires the pop-up style to activate exclusive mode.
SetWindowStyle((GetWindowStyle() & ~wxDEFAULT_FRAME_STYLE) | wxPOPUP_WINDOW);
}
#endif
bool result = wxTopLevelWindow::ShowFullScreen(show, style);
#if defined WIN32
if (!show)
{
// Restore the default style.
SetWindowStyle((GetWindowStyle() & ~wxPOPUP_WINDOW) | wxDEFAULT_FRAME_STYLE);
}
#endif
return result;
}
wxDEFINE_EVENT(wxEVT_HOST_COMMAND, wxCommandEvent);
wxDEFINE_EVENT(DOLPHIN_EVT_LOCAL_INI_CHANGED, wxCommandEvent);
wxDEFINE_EVENT(DOLPHIN_EVT_RELOAD_THEME_BITMAPS, wxCommandEvent);
wxDEFINE_EVENT(DOLPHIN_EVT_UPDATE_LOAD_WII_MENU_ITEM, wxCommandEvent);
wxDEFINE_EVENT(DOLPHIN_EVT_BOOT_SOFTWARE, wxCommandEvent);
wxDEFINE_EVENT(DOLPHIN_EVT_STOP_SOFTWARE, wxCommandEvent);
// Event tables
BEGIN_EVENT_TABLE(CFrame, CRenderFrame)
// Debugger pane context menu
EVT_MENU_RANGE(IDM_FLOAT_LOG_WINDOW, IDM_FLOAT_CODE_WINDOW, CFrame::OnFloatWindow)
// Game list context menu
EVT_MENU(IDM_LIST_INSTALL_WAD, CFrame::OnInstallWAD)
EVT_MENU(IDM_LIST_UNINSTALL_WAD, CFrame::OnUninstallWAD)
// Other
EVT_ACTIVATE(CFrame::OnActive)
EVT_CLOSE(CFrame::OnClose)
EVT_SIZE(CFrame::OnResize)
EVT_MOVE(CFrame::OnMove)
EVT_HOST_COMMAND(wxID_ANY, CFrame::OnHostMessage)
EVT_AUI_PANE_CLOSE(CFrame::OnPaneClose)
// Post events to child panels
EVT_MENU_RANGE(IDM_INTERPRETER, IDM_ADDRBOX, CFrame::PostEvent)
END_EVENT_TABLE()
// ---------------
// Creation and close, quit functions
bool CFrame::InitControllers()
{
if (!g_controller_interface.IsInit())
{
#if defined(HAVE_X11) && HAVE_X11
void* win = reinterpret_cast<void*>(X11Utils::XWindowFromHandle(GetHandle()));
#else
void* win = reinterpret_cast<void*>(GetHandle());
#endif
g_controller_interface.Initialize(win);
Pad::Initialize();
Keyboard::Initialize();
Wiimote::Initialize(Wiimote::InitializeMode::DO_NOT_WAIT_FOR_WIIMOTES);
HotkeyManagerEmu::Initialize();
return true;
}
return false;
}
static Common::Flag s_shutdown_signal_received;
#ifdef _WIN32
static BOOL WINAPI s_ctrl_handler(DWORD fdwCtrlType)
{
switch (fdwCtrlType)
{
case CTRL_C_EVENT:
case CTRL_BREAK_EVENT:
case CTRL_CLOSE_EVENT:
case CTRL_LOGOFF_EVENT:
case CTRL_SHUTDOWN_EVENT:
SetConsoleCtrlHandler(s_ctrl_handler, FALSE);
s_shutdown_signal_received.Set();
return TRUE;
default:
return FALSE;
}
}
#endif
#if defined(__unix__) || defined(__unix) || defined(__APPLE__)
static void SignalHandler(int)
{
const char message[] = "A signal was received. A second signal will force Dolphin to stop.\n";
if (write(STDERR_FILENO, message, sizeof(message)) < 0)
{
}
s_shutdown_signal_received.Set();
}
#endif
CFrame::CFrame(wxFrame* parent, wxWindowID id, const wxString& title, wxRect geometry,
bool use_debugger, bool batch_mode, bool show_log_window, long style)
: CRenderFrame(parent, id, title, wxDefaultPosition, wxSize(800, 600), style),
m_use_debugger(use_debugger), m_batch_mode(batch_mode)
{
BindEvents();
if (show_log_window)
SConfig::GetInstance().m_InterfaceLogWindow = true;
// Debugger class
if (m_use_debugger)
{
m_code_window = new CCodeWindow(this, IDM_CODE_WINDOW);
LoadIniPerspectives();
m_code_window->Load();
}
wxFrame::CreateToolBar(wxTB_DEFAULT_STYLE | wxTB_TEXT | wxTB_FLAT)->Realize();
// Give it a status bar
SetStatusBar(
CreateStatusBar(2, wxSTB_SIZEGRIP | wxSTB_ELLIPSIZE_END | wxSTB_SHOW_TIPS, ID_STATUSBAR));
if (!SConfig::GetInstance().m_InterfaceStatusbar)
GetStatusBar()->Hide();
// Give it a menu bar
SetMenuBar(CreateMenuBar());
// Create a menubar to service requests while the real menubar is hidden from the screen
m_menubar_shadow = CreateMenuBar();
// ---------------
// Main panel
// This panel is the parent for rendering and it holds the gamelistctrl
m_panel = new wxPanel(this, IDM_MPANEL, wxDefaultPosition, wxDefaultSize, 0);
m_game_list_ctrl =
new GameListCtrl(m_batch_mode, m_panel, wxID_ANY, wxDefaultPosition, wxDefaultSize,
wxLC_REPORT | wxSUNKEN_BORDER | wxLC_ALIGN_LEFT);
m_game_list_ctrl->Bind(wxEVT_LIST_ITEM_ACTIVATED, &CFrame::OnGameListCtrlItemActivated, this);
wxBoxSizer* sizerPanel = new wxBoxSizer(wxHORIZONTAL);
sizerPanel->Add(m_game_list_ctrl, 1, wxEXPAND | wxALL);
m_panel->SetSizer(sizerPanel);
// ---------------
// Manager
m_mgr = new wxAuiManager(this, wxAUI_MGR_DEFAULT | wxAUI_MGR_LIVE_RESIZE);
m_mgr->AddPane(m_panel, wxAuiPaneInfo()
.Name("Pane 0")
.Caption("Pane 0")
.PaneBorder(false)
.CaptionVisible(false)
.Layer(0)
.Center()
.Show());
if (!m_code_window)
m_mgr->AddPane(CreateEmptyNotebook(), wxAuiPaneInfo()
.Name("Pane 1")
.Caption(_("Logging"))
.CaptionVisible(true)
.Layer(0)
.FloatingSize(wxSize(600, 350))
.CloseButton(true)
.Hide());
m_aui_fullscreen_perspective = m_mgr->SavePerspective();
if (!SConfig::GetInstance().m_InterfaceToolbar)
DoToggleToolbar(false);
m_log_window = new CLogWindow(this, IDM_LOG_WINDOW);
m_log_window->Hide();
m_log_window->Disable();
InitializeTASDialogs();
InitializeCoreCallbacks();
// Setup perspectives
if (m_code_window)
{
// Load perspective
DoLoadPerspective();
}
else
{
if (SConfig::GetInstance().m_InterfaceLogWindow)
ToggleLogWindow(true);
if (SConfig::GetInstance().m_InterfaceLogConfigWindow)
ToggleLogConfigWindow(true);
}
// Setup the window size.
// This has to be done here instead of in Main because the Show() happens here.
SetMinSize(FromDIP(wxSize(400, 300)));
WxUtils::SetWindowSizeAndFitToScreen(this, geometry.GetPosition(), geometry.GetSize(),
FromDIP(wxSize(800, 600)));
// Start debugging maximized (Must be after the window has been positioned)
if (m_use_debugger)
Maximize(true);
// Commit
m_mgr->Update();
// The window must be shown for m_xrr_config to be created (wxGTK will not allocate X11
// resources until the window is shown for the first time).
Show();
#ifdef _WIN32
SetToolTip("");
GetToolTip()->SetAutoPop(25000);
#endif
#if defined(HAVE_XRANDR) && HAVE_XRANDR
m_xrr_config = new X11Utils::XRRConfiguration(X11Utils::XDisplayFromHandle(GetHandle()),
X11Utils::XWindowFromHandle(GetHandle()));
#endif
// Connect event handlers
m_mgr->Bind(wxEVT_AUI_RENDER, &CFrame::OnManagerResize, this);
// Update controls
UpdateGUI();
// check if game is running
InitControllers();
m_poll_hotkey_timer.SetOwner(this);
Bind(wxEVT_TIMER, &CFrame::PollHotkeys, this, m_poll_hotkey_timer.GetId());
m_poll_hotkey_timer.Start(1000 / 60, wxTIMER_CONTINUOUS);
// Shut down cleanly on SIGINT, SIGTERM (Unix) and on various signals on Windows
m_handle_signal_timer.SetOwner(this);
Bind(wxEVT_TIMER, &CFrame::HandleSignal, this, m_handle_signal_timer.GetId());
m_handle_signal_timer.Start(100, wxTIMER_CONTINUOUS);
#if defined(__unix__) || defined(__unix) || defined(__APPLE__)
struct sigaction sa;
sa.sa_handler = SignalHandler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESETHAND;
sigaction(SIGINT, &sa, nullptr);
sigaction(SIGTERM, &sa, nullptr);
#endif
#ifdef _WIN32
SetConsoleCtrlHandler(s_ctrl_handler, TRUE);
#endif
}
// Destructor
CFrame::~CFrame()
{
Wiimote::Shutdown();
Keyboard::Shutdown();
Pad::Shutdown();
HotkeyManagerEmu::Shutdown();
g_controller_interface.Shutdown();
#if defined(HAVE_XRANDR) && HAVE_XRANDR
delete m_xrr_config;
#endif
ClosePages();
delete m_mgr;
// This object is owned by us, not wxw
m_menubar_shadow->Destroy();
m_menubar_shadow = nullptr;
}
void CFrame::BindEvents()
{
BindMenuBarEvents();
Bind(DOLPHIN_EVT_RELOAD_THEME_BITMAPS, &CFrame::OnReloadThemeBitmaps, this);
Bind(DOLPHIN_EVT_REFRESH_GAMELIST, &CFrame::OnRefreshGameList, this);
Bind(DOLPHIN_EVT_RESCAN_GAMELIST, &CFrame::OnRescanGameList, this);
Bind(DOLPHIN_EVT_UPDATE_LOAD_WII_MENU_ITEM, &CFrame::OnUpdateLoadWiiMenuItem, this);
Bind(DOLPHIN_EVT_BOOT_SOFTWARE, &CFrame::OnPlay, this);
Bind(DOLPHIN_EVT_STOP_SOFTWARE, &CFrame::OnStop, this);
}
void CFrame::InitializeTASDialogs()
{
std::generate(m_tas_input_dialogs.begin(), m_tas_input_dialogs.end(),
[this] { return new TASInputDlg{this}; });
Movie::SetGCInputManip([this](GCPadStatus* pad_status, int controller_id) {
m_tas_input_dialogs[controller_id]->GetValues(pad_status);
});
Movie::SetWiiInputManip([this](u8* data, WiimoteEmu::ReportFeatures rptf, int controller_id,
int ext, wiimote_key key) {
m_tas_input_dialogs[controller_id + 4]->GetValues(data, rptf, ext, key);
});
}
void CFrame::InitializeCoreCallbacks()
{
// Warning: this gets called from the CPU thread, so we should
// only queue things to do on the proper thread
State::SetOnAfterLoadCallback([this] {
AddPendingEvent(wxCommandEvent{wxEVT_HOST_COMMAND, IDM_UPDATE_GUI});
});
// Warning: this gets called from the EmuThread
Core::SetOnStoppedCallback([this] {
AddPendingEvent(wxCommandEvent{wxEVT_HOST_COMMAND, IDM_STOPPED});
});
}
bool CFrame::RendererIsFullscreen()
{
bool fullscreen = false;
if (Core::GetState() == Core::State::Running || Core::GetState() == Core::State::Paused)
{
fullscreen = m_render_frame->IsFullScreen();
}
return fullscreen;
}
void CFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
{
Close(true);
}
// --------
// Events
void CFrame::OnActive(wxActivateEvent& event)
{
m_renderer_has_focus = (event.GetActive() && event.GetEventObject() == m_render_frame);
if (Core::GetState() == Core::State::Running || Core::GetState() == Core::State::Paused)
{
if (m_renderer_has_focus)
{
if (SConfig::GetInstance().bRenderToMain)
m_render_parent->SetFocus();
else if (RendererIsFullscreen() && g_ActiveConfig.ExclusiveFullscreenEnabled())
DoExclusiveFullscreen(true); // Regain exclusive mode
if (SConfig::GetInstance().m_PauseOnFocusLost && Core::GetState() == Core::State::Paused)
DoPause();
if (SConfig::GetInstance().bHideCursor && Core::GetState() == Core::State::Running)
m_render_parent->SetCursor(wxCURSOR_BLANK);
}
else
{
if (SConfig::GetInstance().m_PauseOnFocusLost && Core::GetState() == Core::State::Running)
DoPause();
if (SConfig::GetInstance().bHideCursor)
m_render_parent->SetCursor(wxNullCursor);
}
}
event.Skip();
}
void CFrame::OnClose(wxCloseEvent& event)
{
// Before closing the window we need to shut down the emulation core.
// We'll try to close this window again once that is done.
if (Core::GetState() != Core::State::Uninitialized)
{
DoStop();
if (event.CanVeto())
{
event.Veto();
}
// Tell OnStopped to resubmit the Close event
m_is_closing = true;
return;
}
// Stop Dolphin from saving the minimized Xpos and Ypos
if (main_frame->IsIconized())
main_frame->Iconize(false);
// Don't forget the skip or the window won't be destroyed
event.Skip();
// Save GUI settings
if (m_code_window)
{
SaveIniPerspectives();
}
else
{
m_log_window->SaveSettings();
}
if (m_log_window)
m_log_window->RemoveAllListeners();
// Uninit
m_mgr->UnInit();
}
// Post events
// Warning: This may cause an endless loop if the event is propagated back to its parent
void CFrame::PostEvent(wxCommandEvent& event)
{
if (m_code_window && event.GetId() >= IDM_INTERPRETER && event.GetId() <= IDM_ADDRBOX)
{
event.StopPropagation();
m_code_window->GetEventHandler()->AddPendingEvent(event);
}
else
{
event.Skip();
}
}
void CFrame::OnMove(wxMoveEvent& event)
{
event.Skip();
if (!IsMaximized() && !(SConfig::GetInstance().bRenderToMain && RendererIsFullscreen()))
{
SConfig::GetInstance().iPosX = GetPosition().x;
SConfig::GetInstance().iPosY = GetPosition().y;
}
}
void CFrame::OnResize(wxSizeEvent& event)
{
event.Skip();
if (!IsMaximized() && !IsIconized() &&
!(SConfig::GetInstance().bRenderToMain && RendererIsFullscreen()) &&
!(Core::GetState() != Core::State::Uninitialized && SConfig::GetInstance().bRenderToMain &&
SConfig::GetInstance().bRenderWindowAutoSize))
{
SConfig::GetInstance().iWidth = GetSize().GetWidth();
SConfig::GetInstance().iHeight = GetSize().GetHeight();
}
// Make sure the logger pane is a sane size
if (!m_code_window && m_log_window && m_mgr->GetPane("Pane 1").IsShown() &&
!m_mgr->GetPane("Pane 1").IsFloating() && (m_log_window->x > GetClientRect().GetWidth() ||
m_log_window->y > GetClientRect().GetHeight()))
{
ShowResizePane();
}
}
// Host messages
#ifdef _WIN32
WXLRESULT CFrame::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
{
if (WM_SYSCOMMAND == nMsg && (SC_SCREENSAVE == wParam || SC_MONITORPOWER == wParam))
{
return 0;
}
else if (nMsg == WM_QUERYENDSESSION)
{
// Indicate that the application will be able to close
return 1;
}
else if (nMsg == WM_ENDSESSION)
{
// Actually trigger the close now
Close(true);
return 0;
}
else
{
return wxFrame::MSWWindowProc(nMsg, wParam, lParam);
}
}
#endif
void CFrame::InhibitScreensaver()
{
// Inhibit the screensaver. Depending on the operating system this may also
// disable low-power states and/or screen dimming.
#if defined(HAVE_X11) && HAVE_X11
if (SConfig::GetInstance().bDisableScreenSaver)
{
X11Utils::InhibitScreensaver(X11Utils::XDisplayFromHandle(GetHandle()),
X11Utils::XWindowFromHandle(GetHandle()), true);
}
#endif
#ifdef _WIN32
// Prevents Windows from sleeping, turning off the display, or idling
EXECUTION_STATE should_screen_save =
SConfig::GetInstance().bDisableScreenSaver ? ES_DISPLAY_REQUIRED : 0;
SetThreadExecutionState(ES_CONTINUOUS | should_screen_save | ES_SYSTEM_REQUIRED);
#endif
#ifdef __APPLE__
if (SConfig::GetInstance().bDisableScreenSaver)
{
CFStringRef reason_for_activity = CFSTR("Emulation Running");
if (IOPMAssertionCreateWithName(kIOPMAssertionTypePreventUserIdleDisplaySleep,
kIOPMAssertionLevelOn, reason_for_activity,
&m_power_assertion) != kIOReturnSuccess)
{
m_power_assertion = kIOPMNullAssertionID;
}
}
#endif
}
void CFrame::UninhibitScreensaver()
{
#if defined(HAVE_X11) && HAVE_X11
if (SConfig::GetInstance().bDisableScreenSaver)
{
X11Utils::InhibitScreensaver(X11Utils::XDisplayFromHandle(GetHandle()),
X11Utils::XWindowFromHandle(GetHandle()), false);
}
#endif
#ifdef _WIN32
// Allow windows to resume normal idling behavior
SetThreadExecutionState(ES_CONTINUOUS);
#endif
#ifdef __APPLE__
if (m_power_assertion != kIOPMNullAssertionID)
{
IOPMAssertionRelease(m_power_assertion);
m_power_assertion = kIOPMNullAssertionID;
}
#endif
}
void CFrame::UpdateTitle(const wxString& str)
{
const wxString revision_string = StrToWxStr(scm_rev_str);
if (SConfig::GetInstance().bRenderToMain && SConfig::GetInstance().m_InterfaceStatusbar)
{
GetStatusBar()->SetStatusText(str, 0);
m_render_frame->SetTitle(revision_string);
}
else
{
m_render_frame->SetTitle(revision_string + StrToWxStr(" | ") + str);
}
}
void CFrame::OnHostMessage(wxCommandEvent& event)
{
switch (event.GetId())
{
case IDM_UPDATE_DISASM_DIALOG: // For breakpoints causing pausing
if (!m_code_window || Core::GetState() != Core::State::Paused)
return;
// fallthrough
case IDM_UPDATE_GUI:
UpdateGUI();
break;
case IDM_UPDATE_STATUS_BAR:
if (GetStatusBar() != nullptr)
GetStatusBar()->SetStatusText(event.GetString(), event.GetInt());
break;
case IDM_UPDATE_TITLE:
UpdateTitle(event.GetString());
break;
case IDM_WINDOW_SIZE_REQUEST:
{
std::pair<int, int>* win_size = (std::pair<int, int>*)(event.GetClientData());
OnRenderWindowSizeRequest(win_size->first, win_size->second);
delete win_size;
}
break;
case WM_USER_CREATE:
if (SConfig::GetInstance().bHideCursor)
m_render_parent->SetCursor(wxCURSOR_BLANK);
break;
case IDM_PANIC:
{
wxString caption = event.GetString().BeforeFirst(':');
wxString text = event.GetString().AfterFirst(':');
m_panic_result =
(wxYES == wxMessageBox(text, caption, wxSTAY_ON_TOP | (event.GetInt() ? wxYES_NO : wxOK),
wxWindow::FindFocus()));
m_panic_event.Set();
}
break;
case WM_USER_STOP:
DoStop();
break;
case IDM_STOPPED:
OnStopped();
break;
case IDM_FORCE_CONNECT_WIIMOTE1:
case IDM_FORCE_CONNECT_WIIMOTE2:
case IDM_FORCE_CONNECT_WIIMOTE3:
case IDM_FORCE_CONNECT_WIIMOTE4:
case IDM_FORCE_CONNECT_BALANCEBOARD:
ConnectWiimote(event.GetId() - IDM_FORCE_CONNECT_WIIMOTE1, true);
break;
case IDM_FORCE_DISCONNECT_WIIMOTE1:
case IDM_FORCE_DISCONNECT_WIIMOTE2:
case IDM_FORCE_DISCONNECT_WIIMOTE3:
case IDM_FORCE_DISCONNECT_WIIMOTE4:
case IDM_FORCE_DISCONNECT_BALANCEBOARD:
ConnectWiimote(event.GetId() - IDM_FORCE_DISCONNECT_WIIMOTE1, false);
break;
}
}
void CFrame::OnRenderWindowSizeRequest(int width, int height)
{
if (!SConfig::GetInstance().bRenderWindowAutoSize || !Core::IsRunning() ||
RendererIsFullscreen() || m_render_frame->IsMaximized())
return;
wxSize requested_size(width, height);
// Convert to window pixels, since the size is from the backend it will be in framebuffer px.
requested_size *= 1.0 / m_render_frame->GetContentScaleFactor();
wxSize old_size;
if (!SConfig::GetInstance().bRenderToMain)
{
old_size = m_render_frame->GetClientSize();
}
else
{
// Resize for the render panel only, this implicitly retains space for everything else
// (i.e. log panel, toolbar, statusbar, etc) without needing to compute for them.
old_size = m_render_parent->GetSize();
}
wxSize diff = requested_size - old_size;
if (diff != wxSize())
m_render_frame->SetSize(m_render_frame->GetSize() + diff);
}
bool CFrame::RendererHasFocus()
{
if (m_render_parent == nullptr)
return false;
return m_renderer_has_focus;
}
void CFrame::OnGameListCtrlItemActivated(wxListEvent& WXUNUSED(event))
{
// Show all platforms and regions if...
// 1. All platforms are set to hide
// 2. All Regions are set to hide
// Otherwise call BootGame to either...
// 1. Boot the selected iso
// 2. Boot the default or last loaded iso.
// 3. Call BrowseForDirectory if the gamelist is empty
if (!m_game_list_ctrl->GetISO(0) && GameListCtrl::IsHidingItems())
{
SConfig::GetInstance().m_ListGC = SConfig::GetInstance().m_ListWii =
SConfig::GetInstance().m_ListWad = SConfig::GetInstance().m_ListElfDol =
SConfig::GetInstance().m_ListJap = SConfig::GetInstance().m_ListUsa =
SConfig::GetInstance().m_ListPal = SConfig::GetInstance().m_ListAustralia =
SConfig::GetInstance().m_ListFrance = SConfig::GetInstance().m_ListGermany =
SConfig::GetInstance().m_ListItaly = SConfig::GetInstance().m_ListKorea =
SConfig::GetInstance().m_ListNetherlands =
SConfig::GetInstance().m_ListRussia =
SConfig::GetInstance().m_ListSpain =
SConfig::GetInstance().m_ListTaiwan =
SConfig::GetInstance().m_ListWorld =
SConfig::GetInstance().m_ListUnknown = true;
GetMenuBar()->FindItem(IDM_LIST_GC)->Check(true);
GetMenuBar()->FindItem(IDM_LIST_WII)->Check(true);
GetMenuBar()->FindItem(IDM_LIST_WAD)->Check(true);
GetMenuBar()->FindItem(IDM_LIST_JAP)->Check(true);
GetMenuBar()->FindItem(IDM_LIST_USA)->Check(true);
GetMenuBar()->FindItem(IDM_LIST_PAL)->Check(true);
GetMenuBar()->FindItem(IDM_LIST_AUSTRALIA)->Check(true);
GetMenuBar()->FindItem(IDM_LIST_FRANCE)->Check(true);
GetMenuBar()->FindItem(IDM_LIST_GERMANY)->Check(true);
GetMenuBar()->FindItem(IDM_LIST_ITALY)->Check(true);
GetMenuBar()->FindItem(IDM_LIST_KOREA)->Check(true);
GetMenuBar()->FindItem(IDM_LIST_NETHERLANDS)->Check(true);
GetMenuBar()->FindItem(IDM_LIST_RUSSIA)->Check(true);
GetMenuBar()->FindItem(IDM_LIST_SPAIN)->Check(true);
GetMenuBar()->FindItem(IDM_LIST_TAIWAN)->Check(true);
GetMenuBar()->FindItem(IDM_LIST_WORLD)->Check(true);
GetMenuBar()->FindItem(IDM_LIST_UNKNOWN)->Check(true);
GameListRefresh();
}
else if (!m_game_list_ctrl->GetISO(0))
{
m_game_list_ctrl->BrowseForDirectory();
}
else
{
// Game started by double click
BootGame("");
}
}
static bool IsHotkey(int id, bool held = false)
{
return HotkeyManagerEmu::IsPressed(id, held);
}
static int GetMenuIDFromHotkey(unsigned int key)
{
switch (key)
{
case HK_OPEN:
return wxID_OPEN;
case HK_CHANGE_DISC:
return IDM_CHANGE_DISC;
case HK_REFRESH_LIST:
return wxID_REFRESH;
case HK_PLAY_PAUSE:
return IDM_PLAY;
case HK_STOP:
return IDM_STOP;
case HK_RESET:
return IDM_RESET;
case HK_FRAME_ADVANCE:
return IDM_FRAMESTEP;
case HK_START_RECORDING:
return IDM_RECORD;
case HK_PLAY_RECORDING:
return IDM_PLAY_RECORD;
case HK_EXPORT_RECORDING:
return IDM_RECORD_EXPORT;
case HK_READ_ONLY_MODE:
return IDM_RECORD_READ_ONLY;
case HK_FULLSCREEN:
return IDM_TOGGLE_FULLSCREEN;
case HK_SCREENSHOT:
return IDM_SCREENSHOT;
case HK_EXIT:
return wxID_EXIT;
case HK_WIIMOTE1_CONNECT:
return IDM_CONNECT_WIIMOTE1;
case HK_WIIMOTE2_CONNECT:
return IDM_CONNECT_WIIMOTE2;
case HK_WIIMOTE3_CONNECT:
return IDM_CONNECT_WIIMOTE3;
case HK_WIIMOTE4_CONNECT:
return IDM_CONNECT_WIIMOTE4;
case HK_BALANCEBOARD_CONNECT:
return IDM_CONNECT_BALANCEBOARD;
case HK_LOAD_STATE_SLOT_1:
return IDM_LOAD_SLOT_1;
case HK_LOAD_STATE_SLOT_2:
return IDM_LOAD_SLOT_2;
case HK_LOAD_STATE_SLOT_3:
return IDM_LOAD_SLOT_3;
case HK_LOAD_STATE_SLOT_4:
return IDM_LOAD_SLOT_4;
case HK_LOAD_STATE_SLOT_5:
return IDM_LOAD_SLOT_5;
case HK_LOAD_STATE_SLOT_6:
return IDM_LOAD_SLOT_6;
case HK_LOAD_STATE_SLOT_7:
return IDM_LOAD_SLOT_7;
case HK_LOAD_STATE_SLOT_8:
return IDM_LOAD_SLOT_8;
case HK_LOAD_STATE_SLOT_9:
return IDM_LOAD_SLOT_9;
case HK_LOAD_STATE_SLOT_10:
return IDM_LOAD_SLOT_10;
case HK_SAVE_STATE_SLOT_1:
return IDM_SAVE_SLOT_1;
case HK_SAVE_STATE_SLOT_2:
return IDM_SAVE_SLOT_2;
case HK_SAVE_STATE_SLOT_3:
return IDM_SAVE_SLOT_3;
case HK_SAVE_STATE_SLOT_4:
return IDM_SAVE_SLOT_4;
case HK_SAVE_STATE_SLOT_5:
return IDM_SAVE_SLOT_5;
case HK_SAVE_STATE_SLOT_6:
return IDM_SAVE_SLOT_6;
case HK_SAVE_STATE_SLOT_7:
return IDM_SAVE_SLOT_7;
case HK_SAVE_STATE_SLOT_8:
return IDM_SAVE_SLOT_8;
case HK_SAVE_STATE_SLOT_9:
return IDM_SAVE_SLOT_9;
case HK_SAVE_STATE_SLOT_10:
return IDM_SAVE_SLOT_10;
case HK_LOAD_LAST_STATE_1:
return IDM_LOAD_LAST_1;
case HK_LOAD_LAST_STATE_2:
return IDM_LOAD_LAST_2;
case HK_LOAD_LAST_STATE_3:
return IDM_LOAD_LAST_3;
case HK_LOAD_LAST_STATE_4:
return IDM_LOAD_LAST_4;
case HK_LOAD_LAST_STATE_5:
return IDM_LOAD_LAST_5;
case HK_LOAD_LAST_STATE_6:
return IDM_LOAD_LAST_6;
case HK_LOAD_LAST_STATE_7:
return IDM_LOAD_LAST_7;
case HK_LOAD_LAST_STATE_8:
return IDM_LOAD_LAST_8;
case HK_LOAD_LAST_STATE_9:
return IDM_LOAD_LAST_9;
case HK_LOAD_LAST_STATE_10:
return IDM_LOAD_LAST_10;
case HK_SAVE_FIRST_STATE:
return IDM_SAVE_FIRST_STATE;
case HK_UNDO_LOAD_STATE:
return IDM_UNDO_LOAD_STATE;
case HK_UNDO_SAVE_STATE:
return IDM_UNDO_SAVE_STATE;
case HK_LOAD_STATE_FILE:
return IDM_LOAD_STATE_FILE;
case HK_SAVE_STATE_FILE:
return IDM_SAVE_STATE_FILE;
case HK_SELECT_STATE_SLOT_1:
return IDM_SELECT_SLOT_1;
case HK_SELECT_STATE_SLOT_2:
return IDM_SELECT_SLOT_2;
case HK_SELECT_STATE_SLOT_3:
return IDM_SELECT_SLOT_3;
case HK_SELECT_STATE_SLOT_4:
return IDM_SELECT_SLOT_4;
case HK_SELECT_STATE_SLOT_5:
return IDM_SELECT_SLOT_5;
case HK_SELECT_STATE_SLOT_6:
return IDM_SELECT_SLOT_6;
case HK_SELECT_STATE_SLOT_7:
return IDM_SELECT_SLOT_7;
case HK_SELECT_STATE_SLOT_8:
return IDM_SELECT_SLOT_8;
case HK_SELECT_STATE_SLOT_9:
return IDM_SELECT_SLOT_9;
case HK_SELECT_STATE_SLOT_10:
return IDM_SELECT_SLOT_10;
case HK_SAVE_STATE_SLOT_SELECTED:
return IDM_SAVE_SELECTED_SLOT;
case HK_LOAD_STATE_SLOT_SELECTED:
return IDM_LOAD_SELECTED_SLOT;
case HK_FREELOOK_DECREASE_SPEED:
return IDM_FREELOOK_DECREASE_SPEED;
case HK_FREELOOK_INCREASE_SPEED:
return IDM_FREELOOK_INCREASE_SPEED;
case HK_FREELOOK_RESET_SPEED:
return IDM_FREELOOK_RESET_SPEED;
case HK_FREELOOK_LEFT:
return IDM_FREELOOK_LEFT;
case HK_FREELOOK_RIGHT:
return IDM_FREELOOK_RIGHT;
case HK_FREELOOK_UP:
return IDM_FREELOOK_UP;
case HK_FREELOOK_DOWN:
return IDM_FREELOOK_DOWN;
case HK_FREELOOK_ZOOM_IN:
return IDM_FREELOOK_ZOOM_IN;
case HK_FREELOOK_ZOOM_OUT:
return IDM_FREELOOK_ZOOM_OUT;
case HK_FREELOOK_RESET:
return IDM_FREELOOK_RESET;
}
return -1;
}
void CFrame::OnKeyDown(wxKeyEvent& event)
{
#ifdef __APPLE__
// On OS X, we claim all keyboard events while
// emulation is running to avoid wxWidgets sounding
// the system beep for unhandled key events when
// receiving pad/Wiimote keypresses which take an
// entirely different path through the HID subsystem.
if (!m_renderer_has_focus)
{
// We do however want to pass events on when the
// render window is out of focus: this allows use
// of the keyboard in the rest of the UI.
event.Skip();
}
#else
// On other platforms, we leave the key event alone
// so it can be passed on to the windowing system.
event.Skip();
#endif
}
void CFrame::OnMouse(wxMouseEvent& event)
{
// next handlers are all for FreeLook, so we don't need to check them if disabled
if (!g_Config.bFreeLook)
{
event.Skip();
return;
}
// Free look variables
static bool mouseLookEnabled = false;
static bool mouseMoveEnabled = false;
static float lastMouse[2];
if (event.MiddleDown())
{
lastMouse[0] = event.GetX();
lastMouse[1] = event.GetY();
mouseMoveEnabled = true;
}
else if (event.RightDown())
{
lastMouse[0] = event.GetX();
lastMouse[1] = event.GetY();
mouseLookEnabled = true;
}
else if (event.MiddleUp())
{
mouseMoveEnabled = false;
}
else if (event.RightUp())
{
mouseLookEnabled = false;
}
// no button, so it's a move event
else if (event.GetButton() == wxMOUSE_BTN_NONE)
{
if (mouseLookEnabled)
{
VertexShaderManager::RotateView((event.GetX() - lastMouse[0]) / 200.0f,
(event.GetY() - lastMouse[1]) / 200.0f);
lastMouse[0] = event.GetX();
lastMouse[1] = event.GetY();
}
if (mouseMoveEnabled)
{
VertexShaderManager::TranslateView((event.GetX() - lastMouse[0]) / 50.0f,
(event.GetY() - lastMouse[1]) / 50.0f);
lastMouse[0] = event.GetX();
lastMouse[1] = event.GetY();
}
}
event.Skip();
}
void CFrame::DoFullscreen(bool enable_fullscreen)
{
ToggleDisplayMode(enable_fullscreen);
if (SConfig::GetInstance().bRenderToMain)
{
m_render_frame->ShowFullScreen(enable_fullscreen, wxFULLSCREEN_ALL);
if (enable_fullscreen)
{
// Save the current mode before going to fullscreen
m_aui_current_perspective = m_mgr->SavePerspective();
m_mgr->LoadPerspective(m_aui_fullscreen_perspective, true);
// Hide toolbar
DoToggleToolbar(false);
// Hide menubar (by having wxwidgets delete it)
SetMenuBar(nullptr);
// Hide the statusbar if enabled
if (GetStatusBar()->IsShown())
{
GetStatusBar()->Hide();
this->SendSizeEvent();
}
}
else
{
// Restore saved perspective
m_mgr->LoadPerspective(m_aui_current_perspective, true);
// Restore toolbar to the status it was at before going fullscreen.
DoToggleToolbar(SConfig::GetInstance().m_InterfaceToolbar);
// Recreate the menubar if needed.
if (wxFrame::GetMenuBar() == nullptr)
{
SetMenuBar(CreateMenuBar());
}
// Show statusbar if enabled
if (SConfig::GetInstance().m_InterfaceStatusbar)
{
GetStatusBar()->Show();
this->SendSizeEvent();
}
}
}
else if (g_ActiveConfig.ExclusiveFullscreenEnabled())
{
if (!enable_fullscreen)
DoExclusiveFullscreen(false);
m_render_frame->ShowFullScreen(enable_fullscreen, wxFULLSCREEN_ALL);
m_render_frame->Raise();
if (enable_fullscreen)
DoExclusiveFullscreen(true);
}
else
{
m_render_frame->ShowFullScreen(enable_fullscreen, wxFULLSCREEN_ALL);
m_render_frame->Raise();
}
}
void CFrame::DoExclusiveFullscreen(bool enable_fullscreen)
{
if (!g_renderer || g_renderer->IsFullscreen() == enable_fullscreen)
return;
Core::RunAsCPUThread([enable_fullscreen] { g_renderer->SetFullscreen(enable_fullscreen); });
}
void CFrame::PollHotkeys(wxTimerEvent& event)
{
if (!HotkeyManagerEmu::IsEnabled())
return;
if (Core::GetState() == Core::State::Uninitialized || Core::GetState() == Core::State::Paused)
g_controller_interface.UpdateInput();
if (Core::GetState() != Core::State::Stopping)
{
HotkeyManagerEmu::GetStatus();
ParseHotkeys();
}
}
void CFrame::ParseHotkeys()
{
for (int i = 0; i < NUM_HOTKEYS; i++)
{
switch (i)
{
case HK_OPEN:
case HK_CHANGE_DISC:
case HK_REFRESH_LIST:
case HK_RESET:
case HK_START_RECORDING:
case HK_PLAY_RECORDING:
case HK_EXPORT_RECORDING:
case HK_READ_ONLY_MODE:
case HK_LOAD_STATE_FILE:
case HK_SAVE_STATE_FILE:
case HK_LOAD_STATE_SLOT_SELECTED:
if (IsHotkey(i))
{
const int id = GetMenuIDFromHotkey(i);
if (id >= 0)
{
wxCommandEvent evt(wxEVT_MENU, id);
wxMenuItem* item = GetMenuBar()->FindItem(id);
if (item && item->IsCheckable())
{
item->wxMenuItemBase::Toggle();
evt.SetInt(item->IsChecked());
}
GetEventHandler()->AddPendingEvent(evt);
}
}
default:
break;
// do nothing
}
}
if (!Core::IsRunningAndStarted())
{
return;
}
// Toggle fullscreen
if (IsHotkey(HK_FULLSCREEN))
DoFullscreen(!RendererIsFullscreen());
// Pause and Unpause
if (IsHotkey(HK_PLAY_PAUSE))
DoPause();
// Frame advance
HandleFrameSkipHotkeys();
// Stop
if (IsHotkey(HK_STOP))
DoStop();
// Screenshot hotkey
if (IsHotkey(HK_SCREENSHOT))
Core::SaveScreenShot();
if (IsHotkey(HK_EXIT))
wxPostEvent(this, wxCommandEvent(wxEVT_MENU, wxID_EXIT));
if (IsHotkey(HK_VOLUME_DOWN))
AudioCommon::DecreaseVolume(3);
if (IsHotkey(HK_VOLUME_UP))
AudioCommon::IncreaseVolume(3);
if (IsHotkey(HK_VOLUME_TOGGLE_MUTE))
AudioCommon::ToggleMuteVolume();
if (SConfig::GetInstance().m_bt_passthrough_enabled)
{
const auto ios = IOS::HLE::GetIOS();
auto device = ios ? ios->GetDeviceByName("/dev/usb/oh1/57e/305") : nullptr;
if (device != nullptr)
std::static_pointer_cast<IOS::HLE::Device::BluetoothBase>(device)->UpdateSyncButtonState(
IsHotkey(HK_TRIGGER_SYNC_BUTTON, true));
}
if (m_use_debugger)
{
if (IsHotkey(HK_STEP))
{
wxCommandEvent evt(wxEVT_MENU, IDM_STEP);
GetEventHandler()->AddPendingEvent(evt);
}
if (IsHotkey(HK_STEP_OVER))
{
wxCommandEvent evt(wxEVT_MENU, IDM_STEPOVER);
GetEventHandler()->AddPendingEvent(evt);
}
if (IsHotkey(HK_STEP_OUT))
{
wxCommandEvent evt(wxEVT_MENU, IDM_STEPOUT);
GetEventHandler()->AddPendingEvent(evt);
}
if (IsHotkey(HK_SKIP))
{
wxCommandEvent evt(wxEVT_MENU, IDM_SKIP);
GetEventHandler()->AddPendingEvent(evt);
}
if (IsHotkey(HK_SHOW_PC))
{
wxCommandEvent evt(wxEVT_MENU, IDM_GOTOPC);
GetEventHandler()->AddPendingEvent(evt);
}
if (IsHotkey(HK_SET_PC))
{
wxCommandEvent evt(wxEVT_MENU, IDM_SETPC);
GetEventHandler()->AddPendingEvent(evt);
}
if (IsHotkey(HK_BP_TOGGLE))
{
wxCommandEvent evt(wxEVT_MENU, IDM_TOGGLE_BREAKPOINT);
GetEventHandler()->AddPendingEvent(evt);
}
if (IsHotkey(HK_BP_ADD))
{
BreakPointDlg bpDlg(this);
if (bpDlg.ShowModal() == wxID_OK)
{
wxCommandEvent evt(wxEVT_HOST_COMMAND, IDM_UPDATE_BREAKPOINTS);
m_code_window->GetEventHandler()->AddPendingEvent(evt);
}
}
if (IsHotkey(HK_MBP_ADD))
{
MemoryCheckDlg memDlg(this);
if (memDlg.ShowModal() == wxID_OK)
{
wxCommandEvent evt(wxEVT_HOST_COMMAND, IDM_UPDATE_BREAKPOINTS);
m_code_window->GetEventHandler()->AddPendingEvent(evt);
}
}
}
// Wiimote connect and disconnect hotkeys
int WiimoteId = -1;
if (IsHotkey(HK_WIIMOTE1_CONNECT))
WiimoteId = 0;
if (IsHotkey(HK_WIIMOTE2_CONNECT))
WiimoteId = 1;
if (IsHotkey(HK_WIIMOTE3_CONNECT))
WiimoteId = 2;
if (IsHotkey(HK_WIIMOTE4_CONNECT))
WiimoteId = 3;
if (IsHotkey(HK_BALANCEBOARD_CONNECT))
WiimoteId = 4;
// Actually perform the Wiimote connection or disconnection
if (WiimoteId >= 0 && SConfig::GetInstance().bWii)
{
wxCommandEvent evt;
evt.SetId(IDM_CONNECT_WIIMOTE1 + WiimoteId);
OnConnectWiimote(evt);
}
if (IsHotkey(HK_INCREASE_IR))
{
OSDChoice = 1;
Config::SetCurrent(Config::GFX_EFB_SCALE, Config::Get(Config::GFX_EFB_SCALE) + 1);
}
if (IsHotkey(HK_DECREASE_IR))
{
OSDChoice = 1;
if (Config::Get(Config::GFX_EFB_SCALE) > SCALE_AUTO)
Config::SetCurrent(Config::GFX_EFB_SCALE, Config::Get(Config::GFX_EFB_SCALE) - 1);
}
if (IsHotkey(HK_TOGGLE_CROP))
{
Config::SetCurrent(Config::GFX_CROP, !Config::Get(Config::GFX_CROP));
}
if (IsHotkey(HK_TOGGLE_AR))
{
OSDChoice = 2;
// Toggle aspect ratio
int aspect_ratio = Config::Get(Config::GFX_ASPECT_RATIO);
aspect_ratio = (aspect_ratio + 1) & 3;
Config::SetCurrent(Config::GFX_ASPECT_RATIO, aspect_ratio);
}
if (IsHotkey(HK_TOGGLE_EFBCOPIES))
{
OSDChoice = 3;
// Toggle EFB copies between EFB2RAM and EFB2Texture
Config::SetCurrent(Config::GFX_HACK_SKIP_EFB_COPY_TO_RAM,
!Config::Get(Config::GFX_HACK_SKIP_EFB_COPY_TO_RAM));
}
if (IsHotkey(HK_TOGGLE_FOG))
{
OSDChoice = 4;
Config::SetCurrent(Config::GFX_DISABLE_FOG, !Config::Get(Config::GFX_DISABLE_FOG));
}
if (IsHotkey(HK_TOGGLE_DUMPTEXTURES))
{
Config::SetCurrent(Config::GFX_DUMP_TEXTURES, !Config::Get(Config::GFX_DUMP_TEXTURES));
}
if (IsHotkey(HK_TOGGLE_TEXTURES))
{
Config::SetCurrent(Config::GFX_HIRES_TEXTURES, !Config::Get(Config::GFX_HIRES_TEXTURES));
}
Core::SetIsThrottlerTempDisabled(IsHotkey(HK_TOGGLE_THROTTLE, true));
if (IsHotkey(HK_DECREASE_EMULATION_SPEED))
{
OSDChoice = 5;
if (SConfig::GetInstance().m_EmulationSpeed <= 0.0f)
SConfig::GetInstance().m_EmulationSpeed = 1.0f;
else if (SConfig::GetInstance().m_EmulationSpeed >= 0.2f)
SConfig::GetInstance().m_EmulationSpeed -= 0.1f;
else
SConfig::GetInstance().m_EmulationSpeed = 0.1f;
if (SConfig::GetInstance().m_EmulationSpeed >= 0.95f &&
SConfig::GetInstance().m_EmulationSpeed <= 1.05f)
SConfig::GetInstance().m_EmulationSpeed = 1.0f;
}
if (IsHotkey(HK_INCREASE_EMULATION_SPEED))
{
OSDChoice = 5;
if (SConfig::GetInstance().m_EmulationSpeed > 0.0f)
SConfig::GetInstance().m_EmulationSpeed += 0.1f;
if (SConfig::GetInstance().m_EmulationSpeed >= 0.95f &&
SConfig::GetInstance().m_EmulationSpeed <= 1.05f)
SConfig::GetInstance().m_EmulationSpeed = 1.0f;
}
if (IsHotkey(HK_SAVE_STATE_SLOT_SELECTED))
{
State::Save(m_save_slot);
}
if (IsHotkey(HK_LOAD_STATE_SLOT_SELECTED))
{
State::Load(m_save_slot);
}
if (IsHotkey(HK_TOGGLE_STEREO_SBS))
{
if (g_Config.iStereoMode != STEREO_SBS)
{
// Current implementation of anaglyph stereoscopy uses a
// post-processing shader. Thus the shader needs to be to be
// turned off when selecting other stereoscopy modes.
if (g_Config.sPostProcessingShader == "dubois")
{
Config::SetCurrent(Config::GFX_ENHANCE_POST_SHADER, std::string(""));
}
Config::SetCurrent(Config::GFX_STEREO_MODE, static_cast<int>(STEREO_SBS));
}
else
{
Config::SetCurrent(Config::GFX_STEREO_MODE, static_cast<int>(STEREO_OFF));
}
}
if (IsHotkey(HK_TOGGLE_STEREO_TAB))
{
if (g_Config.iStereoMode != STEREO_TAB)
{
if (g_Config.sPostProcessingShader == "dubois")
{
Config::SetCurrent(Config::GFX_ENHANCE_POST_SHADER, std::string(""));
}
Config::SetCurrent(Config::GFX_STEREO_MODE, static_cast<int>(STEREO_TAB));
}
else
{
Config::SetCurrent(Config::GFX_STEREO_MODE, static_cast<int>(STEREO_OFF));
}
}
if (IsHotkey(HK_TOGGLE_STEREO_ANAGLYPH))
{
if (g_Config.iStereoMode != STEREO_ANAGLYPH)
{
// Setting the anaglyph mode also requires a specific
// post-processing shader to be activated.
Config::SetCurrent(Config::GFX_STEREO_MODE, static_cast<int>(STEREO_ANAGLYPH));
Config::SetCurrent(Config::GFX_ENHANCE_POST_SHADER, std::string("dubois"));
}
else
{
Config::SetCurrent(Config::GFX_STEREO_MODE, static_cast<int>(STEREO_OFF));
Config::SetCurrent(Config::GFX_ENHANCE_POST_SHADER, std::string(""));
}
}
if (IsHotkey(HK_TOGGLE_STEREO_3DVISION))
{
if (g_Config.iStereoMode != STEREO_3DVISION)
{
if (g_Config.sPostProcessingShader == "dubois")
{
Config::SetCurrent(Config::GFX_ENHANCE_POST_SHADER, std::string(""));
}
Config::SetCurrent(Config::GFX_STEREO_MODE, static_cast<int>(STEREO_3DVISION));
}
else
{
Config::SetCurrent(Config::GFX_STEREO_MODE, static_cast<int>(STEREO_OFF));
}
}
if (IsHotkey(HK_DECREASE_DEPTH, true))
{
if (g_Config.iStereoDepth > 0)
Config::SetCurrent(Config::GFX_STEREO_DEPTH, g_Config.iStereoDepth - 1);
}
if (IsHotkey(HK_INCREASE_DEPTH, true))
{
if (g_Config.iStereoDepth < 100)
Config::SetCurrent(Config::GFX_STEREO_DEPTH, g_Config.iStereoDepth + 1);
}
if (IsHotkey(HK_DECREASE_CONVERGENCE, true))
{
int convergence = std::max(0, g_Config.iStereoConvergence - 5);
Config::SetCurrent(Config::GFX_STEREO_CONVERGENCE, convergence);
}
if (IsHotkey(HK_INCREASE_CONVERGENCE, true))
{
int convergence = std::min(500, g_Config.iStereoConvergence + 5);
Config::SetCurrent(Config::GFX_STEREO_CONVERGENCE, convergence);
}
static float debugSpeed = 1.0f;
if (IsHotkey(HK_FREELOOK_DECREASE_SPEED, true))
debugSpeed /= 1.1f;
if (IsHotkey(HK_FREELOOK_INCREASE_SPEED, true))
debugSpeed *= 1.1f;
if (IsHotkey(HK_FREELOOK_RESET_SPEED, true))
debugSpeed = 1.0f;
if (IsHotkey(HK_FREELOOK_UP, true))
VertexShaderManager::TranslateView(0.0f, 0.0f, -debugSpeed);
if (IsHotkey(HK_FREELOOK_DOWN, true))
VertexShaderManager::TranslateView(0.0f, 0.0f, debugSpeed);
if (IsHotkey(HK_FREELOOK_LEFT, true))
VertexShaderManager::TranslateView(debugSpeed, 0.0f);
if (IsHotkey(HK_FREELOOK_RIGHT, true))
VertexShaderManager::TranslateView(-debugSpeed, 0.0f);
if (IsHotkey(HK_FREELOOK_ZOOM_IN, true))
VertexShaderManager::TranslateView(0.0f, debugSpeed);
if (IsHotkey(HK_FREELOOK_ZOOM_OUT, true))
VertexShaderManager::TranslateView(0.0f, -debugSpeed);
if (IsHotkey(HK_FREELOOK_RESET, true))
VertexShaderManager::ResetView();
// Savestates
for (u32 i = 0; i < State::NUM_STATES; i++)
{
if (IsHotkey(HK_LOAD_STATE_SLOT_1 + i))
State::Load(1 + i);
if (IsHotkey(HK_SAVE_STATE_SLOT_1 + i))
State::Save(1 + i);
if (IsHotkey(HK_LOAD_LAST_STATE_1 + i))
State::LoadLastSaved(1 + i);
if (IsHotkey(HK_SELECT_STATE_SLOT_1 + i))
{
wxCommandEvent slot_event;
slot_event.SetId(IDM_SELECT_SLOT_1 + i);
CFrame::OnSelectSlot(slot_event);
}
}
if (IsHotkey(HK_SAVE_FIRST_STATE))
State::SaveFirstSaved();
if (IsHotkey(HK_UNDO_LOAD_STATE))
State::UndoLoadState();
if (IsHotkey(HK_UNDO_SAVE_STATE))
State::UndoSaveState();
}
void CFrame::HandleFrameSkipHotkeys()
{
static const int MAX_FRAME_SKIP_DELAY = 60;
static int frameStepCount = 0;
static const int FRAME_STEP_DELAY = 30;
static int holdFrameStepDelay = 1;
static int holdFrameStepDelayCount = 0;
static bool holdFrameStep = false;
if (IsHotkey(HK_FRAME_ADVANCE_DECREASE_SPEED))
{
++holdFrameStepDelay;
if (holdFrameStepDelay > MAX_FRAME_SKIP_DELAY)
holdFrameStepDelay = MAX_FRAME_SKIP_DELAY;
}
else if (IsHotkey(HK_FRAME_ADVANCE_INCREASE_SPEED))
{
--holdFrameStepDelay;
if (holdFrameStepDelay < 0)
holdFrameStepDelay = 0;
}
else if (IsHotkey(HK_FRAME_ADVANCE_RESET_SPEED))
{
holdFrameStepDelay = 1;
}
else if (IsHotkey(HK_FRAME_ADVANCE, true))
{
if (holdFrameStepDelayCount < holdFrameStepDelay && holdFrameStep)
++holdFrameStepDelayCount;
if ((frameStepCount == 0 || frameStepCount == FRAME_STEP_DELAY) && !holdFrameStep)
{
wxCommandEvent evt;
evt.SetId(IDM_FRAMESTEP);
CFrame::OnFrameStep(evt);
if (holdFrameStepDelay > 0)
holdFrameStep = true;
}
if (frameStepCount < FRAME_STEP_DELAY)
{
++frameStepCount;
if (holdFrameStep)
holdFrameStep = false;
}
if (frameStepCount == FRAME_STEP_DELAY && holdFrameStep &&
holdFrameStepDelayCount >= holdFrameStepDelay)
{
holdFrameStep = false;
holdFrameStepDelayCount = 0;
}
}
else if (frameStepCount > 0)
{
// Reset values of frame advance to default
frameStepCount = 0;
holdFrameStep = false;
holdFrameStepDelayCount = 0;
}
}
void CFrame::HandleSignal(wxTimerEvent& event)
{
if (!s_shutdown_signal_received.TestAndClear())
return;
m_is_closing = true;
Close();
}