dolphin/Source/Core/DolphinWX/Main.cpp
comex ba664b3293 Join the emu thread in Core::Stop. Get rid of Core::Shutdown which did that before.
Core::Shutdown was only called on app exit, yet the emu thread exits
whenever emulation stops; if you launched a new game it would just join
via the destructor when s_emu_thread was set to a new thread.
(Incidentally, the destructor also makes explicitly joining on app exit
rather pointless.)

Because the GUI thread wasn't waiting for the CPU thread to fully shut
down, Core::IsRunning would remain true briefly after CFrame::DoStop
which, given Dolphin's penchant for accessing variables belonging to
other threads, can only mean trouble... In my case, because the previous
commit caused UpdateGUI, which is called at the end of DoStop, to call
PauseAndLock, which checks IsRunning, pressing stop at the right time
would cause strange behavior.
2015-04-13 02:17:38 -04:00

561 lines
14 KiB
C++

// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#include <cstdio>
#include <cstring>
#include <mutex>
#include <string>
#include <utility>
#include <wx/app.h>
#include <wx/buffer.h>
#include <wx/chartype.h>
#include <wx/cmdline.h>
#include <wx/defs.h>
#include <wx/event.h>
#include <wx/gdicmn.h>
#include <wx/image.h>
#include <wx/imagpng.h>
#include <wx/intl.h>
#include <wx/language.h>
#include <wx/msgdlg.h>
#include <wx/setup.h>
#include <wx/string.h>
#include <wx/thread.h>
#include <wx/timer.h>
#include <wx/translation.h>
#include <wx/utils.h>
#include <wx/window.h>
#include "Common/CommonPaths.h"
#include "Common/CommonTypes.h"
#include "Common/CPUDetect.h"
#include "Common/FileUtil.h"
#include "Common/IniFile.h"
#include "Common/Thread.h"
#include "Common/Logging/LogManager.h"
#include "Core/ConfigManager.h"
#include "Core/Core.h"
#include "Core/CoreParameter.h"
#include "Core/Host.h"
#include "Core/Movie.h"
#include "Core/HW/Wiimote.h"
#include "DolphinWX/Frame.h"
#include "DolphinWX/Globals.h"
#include "DolphinWX/Main.h"
#include "DolphinWX/SoftwareVideoConfigDialog.h"
#include "DolphinWX/VideoConfigDiag.h"
#include "DolphinWX/WxUtils.h"
#include "DolphinWX/Debugger/CodeWindow.h"
#include "DolphinWX/Debugger/JitWindow.h"
#include "UICommon/UICommon.h"
#include "VideoCommon/VideoBackendBase.h"
#if defined HAVE_X11 && HAVE_X11
#include <X11/Xlib.h>
#endif
#ifdef _WIN32
#ifndef SM_XVIRTUALSCREEN
#define SM_XVIRTUALSCREEN 76
#endif
#ifndef SM_YVIRTUALSCREEN
#define SM_YVIRTUALSCREEN 77
#endif
#ifndef SM_CXVIRTUALSCREEN
#define SM_CXVIRTUALSCREEN 78
#endif
#ifndef SM_CYVIRTUALSCREEN
#define SM_CYVIRTUALSCREEN 79
#endif
#endif
#ifdef __APPLE__
#import <AppKit/AppKit.h>
#endif
class wxFrame;
// ------------
// Main window
IMPLEMENT_APP(DolphinApp)
bool wxMsgAlert(const char*, const char*, bool, int);
std::string wxStringTranslator(const char *);
CFrame* main_frame = nullptr;
bool DolphinApp::Initialize(int& c, wxChar **v)
{
#if defined HAVE_X11 && HAVE_X11
XInitThreads();
#endif
return wxApp::Initialize(c, v);
}
// The 'main program' equivalent that creates the main window and return the main frame
bool DolphinApp::OnInit()
{
Bind(wxEVT_QUERY_END_SESSION, &DolphinApp::OnEndSession, this);
Bind(wxEVT_END_SESSION, &DolphinApp::OnEndSession, this);
// Declarations and definitions
bool UseDebugger = false;
bool UseLogger = false;
bool selectVideoBackend = false;
bool selectAudioEmulation = false;
bool selectPerfDir = false;
wxString videoBackendName;
wxString audioEmulationName;
wxString userPath;
wxString perfDir;
#if wxUSE_CMDLINE_PARSER // Parse command lines
wxCmdLineEntryDesc cmdLineDesc[] =
{
{
wxCMD_LINE_SWITCH, "h", "help",
"Show this help message",
wxCMD_LINE_VAL_NONE, wxCMD_LINE_OPTION_HELP
},
{
wxCMD_LINE_SWITCH, "d", "debugger",
"Opens the debugger",
wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL
},
{
wxCMD_LINE_SWITCH, "l", "logger",
"Opens the logger",
wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL
},
{
wxCMD_LINE_OPTION, "e", "exec",
"Loads the specified file (ELF, DOL, GCM, ISO, WBFS, CISO, GCZ, WAD)",
wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL
},
{
wxCMD_LINE_SWITCH, "b", "batch",
"Exit Dolphin with emulator",
wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL
},
{
wxCMD_LINE_OPTION, "V", "video_backend",
"Specify a video backend",
wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL
},
{
wxCMD_LINE_OPTION, "A", "audio_emulation",
"Low level (LLE) or high level (HLE) audio",
wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL
},
{
wxCMD_LINE_OPTION, "m", "movie",
"Play a movie file",
wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL
},
{
wxCMD_LINE_OPTION, "U", "user",
"User folder path",
wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL
},
{
wxCMD_LINE_OPTION, "P", "perf_dir",
"Directory for Linux perf perf-$pid.map file",
wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL
},
{
wxCMD_LINE_NONE, nullptr, nullptr, nullptr, wxCMD_LINE_VAL_NONE, 0
}
};
// Gets the command line parameters
wxCmdLineParser parser(cmdLineDesc, argc, argv);
LoadFile = false;
if (argc == 2 && File::Exists(argv[1].ToUTF8().data()))
{
LoadFile = true;
FileToLoad = argv[1];
}
else if (parser.Parse() != 0)
{
return false;
}
UseDebugger = parser.Found("debugger");
UseLogger = parser.Found("logger");
if (!LoadFile)
LoadFile = parser.Found("exec", &FileToLoad);
BatchMode = parser.Found("batch");
selectVideoBackend = parser.Found("video_backend", &videoBackendName);
selectAudioEmulation = parser.Found("audio_emulation", &audioEmulationName);
selectPerfDir = parser.Found("perf_dir", &perfDir);
playMovie = parser.Found("movie", &movieFile);
parser.Found("user", &userPath);
#endif // wxUSE_CMDLINE_PARSER
// Register message box and translation handlers
RegisterMsgAlertHandler(&wxMsgAlert);
RegisterStringTranslator(&wxStringTranslator);
#if wxUSE_ON_FATAL_EXCEPTION
wxHandleFatalExceptions(true);
#endif
UICommon::SetUserDirectory(userPath.ToStdString());
UICommon::CreateDirectories();
InitLanguageSupport(); // The language setting is loaded from the user directory
UICommon::Init();
if (selectPerfDir)
{
SConfig::GetInstance().m_LocalCoreStartupParameter.m_perfDir =
WxStrToStr(perfDir);
}
if (selectVideoBackend && videoBackendName != wxEmptyString)
SConfig::GetInstance().m_LocalCoreStartupParameter.m_strVideoBackend =
WxStrToStr(videoBackendName);
if (selectAudioEmulation)
{
if (audioEmulationName == "HLE")
SConfig::GetInstance().m_LocalCoreStartupParameter.bDSPHLE = true;
else if (audioEmulationName == "LLE")
SConfig::GetInstance().m_LocalCoreStartupParameter.bDSPHLE = false;
}
VideoBackend::ActivateBackend(SConfig::GetInstance().m_LocalCoreStartupParameter.m_strVideoBackend);
// Enable the PNG image handler for screenshots
wxImage::AddHandler(new wxPNGHandler);
int x = SConfig::GetInstance().m_LocalCoreStartupParameter.iPosX;
int y = SConfig::GetInstance().m_LocalCoreStartupParameter.iPosY;
int w = SConfig::GetInstance().m_LocalCoreStartupParameter.iWidth;
int h = SConfig::GetInstance().m_LocalCoreStartupParameter.iHeight;
if (File::Exists("www.dolphin-emulator.com.txt"))
{
File::Delete("www.dolphin-emulator.com.txt");
wxMessageDialog dlg(nullptr, _(
"This version of Dolphin was downloaded from a website stealing money from developers of the emulator. Please "
"download Dolphin from the official website instead: https://dolphin-emu.org/"),
_("Unofficial version detected"), wxOK | wxICON_WARNING);
dlg.ShowModal();
wxLaunchDefaultBrowser("https://dolphin-emu.org/?ref=badver");
exit(0);
}
// The following is not needed with X11, where window managers
// do not allow windows to be created off the desktop.
#ifdef _WIN32
// Out of desktop check
int leftPos = GetSystemMetrics(SM_XVIRTUALSCREEN);
int topPos = GetSystemMetrics(SM_YVIRTUALSCREEN);
int width = GetSystemMetrics(SM_CXVIRTUALSCREEN);
int height = GetSystemMetrics(SM_CYVIRTUALSCREEN);
if ((leftPos + width) < (x + w) || leftPos > x || (topPos + height) < (y + h) || topPos > y)
x = y = wxDefaultCoord;
#elif defined __APPLE__
if (y < 1)
y = wxDefaultCoord;
#endif
main_frame = new CFrame(nullptr, wxID_ANY,
StrToWxStr(scm_rev_str),
wxPoint(x, y), wxSize(w, h),
UseDebugger, BatchMode, UseLogger);
SetTopWindow(main_frame);
main_frame->SetMinSize(wxSize(400, 300));
AfterInit();
return true;
}
void DolphinApp::MacOpenFile(const wxString &fileName)
{
FileToLoad = fileName;
LoadFile = true;
main_frame->BootGame(WxStrToStr(FileToLoad));
}
void DolphinApp::AfterInit()
{
if (!BatchMode)
main_frame->UpdateGameList();
if (playMovie && movieFile != wxEmptyString)
{
if (Movie::PlayInput(WxStrToStr(movieFile)))
{
if (LoadFile && FileToLoad != wxEmptyString)
{
main_frame->BootGame(WxStrToStr(FileToLoad));
}
else
{
main_frame->BootGame("");
}
}
}
// First check if we have an exec command line.
else if (LoadFile && FileToLoad != wxEmptyString)
{
main_frame->BootGame(WxStrToStr(FileToLoad));
}
// If we have selected Automatic Start, start the default ISO,
// or if no default ISO exists, start the last loaded ISO
else if (main_frame->g_pCodeWindow)
{
if (main_frame->g_pCodeWindow->AutomaticStart())
{
main_frame->BootGame("");
}
}
}
void DolphinApp::InitLanguageSupport()
{
unsigned int language = 0;
IniFile ini;
ini.Load(File::GetUserPath(F_DOLPHINCONFIG_IDX));
ini.GetOrCreateSection("Interface")->Get("Language", &language, wxLANGUAGE_DEFAULT);
// Load language if possible, fall back to system default otherwise
if (wxLocale::IsAvailable(language))
{
m_locale = new wxLocale(language);
#ifdef _WIN32
m_locale->AddCatalogLookupPathPrefix(StrToWxStr(File::GetExeDirectory() + DIR_SEP "Languages"));
#endif
m_locale->AddCatalog("dolphin-emu");
if (!m_locale->IsOk())
{
wxMessageBox(_("Error loading selected language. Falling back to system default."), _("Error"));
delete m_locale;
m_locale = new wxLocale(wxLANGUAGE_DEFAULT);
}
}
else
{
wxMessageBox(_("The selected language is not supported by your system. Falling back to system default."), _("Error"));
m_locale = new wxLocale(wxLANGUAGE_DEFAULT);
}
}
void DolphinApp::OnEndSession(wxCloseEvent& event)
{
// Close if we've received wxEVT_END_SESSION (ignore wxEVT_QUERY_END_SESSION)
if (!event.CanVeto())
{
main_frame->Close(true);
}
}
int DolphinApp::OnExit()
{
UICommon::Shutdown();
delete m_locale;
return wxApp::OnExit();
}
void DolphinApp::OnFatalException()
{
WiimoteReal::Shutdown();
}
// ------------
// Talk to GUI
bool wxMsgAlert(const char* caption, const char* text, bool yes_no, int /*Style*/)
{
#ifdef __WXGTK__
if (wxIsMainThread())
#endif
return wxYES == wxMessageBox(StrToWxStr(text), StrToWxStr(caption),
(yes_no) ? wxYES_NO : wxOK, wxWindow::FindFocus());
#ifdef __WXGTK__
else
{
wxCommandEvent event(wxEVT_HOST_COMMAND, IDM_PANIC);
event.SetString(StrToWxStr(caption) + ":" + StrToWxStr(text));
event.SetInt(yes_no);
main_frame->GetEventHandler()->AddPendingEvent(event);
main_frame->panic_event.Wait();
return main_frame->bPanicResult;
}
#endif
}
std::string wxStringTranslator(const char *text)
{
return WxStrToStr(wxGetTranslation(wxString::FromUTF8(text)));
}
// Accessor for the main window class
CFrame* DolphinApp::GetCFrame()
{
return main_frame;
}
void Host_Message(int Id)
{
wxCommandEvent event(wxEVT_HOST_COMMAND, Id);
main_frame->GetEventHandler()->AddPendingEvent(event);
}
void* Host_GetRenderHandle()
{
return main_frame->GetRenderHandle();
}
// OK, this thread boundary is DANGEROUS on Linux
// wxPostEvent / wxAddPendingEvent is the solution.
void Host_NotifyMapLoaded()
{
wxCommandEvent event(wxEVT_HOST_COMMAND, IDM_NOTIFY_MAP_LOADED);
main_frame->GetEventHandler()->AddPendingEvent(event);
if (main_frame->g_pCodeWindow)
{
main_frame->g_pCodeWindow->GetEventHandler()->AddPendingEvent(event);
}
}
void Host_UpdateDisasmDialog()
{
wxCommandEvent event(wxEVT_HOST_COMMAND, IDM_UPDATE_DISASM_DIALOG);
main_frame->GetEventHandler()->AddPendingEvent(event);
if (main_frame->g_pCodeWindow)
{
main_frame->g_pCodeWindow->GetEventHandler()->AddPendingEvent(event);
}
}
void Host_UpdateMainFrame()
{
wxCommandEvent event(wxEVT_HOST_COMMAND, IDM_UPDATE_GUI);
main_frame->GetEventHandler()->AddPendingEvent(event);
if (main_frame->g_pCodeWindow)
{
main_frame->g_pCodeWindow->GetEventHandler()->AddPendingEvent(event);
}
}
void Host_UpdateTitle(const std::string& title)
{
wxCommandEvent event(wxEVT_HOST_COMMAND, IDM_UPDATE_TITLE);
event.SetString(StrToWxStr(title));
main_frame->GetEventHandler()->AddPendingEvent(event);
}
void Host_RequestRenderWindowSize(int width, int height)
{
wxCommandEvent event(wxEVT_HOST_COMMAND, IDM_WINDOW_SIZE_REQUEST);
event.SetClientData(new std::pair<int, int>(width, height));
main_frame->GetEventHandler()->AddPendingEvent(event);
}
void Host_RequestFullscreen(bool enable_fullscreen)
{
wxCommandEvent event(wxEVT_HOST_COMMAND, IDM_FULLSCREEN_REQUEST);
event.SetInt(enable_fullscreen ? 1 : 0);
main_frame->GetEventHandler()->AddPendingEvent(event);
}
void Host_SetStartupDebuggingParameters()
{
SCoreStartupParameter& StartUp = SConfig::GetInstance().m_LocalCoreStartupParameter;
if (main_frame->g_pCodeWindow)
{
StartUp.bBootToPause = main_frame->g_pCodeWindow->BootToPause();
StartUp.bAutomaticStart = main_frame->g_pCodeWindow->AutomaticStart();
StartUp.bJITNoBlockCache = main_frame->g_pCodeWindow->JITNoBlockCache();
StartUp.bJITNoBlockLinking = main_frame->g_pCodeWindow->JITNoBlockLinking();
}
else
{
StartUp.bBootToPause = false;
}
StartUp.bEnableDebugging = main_frame->g_pCodeWindow ? true : false; // RUNNING_DEBUG
}
void Host_SetWiiMoteConnectionState(int _State)
{
static int currentState = -1;
if (_State == currentState)
return;
currentState = _State;
wxCommandEvent event(wxEVT_HOST_COMMAND, IDM_UPDATE_STATUS_BAR);
switch (_State)
{
case 0: event.SetString(_("Not connected")); break;
case 1: event.SetString(_("Connecting...")); break;
case 2: event.SetString(_("Wiimote Connected")); break;
}
// Update field 1 or 2
event.SetInt(1);
NOTICE_LOG(WIIMOTE, "%s", static_cast<const char*>(event.GetString().c_str()));
main_frame->GetEventHandler()->AddPendingEvent(event);
}
bool Host_UIHasFocus()
{
return main_frame->UIHasFocus();
}
bool Host_RendererHasFocus()
{
return main_frame->RendererHasFocus();
}
bool Host_RendererIsFullscreen()
{
return main_frame->RendererIsFullscreen();
}
void Host_ConnectWiimote(int wm_idx, bool connect)
{
CFrame::ConnectWiimote(wm_idx, connect);
}
void Host_ShowVideoConfig(void* parent, const std::string& backend_name,
const std::string& config_name)
{
if (backend_name == "Direct3D" || backend_name == "OpenGL")
{
VideoConfigDiag diag((wxWindow*)parent, backend_name, config_name);
diag.ShowModal();
}
else if (backend_name == "Software Renderer")
{
SoftwareVideoConfigDialog diag((wxWindow*)parent, backend_name, config_name);
diag.ShowModal();
}
}