bootmanager.cpp 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. #include <QHBoxLayout>
  2. #include <QKeyEvent>
  3. #include <QApplication>
  4. #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
  5. // Required for screen DPI information
  6. #include <QScreen>
  7. #include <QWindow>
  8. #endif
  9. #include "common/common.h"
  10. #include "bootmanager.h"
  11. #include "core/core.h"
  12. #include "core/settings.h"
  13. #include "core/system.h"
  14. #include "video_core/debug_utils/debug_utils.h"
  15. #include "video_core/video_core.h"
  16. #include "citra_qt/version.h"
  17. #define APP_NAME "citra"
  18. #define APP_VERSION "0.1-" VERSION
  19. #define APP_TITLE APP_NAME " " APP_VERSION
  20. #define COPYRIGHT "Copyright (C) 2013-2014 Citra Team"
  21. EmuThread::EmuThread(GRenderWindow* render_window) :
  22. filename(""), exec_cpu_step(false), cpu_running(false),
  23. stop_run(false), render_window(render_window)
  24. {
  25. }
  26. void EmuThread::SetFilename(std::string filename)
  27. {
  28. this->filename = filename;
  29. }
  30. void EmuThread::run()
  31. {
  32. stop_run = false;
  33. // holds whether the cpu was running during the last iteration,
  34. // so that the DebugModeLeft signal can be emitted before the
  35. // next execution step
  36. bool was_active = false;
  37. while (!stop_run)
  38. {
  39. if (cpu_running)
  40. {
  41. if (!was_active)
  42. emit DebugModeLeft();
  43. Core::RunLoop();
  44. was_active = cpu_running || exec_cpu_step;
  45. if (!was_active)
  46. emit DebugModeEntered();
  47. }
  48. else if (exec_cpu_step)
  49. {
  50. if (!was_active)
  51. emit DebugModeLeft();
  52. exec_cpu_step = false;
  53. Core::SingleStep();
  54. emit DebugModeEntered();
  55. yieldCurrentThread();
  56. was_active = false;
  57. }
  58. }
  59. render_window->moveContext();
  60. Core::Stop();
  61. }
  62. void EmuThread::Stop()
  63. {
  64. if (!isRunning())
  65. {
  66. LOG_WARNING(Frontend, "EmuThread::Stop called while emu thread wasn't running, returning...");
  67. return;
  68. }
  69. stop_run = true;
  70. // Release emu threads from any breakpoints, so that this doesn't hang forever.
  71. Pica::g_debug_context->ClearBreakpoints();
  72. //core::g_state = core::SYS_DIE;
  73. // TODO: Waiting here is just a bad workaround for retarded shutdown logic.
  74. wait(1000);
  75. if (isRunning())
  76. {
  77. LOG_WARNING(Frontend, "EmuThread still running, terminating...");
  78. quit();
  79. // TODO: Waiting 50 seconds can be necessary if the logging subsystem has a lot of spam
  80. // queued... This should be fixed.
  81. wait(50000);
  82. if (isRunning())
  83. {
  84. LOG_CRITICAL(Frontend, "EmuThread STILL running, something is wrong here...");
  85. terminate();
  86. }
  87. }
  88. LOG_INFO(Frontend, "EmuThread stopped");
  89. System::Shutdown();
  90. }
  91. // This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL context.
  92. // The corresponding functionality is handled in EmuThread instead
  93. class GGLWidgetInternal : public QGLWidget
  94. {
  95. public:
  96. GGLWidgetInternal(QGLFormat fmt, GRenderWindow* parent)
  97. : QGLWidget(fmt, parent), parent(parent) {
  98. }
  99. void paintEvent(QPaintEvent* ev) override {
  100. }
  101. void resizeEvent(QResizeEvent* ev) override {
  102. parent->OnClientAreaResized(ev->size().width(), ev->size().height());
  103. parent->OnFramebufferSizeChanged();
  104. }
  105. private:
  106. GRenderWindow* parent;
  107. };
  108. EmuThread& GRenderWindow::GetEmuThread()
  109. {
  110. return emu_thread;
  111. }
  112. GRenderWindow::GRenderWindow(QWidget* parent) : QWidget(parent), emu_thread(this), keyboard_id(0)
  113. {
  114. std::string window_title = Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc);
  115. setWindowTitle(QString::fromStdString(window_title));
  116. keyboard_id = KeyMap::NewDeviceId();
  117. ReloadSetKeymaps();
  118. // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, WA_DontShowOnScreen, WA_DeleteOnClose
  119. QGLFormat fmt;
  120. fmt.setVersion(3,2);
  121. fmt.setProfile(QGLFormat::CoreProfile);
  122. // Requests a forward-compatible context, which is required to get a 3.2+ context on OS X
  123. fmt.setOption(QGL::NoDeprecatedFunctions);
  124. child = new GGLWidgetInternal(fmt, this);
  125. QBoxLayout* layout = new QHBoxLayout(this);
  126. resize(VideoCore::kScreenTopWidth, VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight);
  127. layout->addWidget(child);
  128. layout->setMargin(0);
  129. setLayout(layout);
  130. connect(&emu_thread, SIGNAL(started()), this, SLOT(moveContext()));
  131. OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
  132. OnFramebufferSizeChanged();
  133. NotifyClientAreaSizeChanged(std::pair<unsigned,unsigned>(child->width(), child->height()));
  134. BackupGeometry();
  135. #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
  136. connect(this->windowHandle(), SIGNAL(screenChanged(QScreen*)), this, SLOT(OnFramebufferSizeChanged()));
  137. #endif
  138. }
  139. void GRenderWindow::moveContext()
  140. {
  141. DoneCurrent();
  142. // We need to move GL context to the swapping thread in Qt5
  143. #if QT_VERSION > QT_VERSION_CHECK(5, 0, 0)
  144. // If the thread started running, move the GL Context to the new thread. Otherwise, move it back.
  145. child->context()->moveToThread((QThread::currentThread() == qApp->thread()) ? &emu_thread : qApp->thread());
  146. #endif
  147. }
  148. GRenderWindow::~GRenderWindow()
  149. {
  150. if (emu_thread.isRunning())
  151. emu_thread.Stop();
  152. }
  153. void GRenderWindow::SwapBuffers()
  154. {
  155. // MakeCurrent is already called in renderer_opengl
  156. child->swapBuffers();
  157. }
  158. void GRenderWindow::closeEvent(QCloseEvent* event)
  159. {
  160. if (emu_thread.isRunning())
  161. emu_thread.Stop();
  162. QWidget::closeEvent(event);
  163. }
  164. void GRenderWindow::MakeCurrent()
  165. {
  166. child->makeCurrent();
  167. }
  168. void GRenderWindow::DoneCurrent()
  169. {
  170. child->doneCurrent();
  171. }
  172. void GRenderWindow::PollEvents() {
  173. }
  174. // On Qt 5.0+, this correctly gets the size of the framebuffer (pixels).
  175. //
  176. // Older versions get the window size (density independent pixels),
  177. // and hence, do not support DPI scaling ("retina" displays).
  178. // The result will be a viewport that is smaller than the extent of the window.
  179. void GRenderWindow::OnFramebufferSizeChanged()
  180. {
  181. // Screen changes potentially incur a change in screen DPI, hence we should update the framebuffer size
  182. #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
  183. // windowHandle() might not be accessible until the window is displayed to screen.
  184. auto pixel_ratio = windowHandle() ? (windowHandle()->screen()->devicePixelRatio()) : 1.0;
  185. unsigned width = child->QPaintDevice::width() * pixel_ratio;
  186. unsigned height = child->QPaintDevice::height() * pixel_ratio;
  187. #else
  188. unsigned width = child->QPaintDevice::width();
  189. unsigned height = child->QPaintDevice::height();
  190. #endif
  191. NotifyFramebufferSizeChanged(std::make_pair(width, height));
  192. }
  193. void GRenderWindow::BackupGeometry()
  194. {
  195. geometry = ((QGLWidget*)this)->saveGeometry();
  196. }
  197. void GRenderWindow::RestoreGeometry()
  198. {
  199. // We don't want to back up the geometry here (obviously)
  200. QWidget::restoreGeometry(geometry);
  201. }
  202. void GRenderWindow::restoreGeometry(const QByteArray& geometry)
  203. {
  204. // Make sure users of this class don't need to deal with backing up the geometry themselves
  205. QWidget::restoreGeometry(geometry);
  206. BackupGeometry();
  207. }
  208. QByteArray GRenderWindow::saveGeometry()
  209. {
  210. // If we are a top-level widget, store the current geometry
  211. // otherwise, store the last backup
  212. if (parent() == nullptr)
  213. return ((QGLWidget*)this)->saveGeometry();
  214. else
  215. return geometry;
  216. }
  217. void GRenderWindow::keyPressEvent(QKeyEvent* event)
  218. {
  219. EmuWindow::KeyPressed({event->key(), keyboard_id});
  220. HID_User::PadUpdateComplete();
  221. }
  222. void GRenderWindow::keyReleaseEvent(QKeyEvent* event)
  223. {
  224. EmuWindow::KeyReleased({event->key(), keyboard_id});
  225. HID_User::PadUpdateComplete();
  226. }
  227. void GRenderWindow::ReloadSetKeymaps()
  228. {
  229. KeyMap::SetKeyMapping({Settings::values.pad_a_key, keyboard_id}, HID_User::PAD_A);
  230. KeyMap::SetKeyMapping({Settings::values.pad_b_key, keyboard_id}, HID_User::PAD_B);
  231. KeyMap::SetKeyMapping({Settings::values.pad_select_key, keyboard_id}, HID_User::PAD_SELECT);
  232. KeyMap::SetKeyMapping({Settings::values.pad_start_key, keyboard_id}, HID_User::PAD_START);
  233. KeyMap::SetKeyMapping({Settings::values.pad_dright_key, keyboard_id}, HID_User::PAD_RIGHT);
  234. KeyMap::SetKeyMapping({Settings::values.pad_dleft_key, keyboard_id}, HID_User::PAD_LEFT);
  235. KeyMap::SetKeyMapping({Settings::values.pad_dup_key, keyboard_id}, HID_User::PAD_UP);
  236. KeyMap::SetKeyMapping({Settings::values.pad_ddown_key, keyboard_id}, HID_User::PAD_DOWN);
  237. KeyMap::SetKeyMapping({Settings::values.pad_r_key, keyboard_id}, HID_User::PAD_R);
  238. KeyMap::SetKeyMapping({Settings::values.pad_l_key, keyboard_id}, HID_User::PAD_L);
  239. KeyMap::SetKeyMapping({Settings::values.pad_x_key, keyboard_id}, HID_User::PAD_X);
  240. KeyMap::SetKeyMapping({Settings::values.pad_y_key, keyboard_id}, HID_User::PAD_Y);
  241. KeyMap::SetKeyMapping({Settings::values.pad_sright_key, keyboard_id}, HID_User::PAD_CIRCLE_RIGHT);
  242. KeyMap::SetKeyMapping({Settings::values.pad_sleft_key, keyboard_id}, HID_User::PAD_CIRCLE_LEFT);
  243. KeyMap::SetKeyMapping({Settings::values.pad_sup_key, keyboard_id}, HID_User::PAD_CIRCLE_UP);
  244. KeyMap::SetKeyMapping({Settings::values.pad_sdown_key, keyboard_id}, HID_User::PAD_CIRCLE_DOWN);
  245. }
  246. void GRenderWindow::OnClientAreaResized(unsigned width, unsigned height)
  247. {
  248. NotifyClientAreaSizeChanged(std::make_pair(width, height));
  249. }
  250. void GRenderWindow::OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) {
  251. setMinimumSize(minimal_size.first, minimal_size.second);
  252. }