bootmanager.cpp 8.8 KB

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