emu_window_sdl2.cpp 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. // Copyright 2016 Citra Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <SDL.h>
  5. #include "common/logging/log.h"
  6. #include "common/scm_rev.h"
  7. #include "core/core.h"
  8. #include "core/perf_stats.h"
  9. #include "input_common/keyboard.h"
  10. #include "input_common/main.h"
  11. #include "input_common/motion_emu.h"
  12. #include "input_common/sdl/sdl.h"
  13. #include "yuzu_cmd/emu_window/emu_window_sdl2.h"
  14. EmuWindow_SDL2::EmuWindow_SDL2(Core::System& system, bool fullscreen,
  15. InputCommon::InputSubsystem* input_subsystem_)
  16. : system{system}, input_subsystem{input_subsystem_} {
  17. if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) {
  18. LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting...");
  19. exit(1);
  20. }
  21. input_subsystem->Initialize();
  22. SDL_SetMainReady();
  23. }
  24. EmuWindow_SDL2::~EmuWindow_SDL2() {
  25. input_subsystem->Shutdown();
  26. SDL_Quit();
  27. }
  28. void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) {
  29. TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0));
  30. input_subsystem->GetMotionEmu()->Tilt(x, y);
  31. }
  32. void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) {
  33. if (button == SDL_BUTTON_LEFT) {
  34. if (state == SDL_PRESSED) {
  35. TouchPressed((unsigned)std::max(x, 0), (unsigned)std::max(y, 0));
  36. } else {
  37. TouchReleased();
  38. }
  39. } else if (button == SDL_BUTTON_RIGHT) {
  40. if (state == SDL_PRESSED) {
  41. input_subsystem->GetMotionEmu()->BeginTilt(x, y);
  42. } else {
  43. input_subsystem->GetMotionEmu()->EndTilt();
  44. }
  45. }
  46. }
  47. std::pair<unsigned, unsigned> EmuWindow_SDL2::TouchToPixelPos(float touch_x, float touch_y) const {
  48. int w, h;
  49. SDL_GetWindowSize(render_window, &w, &h);
  50. touch_x *= w;
  51. touch_y *= h;
  52. return {static_cast<unsigned>(std::max(std::round(touch_x), 0.0f)),
  53. static_cast<unsigned>(std::max(std::round(touch_y), 0.0f))};
  54. }
  55. void EmuWindow_SDL2::OnFingerDown(float x, float y) {
  56. // TODO(NeatNit): keep track of multitouch using the fingerID and a dictionary of some kind
  57. // This isn't critical because the best we can do when we have that is to average them, like the
  58. // 3DS does
  59. const auto [px, py] = TouchToPixelPos(x, y);
  60. TouchPressed(px, py);
  61. }
  62. void EmuWindow_SDL2::OnFingerMotion(float x, float y) {
  63. const auto [px, py] = TouchToPixelPos(x, y);
  64. TouchMoved(px, py);
  65. }
  66. void EmuWindow_SDL2::OnFingerUp() {
  67. TouchReleased();
  68. }
  69. void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) {
  70. if (state == SDL_PRESSED) {
  71. input_subsystem->GetKeyboard()->PressKey(key);
  72. } else if (state == SDL_RELEASED) {
  73. input_subsystem->GetKeyboard()->ReleaseKey(key);
  74. }
  75. }
  76. bool EmuWindow_SDL2::IsOpen() const {
  77. return is_open;
  78. }
  79. bool EmuWindow_SDL2::IsShown() const {
  80. return is_shown;
  81. }
  82. void EmuWindow_SDL2::OnResize() {
  83. int width, height;
  84. SDL_GetWindowSize(render_window, &width, &height);
  85. UpdateCurrentFramebufferLayout(width, height);
  86. }
  87. void EmuWindow_SDL2::Fullscreen() {
  88. if (SDL_SetWindowFullscreen(render_window, SDL_WINDOW_FULLSCREEN) == 0) {
  89. return;
  90. }
  91. LOG_ERROR(Frontend, "Fullscreening failed: {}", SDL_GetError());
  92. // Try a different fullscreening method
  93. LOG_INFO(Frontend, "Attempting to use borderless fullscreen...");
  94. if (SDL_SetWindowFullscreen(render_window, SDL_WINDOW_FULLSCREEN_DESKTOP) == 0) {
  95. return;
  96. }
  97. LOG_ERROR(Frontend, "Borderless fullscreening failed: {}", SDL_GetError());
  98. // Fallback algorithm: Maximise window.
  99. // Works on all systems (unless something is seriously wrong), so no fallback for this one.
  100. LOG_INFO(Frontend, "Falling back on a maximised window...");
  101. SDL_MaximizeWindow(render_window);
  102. }
  103. void EmuWindow_SDL2::PollEvents() {
  104. SDL_Event event;
  105. // SDL_PollEvent returns 0 when there are no more events in the event queue
  106. while (SDL_PollEvent(&event)) {
  107. switch (event.type) {
  108. case SDL_WINDOWEVENT:
  109. switch (event.window.event) {
  110. case SDL_WINDOWEVENT_SIZE_CHANGED:
  111. case SDL_WINDOWEVENT_RESIZED:
  112. case SDL_WINDOWEVENT_MAXIMIZED:
  113. case SDL_WINDOWEVENT_RESTORED:
  114. OnResize();
  115. break;
  116. case SDL_WINDOWEVENT_MINIMIZED:
  117. case SDL_WINDOWEVENT_EXPOSED:
  118. is_shown = event.window.event == SDL_WINDOWEVENT_EXPOSED;
  119. OnResize();
  120. break;
  121. case SDL_WINDOWEVENT_CLOSE:
  122. is_open = false;
  123. break;
  124. }
  125. break;
  126. case SDL_KEYDOWN:
  127. case SDL_KEYUP:
  128. OnKeyEvent(static_cast<int>(event.key.keysym.scancode), event.key.state);
  129. break;
  130. case SDL_MOUSEMOTION:
  131. // ignore if it came from touch
  132. if (event.button.which != SDL_TOUCH_MOUSEID)
  133. OnMouseMotion(event.motion.x, event.motion.y);
  134. break;
  135. case SDL_MOUSEBUTTONDOWN:
  136. case SDL_MOUSEBUTTONUP:
  137. // ignore if it came from touch
  138. if (event.button.which != SDL_TOUCH_MOUSEID) {
  139. OnMouseButton(event.button.button, event.button.state, event.button.x,
  140. event.button.y);
  141. }
  142. break;
  143. case SDL_FINGERDOWN:
  144. OnFingerDown(event.tfinger.x, event.tfinger.y);
  145. break;
  146. case SDL_FINGERMOTION:
  147. OnFingerMotion(event.tfinger.x, event.tfinger.y);
  148. break;
  149. case SDL_FINGERUP:
  150. OnFingerUp();
  151. break;
  152. case SDL_QUIT:
  153. is_open = false;
  154. break;
  155. default:
  156. break;
  157. }
  158. }
  159. const u32 current_time = SDL_GetTicks();
  160. if (current_time > last_time + 2000) {
  161. const auto results = Core::System::GetInstance().GetAndResetPerfStats();
  162. const auto title =
  163. fmt::format("yuzu {} | {}-{} | FPS: {:.0f} ({:.0f}%)", Common::g_build_fullname,
  164. Common::g_scm_branch, Common::g_scm_desc, results.game_fps,
  165. results.emulation_speed * 100.0);
  166. SDL_SetWindowTitle(render_window, title.c_str());
  167. last_time = current_time;
  168. }
  169. }
  170. void EmuWindow_SDL2::OnMinimalClientAreaChangeRequest(std::pair<unsigned, unsigned> minimal_size) {
  171. SDL_SetWindowMinimumSize(render_window, minimal_size.first, minimal_size.second);
  172. }