Fix a segfault when the debugger is used and dolphin exits.
[dolphin.git] / Source / Core / DolphinWX / Src / Main.cpp
blobe4b6d917abe540e359d906d8812c852325e5857a
1 // Copyright (C) 2003 Dolphin Project.
3 // This program is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, version 2.0.
7 // This program is distributed in the hope that it will be useful,
8 // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 // GNU General Public License 2.0 for more details.
12 // A copy of the GPL 2.0 should have been included with the program.
13 // If not, see http://www.gnu.org/licenses/
15 // Official SVN repository and contact information can be found at
16 // http://code.google.com/p/dolphin-emu/
18 #include <vector>
19 #include <string>
21 #include "Common.h" // Common
23 #if defined HAVE_X11 && HAVE_X11
24 #include <X11/Xlib.h>
25 #endif
27 #include "CPUDetect.h"
28 #include "IniFile.h"
29 #include "FileUtil.h"
30 #include "Setup.h"
32 #include "Host.h" // Core
33 #include "PluginManager.h"
35 #include "Globals.h" // Local
36 #include "Main.h"
37 #include "ConfigManager.h"
38 #include "CodeWindow.h"
39 #include "LogWindow.h"
40 #include "JitWindow.h"
41 #include "ExtendedTrace.h"
42 #include "BootManager.h"
43 #include "Frame.h"
45 // ------------
46 // Main window
48 IMPLEMENT_APP(DolphinApp)
50 BEGIN_EVENT_TABLE(DolphinApp, wxApp)
51 EVT_TIMER(wxID_ANY, DolphinApp::AfterInit)
52 END_EVENT_TABLE()
54 #include <wx/stdpaths.h>
55 bool wxMsgAlert(const char*, const char*, bool, int);
57 CFrame* main_frame = NULL;
59 #ifdef WIN32
60 //Has no error handling.
61 //I think that if an error occurs here there's no way to handle it anyway.
62 LONG WINAPI MyUnhandledExceptionFilter(LPEXCEPTION_POINTERS e) {
63 //EnterCriticalSection(&g_uefcs);
65 FILE* file = NULL;
66 fopen_s(&file, "exceptioninfo.txt", "a");
67 fseek(file, 0, SEEK_END);
68 etfprint(file, "\n");
69 //etfprint(file, g_buildtime);
70 //etfprint(file, "\n");
71 //dumpCurrentDate(file);
72 etfprintf(file, "Unhandled Exception\n Code: 0x%08X\n",
73 e->ExceptionRecord->ExceptionCode);
74 #ifndef _M_X64
75 STACKTRACE2(file, e->ContextRecord->Eip, e->ContextRecord->Esp, e->ContextRecord->Ebp);
76 #else
77 STACKTRACE2(file, e->ContextRecord->Rip, e->ContextRecord->Rsp, e->ContextRecord->Rbp);
78 #endif
79 fclose(file);
80 _flushall();
82 //LeaveCriticalSection(&g_uefcs);
83 return EXCEPTION_CONTINUE_SEARCH;
85 #endif
87 // The `main program' equivalent that creates the main window and return the main frame
89 bool DolphinApp::OnInit()
91 // Declarations and definitions
92 bool UseDebugger = false;
93 bool BatchMode = false;
94 bool UseLogger = false;
95 bool selectVideoPlugin = false;
96 bool selectAudioPlugin = false;
97 bool selectWiimotePlugin = false;
99 wxString videoPluginFilename;
100 wxString audioPluginFilename;
101 wxString wiimotePluginFilename;
103 #if wxUSE_CMDLINE_PARSER // Parse command lines
104 #if wxCHECK_VERSION(2, 9, 0)
105 wxCmdLineEntryDesc cmdLineDesc[] =
108 wxCMD_LINE_SWITCH, "h", "help", "Show this help message",
109 wxCMD_LINE_VAL_NONE, wxCMD_LINE_OPTION_HELP
112 wxCMD_LINE_SWITCH, "d", "debugger", "Opens the debugger"
115 wxCMD_LINE_SWITCH, "l", "logger", "Opens the logger"
118 wxCMD_LINE_OPTION, "e", "exec", "Loads the specified file (DOL, ELF, WAD, GCM, ISO)",
119 wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL
122 wxCMD_LINE_SWITCH, "b", "batch", "Exit Dolphin with emulator"
125 wxCMD_LINE_OPTION, "V", "video_plugin","Specify a video plugin",
126 wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL
129 wxCMD_LINE_OPTION, "A", "audio_plugin","Specify an audio plugin",
130 wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL
133 wxCMD_LINE_OPTION, "W", "wiimote_plugin","Specify a wiimote plugin",
134 wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL
137 wxCMD_LINE_NONE
140 #else
141 wxCmdLineEntryDesc cmdLineDesc[] =
144 wxCMD_LINE_SWITCH, _("h"), _("help"),
145 wxT("Show this help message"),
146 wxCMD_LINE_VAL_NONE, wxCMD_LINE_OPTION_HELP
149 wxCMD_LINE_SWITCH, _("d"), _("debugger"), wxT("Opens the debugger")
152 wxCMD_LINE_SWITCH, _("l"), _("logger"), wxT("Opens the logger")
155 wxCMD_LINE_OPTION, _("e"), _("exec"), wxT("Loads the specified file (DOL, ELF, WAD, GCM, ISO)"),
156 wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL
159 wxCMD_LINE_SWITCH, _("b"), _("batch"), wxT("Exit Dolphin with emulator")
162 wxCMD_LINE_OPTION, _("V"), _("video_plugin"), wxT("Specify a video plugin"),
163 wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL
166 wxCMD_LINE_OPTION, _("A"), _("audio_plugin"), wxT("Specify an audio plugin"),
167 wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL
170 wxCMD_LINE_OPTION, _("W"), _("wiimote_plugin"), wxT("Specify a wiimote plugin"),
171 wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL
174 wxCMD_LINE_NONE
177 #endif
178 // Gets the command line parameters
179 wxCmdLineParser parser(cmdLineDesc, argc, argv);
181 if (parser.Parse() != 0)
183 return false;
185 #if wxCHECK_VERSION(2, 9, 0)
186 UseDebugger = parser.Found("debugger");
187 UseLogger = parser.Found("logger");
188 LoadFile = parser.Found("exec", &FileToLoad);
189 BatchMode = parser.Found("batch");
190 #else
191 UseDebugger = parser.Found(wxT("debugger"));
192 UseLogger = parser.Found(wxT("logger"));
193 LoadFile = parser.Found(wxT("exec"), &FileToLoad);
194 BatchMode = parser.Found(wxT("batch"));
195 #endif
197 #if wxCHECK_VERSION(2, 9, 0)
198 selectVideoPlugin = parser.Found("video_plugin", &videoPluginFilename);
199 selectAudioPlugin = parser.Found("audio_plugin", &audioPluginFilename);
200 selectWiimotePlugin = parser.Found("wiimote_plugin", &wiimotePluginFilename);
201 #else
202 selectVideoPlugin = parser.Found(wxT("video_plugin"), &videoPluginFilename);
203 selectAudioPlugin = parser.Found(wxT("audio_plugin"), &audioPluginFilename);
204 selectWiimotePlugin = parser.Found(wxT("wiimote_plugin"), &wiimotePluginFilename);
205 #endif
206 #endif // wxUSE_CMDLINE_PARSER
208 #if defined _DEBUG && defined _WIN32
209 int tmpflag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
210 tmpflag |= _CRTDBG_DELAY_FREE_MEM_DF;
211 _CrtSetDbgFlag(tmpflag);
212 #endif
214 // Register message box handler
215 #ifndef _WIN32
216 RegisterMsgAlertHandler(&wxMsgAlert);
217 #endif
219 // "ExtendedTrace" looks freakin dangerous!!!
220 #ifdef _WIN32
221 EXTENDEDTRACEINITIALIZE(".");
222 SetUnhandledExceptionFilter(&MyUnhandledExceptionFilter);
223 #endif
225 // TODO: if First Boot
226 if (!cpu_info.bSSE2)
228 PanicAlert("Hi,\n\nDolphin requires that your CPU has support for SSE2 extensions.\n"
229 "Unfortunately your CPU does not support them, so Dolphin will not run.\n\n"
230 "Sayonara!\n");
231 return false;
234 #if ! defined(__APPLE__) && ! defined(__linux__)
235 // Keep the user config dir free unless user wants to save the working dir
236 if (!File::Exists((std::string(File::GetUserPath(D_CONFIG_IDX)) + "portable").c_str()))
238 char tmp[1024];
239 sprintf(tmp, "%s/.dolphin%swd", (const char*)wxStandardPaths::Get().GetUserConfigDir().mb_str(),
240 #ifdef _M_IX86
241 "x32");
242 #else
243 "x64");
244 #endif
245 FILE* workingDir = fopen(tmp, "r");
246 if (!workingDir)
248 if (PanicYesNo("Dolphin has not been configured with an install location,\nKeep Dolphin portable?"))
250 FILE* portable = fopen((std::string(File::GetUserPath(D_CONFIG_IDX)) + "portable").c_str(), "w");
251 if (!portable)
253 PanicAlert("Portable Setting could not be saved\n Are you running Dolphin from read only media or from a directory that dolphin is not located in?");
255 else
257 fclose(portable);
260 else
262 char CWD[1024];
263 sprintf(CWD, "%s", (const char*)wxGetCwd().mb_str());
264 if (PanicYesNo("Set install location to:\n %s ?", CWD))
266 FILE* workingDirF = fopen(tmp, "w");
267 if (!workingDirF)
268 PanicAlert("Install directory could not be saved");
269 else
271 fwrite(CWD, ((std::string)CWD).size()+1, 1, workingDirF);
272 fwrite("", 1, 1, workingDirF); //seems to be needed on linux
273 fclose(workingDirF);
276 else
277 PanicAlert("Relaunch Dolphin from the install directory and save from there");
280 else
282 char *tmpChar;
283 long len;
284 fseek(workingDir, 0, SEEK_END);
285 len = ftell(workingDir);
286 fseek(workingDir, 0, SEEK_SET);
287 tmpChar = new char[len];
288 fread(tmpChar, len, 1, workingDir);
289 fclose(workingDir);
290 if (!wxSetWorkingDirectory(wxString::From8BitData(tmpChar)))
292 INFO_LOG(CONSOLE, "set working directory failed");
294 delete [] tmpChar;
297 #endif
299 #ifdef __APPLE__
300 const char *AppSupportDir = File::GetUserPath(D_USER_IDX);
302 if (!File::Exists(AppSupportDir))
304 // Fresh run: create Dolphin dir and copy contents of User within the bundle to App Support
305 File::CopyDir(std::string(File::GetBundleDirectory() + DIR_SEP USERDATA_DIR DIR_SEP).c_str(), AppSupportDir);
307 else if (!File::IsDirectory(AppSupportDir))
308 PanicAlert("~/Library/Application Support/Dolphin exists, but is not a directory");
309 #endif
311 #ifdef __linux__
312 //create all necessary directories in user directory
313 //TODO : detect the revision and upgrade where necessary
314 File::CopyDir(SHARED_USER_DIR CONFIG_DIR DIR_SEP, File::GetUserPath(D_CONFIG_IDX));
315 File::CopyDir(SHARED_USER_DIR GAMECONFIG_DIR DIR_SEP, File::GetUserPath(D_GAMECONFIG_IDX));
316 File::CopyDir(SHARED_USER_DIR MAPS_DIR DIR_SEP, File::GetUserPath(D_MAPS_IDX));
317 File::CopyDir(SHARED_USER_DIR SHADERS_DIR DIR_SEP, File::GetUserPath(D_SHADERS_IDX));
318 File::CopyDir(SHARED_USER_DIR WII_USER_DIR DIR_SEP, File::GetUserPath(D_WIIUSER_IDX));
320 if (!File::Exists(File::GetUserPath(D_GCUSER_IDX)))
321 File::CreateFullPath(File::GetUserPath(D_GCUSER_IDX));
322 if (!File::Exists(File::GetUserPath(D_CACHE_IDX)))
323 File::CreateFullPath(File::GetUserPath(D_CACHE_IDX));
324 if (!File::Exists(File::GetUserPath(D_DUMPDSP_IDX)))
325 File::CreateFullPath(File::GetUserPath(D_DUMPDSP_IDX));
326 if (!File::Exists(File::GetUserPath(D_DUMPTEXTURES_IDX)))
327 File::CreateFullPath(File::GetUserPath(D_DUMPTEXTURES_IDX));
328 if (!File::Exists(File::GetUserPath(D_HIRESTEXTURES_IDX)))
329 File::CreateFullPath(File::GetUserPath(D_HIRESTEXTURES_IDX));
330 if (!File::Exists(File::GetUserPath(D_SCREENSHOTS_IDX)))
331 File::CreateFullPath(File::GetUserPath(D_SCREENSHOTS_IDX));
332 if (!File::Exists(File::GetUserPath(D_STATESAVES_IDX)))
333 File::CreateFullPath(File::GetUserPath(D_STATESAVES_IDX));
334 if (!File::Exists(File::GetUserPath(D_MAILLOGS_IDX)))
335 File::CreateFullPath(File::GetUserPath(D_MAILLOGS_IDX));
336 #endif
338 LogManager::Init();
339 SConfig::Init();
340 CPluginManager::Init();
342 if (selectVideoPlugin && videoPluginFilename != wxEmptyString)
343 SConfig::GetInstance().m_LocalCoreStartupParameter.m_strVideoPlugin =
344 std::string(videoPluginFilename.mb_str());
346 if (selectAudioPlugin && audioPluginFilename != wxEmptyString)
347 SConfig::GetInstance().m_LocalCoreStartupParameter.m_strDSPPlugin =
348 std::string(audioPluginFilename.mb_str());
350 if (selectWiimotePlugin && wiimotePluginFilename != wxEmptyString)
351 SConfig::GetInstance().m_LocalCoreStartupParameter.m_strWiimotePlugin =
352 std::string(wiimotePluginFilename.mb_str());
354 // Enable the PNG image handler
355 wxInitAllImageHandlers();
357 SetEnableAlert(SConfig::GetInstance().m_LocalCoreStartupParameter.bUsePanicHandlers);
359 int x = SConfig::GetInstance().m_LocalCoreStartupParameter.iPosX;
360 int y = SConfig::GetInstance().m_LocalCoreStartupParameter.iPosY;
361 int w = SConfig::GetInstance().m_LocalCoreStartupParameter.iWidth;
362 int h = SConfig::GetInstance().m_LocalCoreStartupParameter.iHeight;
364 // The following is not needed in linux. Linux window managers do not allow windows to
365 // be created off the desktop.
366 #ifdef _WIN32
367 // Out of desktop check
368 HWND hDesktop = GetDesktopWindow();
369 RECT rc;
370 GetWindowRect(hDesktop, &rc);
371 if (rc.right < x + w || rc.bottom < y + h)
372 x = y = -1;
373 #endif
375 main_frame = new CFrame((wxFrame*)NULL, wxID_ANY,
376 wxString::FromAscii(svn_rev_str),
377 wxPoint(x, y), wxSize(w, h),
378 UseDebugger, BatchMode, UseLogger);
379 SetTopWindow(main_frame);
381 #if defined HAVE_X11 && HAVE_X11
382 XInitThreads();
383 #endif
385 // Postpone final actions until event handler is running
386 m_afterinit = new wxTimer(this, wxID_ANY);
387 m_afterinit->Start(1, wxTIMER_ONE_SHOT);
389 return true;
392 void DolphinApp::AfterInit(wxTimerEvent& WXUNUSED(event))
394 delete m_afterinit;
396 // Updating the game list makes use of wxProgressDialog which may
397 // only be run after OnInit() when the event handler is running.
398 main_frame->UpdateGameList();
400 // Check the autoboot options:
402 // First check if we have an exec command line.
403 if (LoadFile && FileToLoad != wxEmptyString)
405 main_frame->BootGame(std::string(FileToLoad.mb_str()));
408 // If we have selected Automatic Start, start the default ISO,
409 // or if no default ISO exists, start the last loaded ISO
410 else if (main_frame->g_pCodeWindow)
412 if (main_frame->g_pCodeWindow->AutomaticStart())
414 if(!SConfig::GetInstance().m_LocalCoreStartupParameter.m_strDefaultGCM.empty()
415 && File::Exists(SConfig::GetInstance().m_LocalCoreStartupParameter.
416 m_strDefaultGCM.c_str()))
418 main_frame->BootGame(SConfig::GetInstance().m_LocalCoreStartupParameter.
419 m_strDefaultGCM);
421 else if(!SConfig::GetInstance().m_LastFilename.empty()
422 && File::Exists(SConfig::GetInstance().m_LastFilename.c_str()))
424 main_frame->BootGame(SConfig::GetInstance().m_LastFilename);
430 void DolphinApp::OnEndSession()
432 SConfig::GetInstance().SaveSettings();
435 int DolphinApp::OnExit()
437 #ifdef _WIN32
438 if (SConfig::GetInstance().m_WiiAutoUnpair)
440 if (CPluginManager::GetInstance().GetWiimote())
441 CPluginManager::GetInstance().GetWiimote()->Wiimote_UnPairWiimotes();
443 #endif
444 CPluginManager::Shutdown();
445 SConfig::Shutdown();
446 LogManager::Shutdown();
448 return wxApp::OnExit();
452 // ------------
453 // Talk to GUI
456 // g_VideoInitialize.pSysMessage() goes here
457 void Host_SysMessage(const char *fmt, ...)
459 va_list list;
460 char msg[512];
462 va_start(list, fmt);
463 vsprintf(msg, fmt, list);
464 va_end(list);
466 if (msg[strlen(msg)-1] == '\n') msg[strlen(msg)-1] = 0;
467 //wxMessageBox(wxString::FromAscii(msg));
468 PanicAlert("%s", msg);
471 bool wxMsgAlert(const char* caption, const char* text, bool yes_no, int /*Style*/)
473 return wxYES == wxMessageBox(wxString::FromAscii(text),
474 wxString::FromAscii(caption),
475 (yes_no)?wxYES_NO:wxOK);
478 // Accessor for the main window class
479 CFrame* DolphinApp::GetCFrame()
481 return main_frame;
484 void Host_Message(int Id)
487 switch(Id)
489 #if defined(HAVE_X11) && HAVE_X11
490 case WM_USER_STOP:
491 #endif
492 case WM_USER_CREATE:
494 wxCommandEvent event(wxEVT_HOST_COMMAND, Id);
495 main_frame->GetEventHandler()->AddPendingEvent(event);
496 break;
498 default:
499 main_frame->OnCustomHostMessage(Id);
503 // OK, this thread boundary is DANGEROUS on linux
504 // wxPostEvent / wxAddPendingEvent is the solution.
505 void Host_NotifyMapLoaded()
507 wxCommandEvent event(wxEVT_HOST_COMMAND, IDM_NOTIFYMAPLOADED);
508 main_frame->GetEventHandler()->AddPendingEvent(event);
510 if (main_frame->g_pCodeWindow)
512 main_frame->g_pCodeWindow->GetEventHandler()->AddPendingEvent(event);
517 void Host_UpdateLogDisplay()
519 wxCommandEvent event(wxEVT_HOST_COMMAND, IDM_UPDATELOGDISPLAY);
520 main_frame->GetEventHandler()->AddPendingEvent(event);
522 if (main_frame->g_pCodeWindow)
524 main_frame->g_pCodeWindow->GetEventHandler()->AddPendingEvent(event);
529 void Host_UpdateDisasmDialog()
531 wxCommandEvent event(wxEVT_HOST_COMMAND, IDM_UPDATEDISASMDIALOG);
532 main_frame->GetEventHandler()->AddPendingEvent(event);
534 if (main_frame->g_pCodeWindow)
536 main_frame->g_pCodeWindow->GetEventHandler()->AddPendingEvent(event);
541 void Host_ShowJitResults(unsigned int address)
543 CJitWindow::ViewAddr(address);
546 void Host_UpdateMainFrame()
548 wxCommandEvent event(wxEVT_HOST_COMMAND, IDM_UPDATEGUI);
549 main_frame->GetEventHandler()->AddPendingEvent(event);
551 if (main_frame->g_pCodeWindow)
553 main_frame->g_pCodeWindow->GetEventHandler()->AddPendingEvent(event);
557 void Host_UpdateTitle(const char* title)
559 wxCommandEvent event(wxEVT_HOST_COMMAND, IDM_UPDATETITLE);
560 event.SetString(wxString::FromAscii(title));
561 main_frame->GetEventHandler()->AddPendingEvent(event);
564 void Host_UpdateBreakPointView()
566 wxCommandEvent event(wxEVT_HOST_COMMAND, IDM_UPDATEBREAKPOINTS);
567 main_frame->GetEventHandler()->AddPendingEvent(event);
569 if (main_frame->g_pCodeWindow)
571 main_frame->g_pCodeWindow->GetEventHandler()->AddPendingEvent(event);
575 void Host_UpdateMemoryView()
578 void Host_SetDebugMode(bool)
581 void Host_RequestWindowSize(int& x, int& y, int& width, int& height)
583 main_frame->OnSizeRequest(x, y, width, height);
586 void Host_SetWaitCursor(bool enable)
588 if (enable)
589 wxBeginBusyCursor();
590 else
591 wxEndBusyCursor();
594 void Host_UpdateStatusBar(const char* _pText, int Field)
596 wxCommandEvent event(wxEVT_HOST_COMMAND, IDM_UPDATESTATUSBAR);
597 // Set the event string
598 event.SetString(wxString::FromAscii(_pText));
599 // Update statusbar field
600 event.SetInt(Field);
601 // Post message
602 // TODO : this has been said to cause hang (??) how is that even possible ? :d
603 event.StopPropagation();
604 main_frame->GetEventHandler()->AddPendingEvent(event);
607 void Host_SetWiiMoteConnectionState(int _State)
609 static int currentState = -1;
610 if (_State == currentState)
611 return;
612 currentState = _State;
614 wxCommandEvent event(wxEVT_HOST_COMMAND, IDM_UPDATESTATUSBAR);
616 switch(_State)
618 case 0: event.SetString(wxString::FromAscii("Not connected")); break;
619 case 1: event.SetString(wxString::FromAscii("Connecting...")); break;
620 case 2: event.SetString(wxString::FromAscii("Wiimote Connected")); break;
622 // Update field 1 or 2
623 event.SetInt(1);
625 main_frame->GetEventHandler()->AddPendingEvent(event);
628 bool Host_RendererHasFocus()
630 return main_frame->RendererHasFocus();