yuzu/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
James Rowe 09ac66388c Input: Remove global variables from SDL Input
Changes the interface as well to remove any unique methods that
frontends needed to call such as StartJoystickEventHandler by
conditionally starting the polling thread only if the frontend hasn't
started it already. Additionally, moves all global state into a single
SDLState class in order to guarantee that the destructors are called in
the proper order
2019-03-02 19:09:34 +01:00

281 lines
9 KiB
C++

// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <cstdlib>
#include <string>
#define SDL_MAIN_HANDLED
#include <SDL.h>
#include <fmt/format.h>
#include <glad/glad.h>
#include "common/logging/log.h"
#include "common/scm_rev.h"
#include "common/string_util.h"
#include "core/settings.h"
#include "input_common/keyboard.h"
#include "input_common/main.h"
#include "input_common/motion_emu.h"
#include "input_common/sdl/sdl.h"
#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) {
TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0));
InputCommon::GetMotionEmu()->Tilt(x, y);
}
void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) {
if (button == SDL_BUTTON_LEFT) {
if (state == SDL_PRESSED) {
TouchPressed((unsigned)std::max(x, 0), (unsigned)std::max(y, 0));
} else {
TouchReleased();
}
} else if (button == SDL_BUTTON_RIGHT) {
if (state == SDL_PRESSED) {
InputCommon::GetMotionEmu()->BeginTilt(x, y);
} else {
InputCommon::GetMotionEmu()->EndTilt();
}
}
}
std::pair<unsigned, unsigned> EmuWindow_SDL2::TouchToPixelPos(float touch_x, float touch_y) const {
int w, h;
SDL_GetWindowSize(render_window, &w, &h);
touch_x *= w;
touch_y *= h;
return {static_cast<unsigned>(std::max(std::round(touch_x), 0.0f)),
static_cast<unsigned>(std::max(std::round(touch_y), 0.0f))};
}
void EmuWindow_SDL2::OnFingerDown(float x, float y) {
// TODO(NeatNit): keep track of multitouch using the fingerID and a dictionary of some kind
// This isn't critical because the best we can do when we have that is to average them, like the
// 3DS does
const auto [px, py] = TouchToPixelPos(x, y);
TouchPressed(px, py);
}
void EmuWindow_SDL2::OnFingerMotion(float x, float y) {
const auto [px, py] = TouchToPixelPos(x, y);
TouchMoved(px, py);
}
void EmuWindow_SDL2::OnFingerUp() {
TouchReleased();
}
void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) {
if (state == SDL_PRESSED) {
InputCommon::GetKeyboard()->PressKey(key);
} else if (state == SDL_RELEASED) {
InputCommon::GetKeyboard()->ReleaseKey(key);
}
}
bool EmuWindow_SDL2::IsOpen() const {
return is_open;
}
void EmuWindow_SDL2::OnResize() {
int width, height;
SDL_GetWindowSize(render_window, &width, &height);
UpdateCurrentFramebufferLayout(width, height);
}
void EmuWindow_SDL2::Fullscreen() {
if (SDL_SetWindowFullscreen(render_window, SDL_WINDOW_FULLSCREEN) == 0) {
return;
}
LOG_ERROR(Frontend, "Fullscreening failed: {}", SDL_GetError());
// Try a different fullscreening method
LOG_INFO(Frontend, "Attempting to use borderless fullscreen...");
if (SDL_SetWindowFullscreen(render_window, SDL_WINDOW_FULLSCREEN_DESKTOP) == 0) {
return;
}
LOG_ERROR(Frontend, "Borderless fullscreening failed: {}", SDL_GetError());
// Fallback algorithm: Maximise window.
// Works on all systems (unless something is seriously wrong), so no fallback for this one.
LOG_INFO(Frontend, "Falling back on a maximised window...");
SDL_MaximizeWindow(render_window);
}
bool EmuWindow_SDL2::SupportsRequiredGLExtensions() {
std::vector<std::string> unsupported_ext;
if (!GLAD_GL_ARB_direct_state_access)
unsupported_ext.push_back("ARB_direct_state_access");
if (!GLAD_GL_ARB_vertex_type_10f_11f_11f_rev)
unsupported_ext.push_back("ARB_vertex_type_10f_11f_11f_rev");
if (!GLAD_GL_ARB_texture_mirror_clamp_to_edge)
unsupported_ext.push_back("ARB_texture_mirror_clamp_to_edge");
if (!GLAD_GL_ARB_multi_bind)
unsupported_ext.push_back("ARB_multi_bind");
// Extensions required to support some texture formats.
if (!GLAD_GL_EXT_texture_compression_s3tc)
unsupported_ext.push_back("EXT_texture_compression_s3tc");
if (!GLAD_GL_ARB_texture_compression_rgtc)
unsupported_ext.push_back("ARB_texture_compression_rgtc");
if (!GLAD_GL_ARB_depth_buffer_float)
unsupported_ext.push_back("ARB_depth_buffer_float");
for (const std::string& ext : unsupported_ext)
LOG_CRITICAL(Frontend, "Unsupported GL extension: {}", ext);
return unsupported_ext.empty();
}
EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) {
// Initialize the window
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) {
LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting...");
exit(1);
}
InputCommon::Init();
SDL_SetMainReady();
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0);
std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_fullname,
Common::g_scm_branch, Common::g_scm_desc);
render_window =
SDL_CreateWindow(window_title.c_str(),
SDL_WINDOWPOS_UNDEFINED, // x position
SDL_WINDOWPOS_UNDEFINED, // y position
Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height,
SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
if (render_window == nullptr) {
LOG_CRITICAL(Frontend, "Failed to create SDL2 window! {}", SDL_GetError());
exit(1);
}
if (fullscreen) {
Fullscreen();
}
gl_context = SDL_GL_CreateContext(render_window);
if (gl_context == nullptr) {
LOG_CRITICAL(Frontend, "Failed to create SDL2 GL context! {}", SDL_GetError());
exit(1);
}
if (!gladLoadGLLoader(static_cast<GLADloadproc>(SDL_GL_GetProcAddress))) {
LOG_CRITICAL(Frontend, "Failed to initialize GL functions! {}", SDL_GetError());
exit(1);
}
if (!SupportsRequiredGLExtensions()) {
LOG_CRITICAL(Frontend, "GPU does not support all required OpenGL extensions! Exiting...");
exit(1);
}
OnResize();
OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
SDL_PumpEvents();
SDL_GL_SetSwapInterval(false);
LOG_INFO(Frontend, "yuzu Version: {} | {}-{}", Common::g_build_fullname, Common::g_scm_branch,
Common::g_scm_desc);
Settings::LogSettings();
DoneCurrent();
}
EmuWindow_SDL2::~EmuWindow_SDL2() {
InputCommon::Shutdown();
SDL_GL_DeleteContext(gl_context);
SDL_Quit();
}
void EmuWindow_SDL2::SwapBuffers() {
SDL_GL_SwapWindow(render_window);
}
void EmuWindow_SDL2::PollEvents() {
SDL_Event event;
// SDL_PollEvent returns 0 when there are no more events in the event queue
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_WINDOWEVENT:
switch (event.window.event) {
case SDL_WINDOWEVENT_SIZE_CHANGED:
case SDL_WINDOWEVENT_RESIZED:
case SDL_WINDOWEVENT_MAXIMIZED:
case SDL_WINDOWEVENT_RESTORED:
case SDL_WINDOWEVENT_MINIMIZED:
OnResize();
break;
case SDL_WINDOWEVENT_CLOSE:
is_open = false;
break;
}
break;
case SDL_KEYDOWN:
case SDL_KEYUP:
OnKeyEvent(static_cast<int>(event.key.keysym.scancode), event.key.state);
break;
case SDL_MOUSEMOTION:
// ignore if it came from touch
if (event.button.which != SDL_TOUCH_MOUSEID)
OnMouseMotion(event.motion.x, event.motion.y);
break;
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
// ignore if it came from touch
if (event.button.which != SDL_TOUCH_MOUSEID) {
OnMouseButton(event.button.button, event.button.state, event.button.x,
event.button.y);
}
break;
case SDL_FINGERDOWN:
OnFingerDown(event.tfinger.x, event.tfinger.y);
break;
case SDL_FINGERMOTION:
OnFingerMotion(event.tfinger.x, event.tfinger.y);
break;
case SDL_FINGERUP:
OnFingerUp();
break;
case SDL_QUIT:
is_open = false;
break;
default:
break;
}
}
}
void EmuWindow_SDL2::MakeCurrent() {
SDL_GL_MakeCurrent(render_window, gl_context);
}
void EmuWindow_SDL2::DoneCurrent() {
SDL_GL_MakeCurrent(render_window, nullptr);
}
void EmuWindow_SDL2::OnMinimalClientAreaChangeRequest(
const std::pair<unsigned, unsigned>& minimal_size) {
SDL_SetWindowMinimumSize(render_window, minimal_size.first, minimal_size.second);
}