Host.cpp 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. // Copyright 2015 Dolphin Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include "DolphinQt/Host.h"
  4. #include <functional>
  5. #include <QAbstractEventDispatcher>
  6. #include <QApplication>
  7. #include <QLocale>
  8. #include <imgui.h>
  9. #ifdef _WIN32
  10. #include <windows.h>
  11. #endif
  12. #include "Common/Common.h"
  13. #include "Core/Config/MainSettings.h"
  14. #include "Core/ConfigManager.h"
  15. #include "Core/Core.h"
  16. #include "Core/Debugger/PPCDebugInterface.h"
  17. #include "Core/Host.h"
  18. #include "Core/NetPlayProto.h"
  19. #include "Core/PowerPC/PowerPC.h"
  20. #include "Core/State.h"
  21. #include "Core/System.h"
  22. #ifdef HAS_LIBMGBA
  23. #include "DolphinQt/GBAWidget.h"
  24. #endif
  25. #include "DolphinQt/QtUtils/QueueOnObject.h"
  26. #include "DolphinQt/Settings.h"
  27. #include "InputCommon/ControllerInterface/ControllerInterface.h"
  28. #include "UICommon/DiscordPresence.h"
  29. #include "VideoCommon/AbstractGfx.h"
  30. #include "VideoCommon/Fifo.h"
  31. #include "VideoCommon/Present.h"
  32. #include "VideoCommon/VideoConfig.h"
  33. Host::Host()
  34. {
  35. State::SetOnAfterLoadCallback([] { Host_UpdateDisasmDialog(); });
  36. }
  37. Host::~Host()
  38. {
  39. State::SetOnAfterLoadCallback(nullptr);
  40. }
  41. Host* Host::GetInstance()
  42. {
  43. static Host* s_instance = new Host();
  44. return s_instance;
  45. }
  46. void Host::SetRenderHandle(void* handle)
  47. {
  48. m_render_to_main = Config::Get(Config::MAIN_RENDER_TO_MAIN);
  49. if (m_render_handle == handle)
  50. return;
  51. m_render_handle = handle;
  52. if (g_presenter)
  53. {
  54. g_presenter->ChangeSurface(handle);
  55. g_controller_interface.ChangeWindow(handle);
  56. }
  57. }
  58. void Host::SetMainWindowHandle(void* handle)
  59. {
  60. m_main_window_handle = handle;
  61. }
  62. static void RunWithGPUThreadInactive(std::function<void()> f)
  63. {
  64. // Potentially any thread which shows panic alerts can be blocked on this returning.
  65. // This means that, in order to avoid deadlocks, we need to be careful with how we
  66. // synchronize with other threads. Note that the panic alert handler temporarily declares
  67. // us as the CPU and/or GPU thread if the panic alert was requested by that thread.
  68. // TODO: What about the unlikely case where the GPU thread calls the panic alert handler
  69. // while the panic alert handler is processing a panic alert from the CPU thread?
  70. if (Core::IsGPUThread())
  71. {
  72. // If we are the GPU thread, we can't call Core::PauseAndLock without getting a deadlock,
  73. // since it would try to pause the GPU thread while that thread is waiting for us.
  74. // However, since we know that the GPU thread is inactive, we can just run f directly.
  75. f();
  76. }
  77. else if (Core::IsCPUThread())
  78. {
  79. // If we are the CPU thread in dual core mode, we can't call Core::PauseAndLock, for the
  80. // same reason as above. Instead, we use Fifo::PauseAndLock to pause the GPU thread only.
  81. // (Note that this case cannot be reached in single core mode, because in single core mode,
  82. // the CPU and GPU threads are the same thread, and we already checked for the GPU thread.)
  83. auto& system = Core::System::GetInstance();
  84. const bool was_running = Core::GetState(system) == Core::State::Running;
  85. auto& fifo = system.GetFifo();
  86. fifo.PauseAndLock(true, was_running);
  87. f();
  88. fifo.PauseAndLock(false, was_running);
  89. }
  90. else
  91. {
  92. // If we reach here, we can call Core::PauseAndLock (which we do using a CPUThreadGuard).
  93. const Core::CPUThreadGuard guard(Core::System::GetInstance());
  94. f();
  95. }
  96. }
  97. bool Host::GetRenderFocus()
  98. {
  99. #ifdef _WIN32
  100. // Unfortunately Qt calls SetRenderFocus() with a slight delay compared to what we actually need
  101. // to avoid inputs that cause a focus loss to be processed by the emulation
  102. if (m_render_to_main && !m_render_fullscreen)
  103. return GetForegroundWindow() == (HWND)m_main_window_handle.load();
  104. return GetForegroundWindow() == (HWND)m_render_handle.load();
  105. #else
  106. return m_render_focus;
  107. #endif
  108. }
  109. bool Host::GetRenderFullFocus()
  110. {
  111. return m_render_full_focus;
  112. }
  113. void Host::SetRenderFocus(bool focus)
  114. {
  115. m_render_focus = focus;
  116. if (g_gfx && m_render_fullscreen && g_ActiveConfig.ExclusiveFullscreenEnabled())
  117. {
  118. RunWithGPUThreadInactive([focus] {
  119. if (!Config::Get(Config::MAIN_RENDER_TO_MAIN))
  120. g_gfx->SetFullscreen(focus);
  121. });
  122. }
  123. }
  124. void Host::SetRenderFullFocus(bool focus)
  125. {
  126. m_render_full_focus = focus;
  127. }
  128. bool Host::GetGBAFocus()
  129. {
  130. #ifdef HAS_LIBMGBA
  131. return qobject_cast<GBAWidget*>(QApplication::activeWindow()) != nullptr;
  132. #else
  133. return false;
  134. #endif
  135. }
  136. bool Host::GetTASInputFocus() const
  137. {
  138. return m_tas_input_focus;
  139. }
  140. bool Host::GetRenderFullscreen()
  141. {
  142. return m_render_fullscreen;
  143. }
  144. void Host::SetRenderFullscreen(bool fullscreen)
  145. {
  146. m_render_fullscreen = fullscreen;
  147. if (g_gfx && g_gfx->IsFullscreen() != fullscreen && g_ActiveConfig.ExclusiveFullscreenEnabled())
  148. {
  149. RunWithGPUThreadInactive([fullscreen] { g_gfx->SetFullscreen(fullscreen); });
  150. }
  151. }
  152. void Host::SetTASInputFocus(const bool focus)
  153. {
  154. m_tas_input_focus = focus;
  155. }
  156. void Host::ResizeSurface(int new_width, int new_height)
  157. {
  158. if (g_presenter)
  159. g_presenter->ResizeSurface();
  160. }
  161. std::vector<std::string> Host_GetPreferredLocales()
  162. {
  163. const QStringList ui_languages = QLocale::system().uiLanguages();
  164. std::vector<std::string> converted_languages(ui_languages.size());
  165. for (int i = 0; i < ui_languages.size(); ++i)
  166. converted_languages[i] = ui_languages[i].toStdString();
  167. return converted_languages;
  168. }
  169. void Host_Message(HostMessageID id)
  170. {
  171. if (id == HostMessageID::WMUserStop)
  172. {
  173. emit Host::GetInstance()->RequestStop();
  174. }
  175. else if (id == HostMessageID::WMUserJobDispatch)
  176. {
  177. // Just poke the main thread to get it to wake up, job dispatch
  178. // will happen automatically before it goes back to sleep again.
  179. QAbstractEventDispatcher::instance(qApp->thread())->wakeUp();
  180. }
  181. }
  182. void Host_UpdateTitle(const std::string& title)
  183. {
  184. emit Host::GetInstance()->RequestTitle(QString::fromStdString(title));
  185. }
  186. bool Host_RendererHasFocus()
  187. {
  188. return Host::GetInstance()->GetRenderFocus() || Host::GetInstance()->GetGBAFocus();
  189. }
  190. bool Host_RendererHasFullFocus()
  191. {
  192. return Host::GetInstance()->GetRenderFullFocus();
  193. }
  194. bool Host_RendererIsFullscreen()
  195. {
  196. return Host::GetInstance()->GetRenderFullscreen();
  197. }
  198. bool Host_TASInputHasFocus()
  199. {
  200. return Host::GetInstance()->GetTASInputFocus();
  201. }
  202. void Host_YieldToUI()
  203. {
  204. qApp->processEvents(QEventLoop::ExcludeUserInputEvents);
  205. }
  206. void Host_UpdateDisasmDialog()
  207. {
  208. if (Settings::Instance().GetIsContinuouslyFrameStepping())
  209. return;
  210. QueueOnObject(QApplication::instance(), [] { emit Host::GetInstance()->UpdateDisasmDialog(); });
  211. }
  212. void Host_JitCacheInvalidation()
  213. {
  214. QueueOnObject(QApplication::instance(), [] { emit Host::GetInstance()->JitCacheInvalidation(); });
  215. }
  216. void Host_JitProfileDataWiped()
  217. {
  218. QueueOnObject(QApplication::instance(), [] { emit Host::GetInstance()->JitProfileDataWiped(); });
  219. }
  220. void Host_PPCSymbolsChanged()
  221. {
  222. QueueOnObject(QApplication::instance(), [] { emit Host::GetInstance()->PPCSymbolsChanged(); });
  223. }
  224. void Host_PPCBreakpointsChanged()
  225. {
  226. QueueOnObject(QApplication::instance(),
  227. [] { emit Host::GetInstance()->PPCBreakpointsChanged(); });
  228. }
  229. // We ignore these, and their purpose should be questioned individually.
  230. // In particular, RequestRenderWindowSize, RequestFullscreen, and
  231. // UpdateMainFrame should almost certainly be removed.
  232. void Host_UpdateMainFrame()
  233. {
  234. }
  235. void Host_RequestRenderWindowSize(int w, int h)
  236. {
  237. emit Host::GetInstance()->RequestRenderSize(w, h);
  238. }
  239. bool Host_UIBlocksControllerState()
  240. {
  241. return ImGui::GetCurrentContext() && ImGui::GetIO().WantCaptureKeyboard;
  242. }
  243. void Host_RefreshDSPDebuggerWindow()
  244. {
  245. }
  246. void Host_TitleChanged()
  247. {
  248. #ifdef USE_DISCORD_PRESENCE
  249. // TODO: Not sure if the NetPlay check is needed.
  250. if (!NetPlay::IsNetPlayRunning())
  251. Discord::UpdateDiscordPresence();
  252. #endif
  253. }
  254. void Host_UpdateDiscordClientID(const std::string& client_id)
  255. {
  256. #ifdef USE_DISCORD_PRESENCE
  257. Discord::UpdateClientID(client_id);
  258. #endif
  259. }
  260. bool Host_UpdateDiscordPresenceRaw(const std::string& details, const std::string& state,
  261. const std::string& large_image_key,
  262. const std::string& large_image_text,
  263. const std::string& small_image_key,
  264. const std::string& small_image_text,
  265. const int64_t start_timestamp, const int64_t end_timestamp,
  266. const int party_size, const int party_max)
  267. {
  268. #ifdef USE_DISCORD_PRESENCE
  269. return Discord::UpdateDiscordPresenceRaw(details, state, large_image_key, large_image_text,
  270. small_image_key, small_image_text, start_timestamp,
  271. end_timestamp, party_size, party_max);
  272. #else
  273. return false;
  274. #endif
  275. }
  276. #ifndef HAS_LIBMGBA
  277. std::unique_ptr<GBAHostInterface> Host_CreateGBAHost(std::weak_ptr<HW::GBA::Core> core)
  278. {
  279. return nullptr;
  280. }
  281. #endif