2013-04-06 14:34:46 +00:00
|
|
|
// SDL/EGL implementation of the framework.
|
|
|
|
// This is quite messy due to platform-specific implementations and #ifdef's.
|
2014-05-30 15:21:43 +00:00
|
|
|
// Note: SDL1.2 implementation is deprecated and will soon be replaced by SDL2.0.
|
|
|
|
// If your platform is not supported, it is suggested to use Qt instead.
|
2012-04-10 09:59:57 +00:00
|
|
|
|
2013-12-13 10:03:40 +00:00
|
|
|
#include <unistd.h>
|
2013-11-05 02:58:30 +00:00
|
|
|
#include <pwd.h>
|
2014-05-30 15:21:43 +00:00
|
|
|
|
2012-11-25 21:25:54 +00:00
|
|
|
#include "SDL.h"
|
2014-05-30 15:21:43 +00:00
|
|
|
#include "SDL/SDLJoystick.h"
|
2014-06-07 14:55:51 +00:00
|
|
|
SDLJoystick *joystick = NULL;
|
2013-12-13 11:49:38 +00:00
|
|
|
|
2016-10-12 15:32:52 +00:00
|
|
|
#if PPSSPP_PLATFORM(RPI)
|
2014-02-09 22:31:31 +00:00
|
|
|
#include <bcm_host.h>
|
|
|
|
#endif
|
|
|
|
|
2018-01-21 17:03:19 +00:00
|
|
|
#include <atomic>
|
2013-12-13 11:49:38 +00:00
|
|
|
#include <algorithm>
|
2017-10-20 09:53:07 +00:00
|
|
|
#include <cassert>
|
2017-12-13 22:22:21 +00:00
|
|
|
#include <cmath>
|
2018-01-21 17:03:19 +00:00
|
|
|
#include <thread>
|
2013-12-13 11:49:38 +00:00
|
|
|
|
2012-04-10 09:59:57 +00:00
|
|
|
#include "base/display.h"
|
|
|
|
#include "base/logging.h"
|
|
|
|
#include "base/timeutil.h"
|
2017-12-07 16:02:00 +00:00
|
|
|
#include "ext/glslang/glslang/Public/ShaderLang.h"
|
2012-04-10 09:59:57 +00:00
|
|
|
#include "input/input_state.h"
|
2013-06-30 02:15:55 +00:00
|
|
|
#include "input/keycodes.h"
|
2012-06-03 17:01:08 +00:00
|
|
|
#include "net/resolve.h"
|
2013-11-05 02:58:30 +00:00
|
|
|
#include "base/NKCodeFromSDL.h"
|
2013-06-30 02:15:55 +00:00
|
|
|
#include "util/const_map.h"
|
2014-11-21 18:20:19 +00:00
|
|
|
#include "util/text/utf8.h"
|
2013-11-04 13:49:30 +00:00
|
|
|
#include "math/math_util.h"
|
2018-01-21 17:03:19 +00:00
|
|
|
#include "thin3d/GLRenderManager.h"
|
2018-01-21 17:47:07 +00:00
|
|
|
#include "thread/threadutil.h"
|
2017-12-13 22:22:21 +00:00
|
|
|
#include "math.h"
|
2017-12-13 21:58:45 +00:00
|
|
|
|
2017-12-21 21:09:26 +00:00
|
|
|
#if !defined(__APPLE__)
|
|
|
|
#include "SDL_syswm.h"
|
|
|
|
#endif
|
|
|
|
|
2017-12-13 22:22:21 +00:00
|
|
|
#if defined(VK_USE_PLATFORM_XLIB_KHR)
|
2017-12-13 21:58:45 +00:00
|
|
|
#include <X11/Xlib.h>
|
|
|
|
#include <X11/Xutil.h>
|
2017-12-15 15:03:48 +00:00
|
|
|
#elif defined(VK_USE_PLATFORM_XCB_KHR)
|
|
|
|
#include <X11/Xlib.h>
|
|
|
|
#include <X11/Xutil.h>
|
|
|
|
#include <X11/Xlib-xcb.h>
|
2017-12-13 21:58:45 +00:00
|
|
|
#endif
|
2014-02-05 13:46:21 +00:00
|
|
|
|
2017-12-15 14:29:19 +00:00
|
|
|
#if defined(USING_EGL)
|
|
|
|
#include "EGL/egl.h"
|
|
|
|
#endif
|
|
|
|
|
2014-06-22 09:17:03 +00:00
|
|
|
#include "Core/System.h"
|
2013-11-26 15:30:10 +00:00
|
|
|
#include "Core/Core.h"
|
|
|
|
#include "Core/Config.h"
|
2015-12-31 15:59:40 +00:00
|
|
|
#include "Common/GraphicsContext.h"
|
2018-02-04 11:26:35 +00:00
|
|
|
#include "SDLGLGraphicsContext.h"
|
|
|
|
#include "SDLVulkanGraphicsContext.h"
|
|
|
|
|
2013-11-22 00:59:18 +00:00
|
|
|
|
2014-06-22 08:53:06 +00:00
|
|
|
GlobalUIState lastUIState = UISTATE_MENU;
|
2014-06-22 08:34:22 +00:00
|
|
|
GlobalUIState GetUIState();
|
2013-11-05 02:58:30 +00:00
|
|
|
|
2014-02-07 10:33:31 +00:00
|
|
|
static bool g_ToggleFullScreenNextFrame = false;
|
2017-07-30 14:51:53 +00:00
|
|
|
static int g_ToggleFullScreenType;
|
2014-02-07 10:33:31 +00:00
|
|
|
static int g_QuitRequested = 0;
|
2014-01-03 14:15:35 +00:00
|
|
|
|
2014-05-02 21:46:24 +00:00
|
|
|
static int g_DesktopWidth = 0;
|
|
|
|
static int g_DesktopHeight = 0;
|
|
|
|
|
2014-05-30 15:21:43 +00:00
|
|
|
#if defined(USING_EGL)
|
2014-09-26 23:47:19 +00:00
|
|
|
|
|
|
|
static EGLDisplay g_eglDisplay = NULL;
|
|
|
|
static EGLContext g_eglContext = NULL;
|
|
|
|
static EGLSurface g_eglSurface = NULL;
|
|
|
|
#ifdef USING_FBDEV
|
|
|
|
static EGLNativeDisplayType g_Display = NULL;
|
|
|
|
#else
|
|
|
|
static Display* g_Display = NULL;
|
|
|
|
#endif
|
|
|
|
static NativeWindowType g_Window = (NativeWindowType)NULL;
|
2013-01-12 15:38:37 +00:00
|
|
|
|
2013-01-14 11:32:39 +00:00
|
|
|
int8_t CheckEGLErrors(const std::string& file, uint16_t line) {
|
2013-01-12 15:38:37 +00:00
|
|
|
EGLenum error;
|
2013-01-14 11:32:39 +00:00
|
|
|
std::string errortext;
|
2013-01-12 15:38:37 +00:00
|
|
|
|
|
|
|
error = eglGetError();
|
|
|
|
switch (error)
|
|
|
|
{
|
|
|
|
case EGL_SUCCESS: case 0: return 0;
|
|
|
|
case EGL_NOT_INITIALIZED: errortext = "EGL_NOT_INITIALIZED"; break;
|
|
|
|
case EGL_BAD_ACCESS: errortext = "EGL_BAD_ACCESS"; break;
|
|
|
|
case EGL_BAD_ALLOC: errortext = "EGL_BAD_ALLOC"; break;
|
|
|
|
case EGL_BAD_ATTRIBUTE: errortext = "EGL_BAD_ATTRIBUTE"; break;
|
|
|
|
case EGL_BAD_CONTEXT: errortext = "EGL_BAD_CONTEXT"; break;
|
|
|
|
case EGL_BAD_CONFIG: errortext = "EGL_BAD_CONFIG"; break;
|
|
|
|
case EGL_BAD_CURRENT_SURFACE: errortext = "EGL_BAD_CURRENT_SURFACE"; break;
|
|
|
|
case EGL_BAD_DISPLAY: errortext = "EGL_BAD_DISPLAY"; break;
|
|
|
|
case EGL_BAD_SURFACE: errortext = "EGL_BAD_SURFACE"; break;
|
|
|
|
case EGL_BAD_MATCH: errortext = "EGL_BAD_MATCH"; break;
|
|
|
|
case EGL_BAD_PARAMETER: errortext = "EGL_BAD_PARAMETER"; break;
|
|
|
|
case EGL_BAD_NATIVE_PIXMAP: errortext = "EGL_BAD_NATIVE_PIXMAP"; break;
|
|
|
|
case EGL_BAD_NATIVE_WINDOW: errortext = "EGL_BAD_NATIVE_WINDOW"; break;
|
|
|
|
default: errortext = "unknown"; break;
|
|
|
|
}
|
|
|
|
printf( "ERROR: EGL Error detected in file %s at line %d: %s (0x%X)\n", file.c_str(), line, errortext.c_str(), error );
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
#define EGL_ERROR(str, check) { \
|
|
|
|
if (check) CheckEGLErrors( __FILE__, __LINE__ ); \
|
|
|
|
printf("EGL ERROR: " str "\n"); \
|
|
|
|
return 1; \
|
|
|
|
}
|
|
|
|
|
2013-01-14 11:32:39 +00:00
|
|
|
int8_t EGL_Open() {
|
2014-05-30 15:21:43 +00:00
|
|
|
#ifdef USING_FBDEV
|
|
|
|
g_Display = ((EGLNativeDisplayType)0);
|
2013-05-06 13:56:41 +00:00
|
|
|
#else
|
2013-01-12 15:38:37 +00:00
|
|
|
if ((g_Display = XOpenDisplay(NULL)) == NULL)
|
|
|
|
EGL_ERROR("Unable to get display!", false);
|
2013-05-06 13:56:41 +00:00
|
|
|
#endif
|
2013-01-12 15:38:37 +00:00
|
|
|
if ((g_eglDisplay = eglGetDisplay((NativeDisplayType)g_Display)) == EGL_NO_DISPLAY)
|
|
|
|
EGL_ERROR("Unable to create EGL display.", true);
|
|
|
|
if (eglInitialize(g_eglDisplay, NULL, NULL) != EGL_TRUE)
|
|
|
|
EGL_ERROR("Unable to initialize EGL display.", true);
|
2013-01-14 11:32:39 +00:00
|
|
|
return 0;
|
2013-01-12 15:38:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int8_t EGL_Init() {
|
2014-02-07 13:18:45 +00:00
|
|
|
EGLConfig g_eglConfig;
|
2013-01-12 15:38:37 +00:00
|
|
|
EGLint g_numConfigs = 0;
|
|
|
|
EGLint attrib_list[]= {
|
2014-05-30 15:21:43 +00:00
|
|
|
// TODO: Should cycle through fallbacks, like on Android
|
|
|
|
#ifdef USING_FBDEV
|
2013-01-12 15:38:37 +00:00
|
|
|
EGL_RED_SIZE, 5,
|
|
|
|
EGL_GREEN_SIZE, 6,
|
|
|
|
EGL_BLUE_SIZE, 5,
|
2013-03-22 15:14:18 +00:00
|
|
|
#endif
|
2013-01-12 15:38:37 +00:00
|
|
|
EGL_DEPTH_SIZE, 16,
|
|
|
|
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
|
2014-11-18 03:34:49 +00:00
|
|
|
#ifdef USING_GLES2
|
2013-01-12 15:38:37 +00:00
|
|
|
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
|
2014-11-18 03:34:49 +00:00
|
|
|
#else
|
|
|
|
EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
|
|
|
|
#endif
|
2013-01-12 15:38:37 +00:00
|
|
|
EGL_SAMPLE_BUFFERS, 0,
|
|
|
|
EGL_SAMPLES, 0,
|
|
|
|
EGL_NONE};
|
|
|
|
|
|
|
|
const EGLint attributes[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
|
|
|
|
|
2013-03-22 15:14:18 +00:00
|
|
|
EGLBoolean result = eglChooseConfig(g_eglDisplay, attrib_list, &g_eglConfig, 1, &g_numConfigs);
|
2013-01-14 11:32:39 +00:00
|
|
|
if (result != EGL_TRUE || g_numConfigs == 0) EGL_ERROR("Unable to query for available configs.", true);
|
2013-01-12 15:38:37 +00:00
|
|
|
|
2013-03-22 15:14:18 +00:00
|
|
|
g_eglContext = eglCreateContext(g_eglDisplay, g_eglConfig, NULL, attributes );
|
2013-01-12 15:38:37 +00:00
|
|
|
if (g_eglContext == EGL_NO_CONTEXT) EGL_ERROR("Unable to create GLES context!", true);
|
|
|
|
|
2017-12-21 13:13:55 +00:00
|
|
|
#if !defined(USING_FBDEV) && !defined(__APPLE__)
|
2014-09-26 23:47:19 +00:00
|
|
|
//Get the SDL window handle
|
2013-01-12 15:38:37 +00:00
|
|
|
SDL_SysWMinfo sysInfo; //Will hold our Window information
|
|
|
|
SDL_VERSION(&sysInfo.version); //Set SDL version
|
|
|
|
g_Window = (NativeWindowType)sysInfo.info.x11.window;
|
2017-12-21 13:13:55 +00:00
|
|
|
#else
|
|
|
|
g_Window = (NativeWindowType)NULL;
|
2013-05-06 13:56:41 +00:00
|
|
|
#endif
|
2013-01-12 15:38:37 +00:00
|
|
|
g_eglSurface = eglCreateWindowSurface(g_eglDisplay, g_eglConfig, g_Window, 0);
|
2014-01-21 14:57:15 +00:00
|
|
|
if (g_eglSurface == EGL_NO_SURFACE)
|
|
|
|
EGL_ERROR("Unable to create EGL surface!", true);
|
2013-01-12 15:38:37 +00:00
|
|
|
|
|
|
|
if (eglMakeCurrent(g_eglDisplay, g_eglSurface, g_eglSurface, g_eglContext) != EGL_TRUE)
|
|
|
|
EGL_ERROR("Unable to make GLES context current.", true);
|
2013-05-06 13:56:41 +00:00
|
|
|
|
2013-01-12 15:38:37 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void EGL_Close() {
|
2014-02-07 13:18:45 +00:00
|
|
|
if (g_eglDisplay != NULL) {
|
2013-01-12 15:38:37 +00:00
|
|
|
eglMakeCurrent(g_eglDisplay, NULL, NULL, EGL_NO_CONTEXT);
|
|
|
|
if (g_eglContext != NULL) {
|
|
|
|
eglDestroyContext(g_eglDisplay, g_eglContext);
|
|
|
|
}
|
|
|
|
if (g_eglSurface != NULL) {
|
|
|
|
eglDestroySurface(g_eglDisplay, g_eglSurface);
|
|
|
|
}
|
|
|
|
eglTerminate(g_eglDisplay);
|
|
|
|
g_eglDisplay = NULL;
|
|
|
|
}
|
|
|
|
if (g_Display != NULL) {
|
2014-09-26 23:47:19 +00:00
|
|
|
#if !defined(USING_FBDEV)
|
2013-01-12 15:38:37 +00:00
|
|
|
XCloseDisplay(g_Display);
|
2014-09-26 23:47:19 +00:00
|
|
|
#endif
|
2013-01-12 15:38:37 +00:00
|
|
|
g_Display = NULL;
|
|
|
|
}
|
|
|
|
g_eglSurface = NULL;
|
|
|
|
g_eglContext = NULL;
|
|
|
|
}
|
2014-02-05 13:46:21 +00:00
|
|
|
#endif
|
2013-01-12 15:38:37 +00:00
|
|
|
|
2017-12-15 14:29:19 +00:00
|
|
|
|
2017-02-06 10:20:27 +00:00
|
|
|
int getDisplayNumber(void) {
|
|
|
|
int displayNumber = 0;
|
|
|
|
char * displayNumberStr;
|
2014-10-15 20:35:03 +00:00
|
|
|
|
2017-02-06 10:20:27 +00:00
|
|
|
//get environment
|
|
|
|
displayNumberStr=getenv("SDL_VIDEO_FULLSCREEN_HEAD");
|
2014-10-15 20:35:03 +00:00
|
|
|
|
2018-02-04 11:26:35 +00:00
|
|
|
if (displayNumberStr) {
|
2017-02-06 10:20:27 +00:00
|
|
|
displayNumber = atoi(displayNumberStr);
|
|
|
|
}
|
2014-10-15 20:35:03 +00:00
|
|
|
|
2017-02-06 10:20:27 +00:00
|
|
|
return displayNumber;
|
2014-09-22 18:08:02 +00:00
|
|
|
}
|
|
|
|
|
2013-01-12 15:38:37 +00:00
|
|
|
// Simple implementations of System functions
|
2012-07-16 13:00:52 +00:00
|
|
|
|
|
|
|
|
2012-04-10 09:59:57 +00:00
|
|
|
void SystemToast(const char *text) {
|
2012-07-05 21:30:35 +00:00
|
|
|
#ifdef _WIN32
|
2012-07-06 20:32:32 +00:00
|
|
|
MessageBox(0, text, "Toast!", MB_ICONINFORMATION);
|
2012-07-05 21:30:35 +00:00
|
|
|
#else
|
2012-07-06 20:32:32 +00:00
|
|
|
puts(text);
|
2012-07-05 21:30:35 +00:00
|
|
|
#endif
|
2012-04-10 09:59:57 +00:00
|
|
|
}
|
|
|
|
|
2012-04-16 21:30:13 +00:00
|
|
|
void ShowKeyboard() {
|
|
|
|
// Irrelevant on PC
|
|
|
|
}
|
|
|
|
|
2012-04-10 09:59:57 +00:00
|
|
|
void Vibrate(int length_ms) {
|
|
|
|
// Ignore on PC
|
|
|
|
}
|
|
|
|
|
2013-12-04 16:38:37 +00:00
|
|
|
void System_SendMessage(const char *command, const char *parameter) {
|
2014-01-03 14:15:35 +00:00
|
|
|
if (!strcmp(command, "toggle_fullscreen")) {
|
|
|
|
g_ToggleFullScreenNextFrame = true;
|
2017-07-30 14:51:53 +00:00
|
|
|
if (strcmp(parameter, "1") == 0) {
|
|
|
|
g_ToggleFullScreenType = 1;
|
|
|
|
} else if (strcmp(parameter, "0") == 0) {
|
|
|
|
g_ToggleFullScreenType = 0;
|
|
|
|
} else {
|
|
|
|
// Just toggle.
|
|
|
|
g_ToggleFullScreenType = -1;
|
|
|
|
}
|
2014-02-07 10:33:31 +00:00
|
|
|
} else if (!strcmp(command, "finish")) {
|
|
|
|
// Do a clean exit
|
|
|
|
g_QuitRequested = true;
|
2014-01-03 14:15:35 +00:00
|
|
|
}
|
2013-12-04 16:38:37 +00:00
|
|
|
}
|
|
|
|
|
2015-12-17 21:41:50 +00:00
|
|
|
void System_AskForPermission(SystemPermission permission) {}
|
|
|
|
PermissionStatus System_GetPermissionStatus(SystemPermission permission) { return PERMISSION_STATUS_GRANTED; }
|
|
|
|
|
2013-12-04 16:38:37 +00:00
|
|
|
void LaunchBrowser(const char *url) {
|
2015-05-26 14:51:53 +00:00
|
|
|
#if defined(MOBILE_DEVICE)
|
|
|
|
ILOG("Would have gone to %s but LaunchBrowser is not implemented on this platform", url);
|
|
|
|
#elif defined(_WIN32)
|
2012-04-10 09:59:57 +00:00
|
|
|
ShellExecute(NULL, "open", url, NULL, NULL, SW_SHOWNORMAL);
|
2015-05-26 14:51:53 +00:00
|
|
|
#elif defined(__APPLE__)
|
|
|
|
std::string command = std::string("open ") + url;
|
|
|
|
system(command.c_str());
|
|
|
|
#else
|
2013-10-30 21:48:12 +00:00
|
|
|
std::string command = std::string("xdg-open ") + url;
|
2014-01-30 16:33:48 +00:00
|
|
|
int err = system(command.c_str());
|
|
|
|
if (err) {
|
2014-01-30 16:23:04 +00:00
|
|
|
ILOG("Would have gone to %s but xdg-utils seems not to be installed", url)
|
|
|
|
}
|
2012-04-10 09:59:57 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2013-12-04 16:38:37 +00:00
|
|
|
void LaunchMarket(const char *url) {
|
2015-05-26 14:51:53 +00:00
|
|
|
#if defined(MOBILE_DEVICE)
|
|
|
|
ILOG("Would have gone to %s but LaunchMarket is not implemented on this platform", url);
|
|
|
|
#elif defined(_WIN32)
|
2012-04-10 09:59:57 +00:00
|
|
|
ShellExecute(NULL, "open", url, NULL, NULL, SW_SHOWNORMAL);
|
2015-05-26 14:51:53 +00:00
|
|
|
#elif defined(__APPLE__)
|
|
|
|
std::string command = std::string("open ") + url;
|
|
|
|
system(command.c_str());
|
|
|
|
#else
|
2013-10-30 21:48:12 +00:00
|
|
|
std::string command = std::string("xdg-open ") + url;
|
2014-01-30 16:33:48 +00:00
|
|
|
int err = system(command.c_str());
|
|
|
|
if (err) {
|
2014-01-30 16:23:04 +00:00
|
|
|
ILOG("Would have gone to %s but xdg-utils seems not to be installed", url)
|
|
|
|
}
|
2012-04-10 09:59:57 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2013-12-04 16:38:37 +00:00
|
|
|
void LaunchEmail(const char *email_address) {
|
2015-05-26 14:51:53 +00:00
|
|
|
#if defined(MOBILE_DEVICE)
|
|
|
|
ILOG("Would have opened your email client for %s but LaunchEmail is not implemented on this platform", email_address);
|
|
|
|
#elif defined(_WIN32)
|
2012-04-10 09:59:57 +00:00
|
|
|
ShellExecute(NULL, "open", (std::string("mailto:") + email_address).c_str(), NULL, NULL, SW_SHOWNORMAL);
|
2015-05-26 14:51:53 +00:00
|
|
|
#elif defined(__APPLE__)
|
|
|
|
std::string command = std::string("open mailto:") + email_address;
|
|
|
|
system(command.c_str());
|
|
|
|
#else
|
2013-11-01 17:09:36 +00:00
|
|
|
std::string command = std::string("xdg-email ") + email_address;
|
2014-01-30 16:33:48 +00:00
|
|
|
int err = system(command.c_str());
|
|
|
|
if (err) {
|
2014-01-30 16:23:04 +00:00
|
|
|
ILOG("Would have gone to %s but xdg-utils seems not to be installed", email_address)
|
|
|
|
}
|
2012-04-10 09:59:57 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2013-09-04 09:28:19 +00:00
|
|
|
std::string System_GetProperty(SystemProperty prop) {
|
|
|
|
switch (prop) {
|
|
|
|
case SYSPROP_NAME:
|
2013-11-26 06:38:00 +00:00
|
|
|
#ifdef _WIN32
|
|
|
|
return "SDL:Windows";
|
|
|
|
#elif __linux__
|
|
|
|
return "SDL:Linux";
|
|
|
|
#elif __APPLE__
|
|
|
|
return "SDL:OSX";
|
|
|
|
#else
|
2013-09-04 09:28:19 +00:00
|
|
|
return "SDL:";
|
2013-11-26 06:38:00 +00:00
|
|
|
#endif
|
2013-09-04 09:28:19 +00:00
|
|
|
case SYSPROP_LANGREGION:
|
|
|
|
return "en_US";
|
|
|
|
default:
|
|
|
|
return "";
|
|
|
|
}
|
2013-08-17 22:14:25 +00:00
|
|
|
}
|
|
|
|
|
2015-01-11 13:15:26 +00:00
|
|
|
int System_GetPropertyInt(SystemProperty prop) {
|
|
|
|
switch (prop) {
|
|
|
|
case SYSPROP_AUDIO_SAMPLE_RATE:
|
|
|
|
return 44100;
|
2015-01-13 23:27:21 +00:00
|
|
|
case SYSPROP_DISPLAY_REFRESH_RATE:
|
|
|
|
return 60000;
|
2015-04-03 09:13:38 +00:00
|
|
|
case SYSPROP_DEVICE_TYPE:
|
2015-05-26 14:51:53 +00:00
|
|
|
#if defined(MOBILE_DEVICE)
|
2015-04-03 09:13:38 +00:00
|
|
|
return DEVICE_TYPE_MOBILE;
|
|
|
|
#else
|
2015-05-26 14:51:53 +00:00
|
|
|
return DEVICE_TYPE_DESKTOP;
|
2015-04-03 09:13:38 +00:00
|
|
|
#endif
|
2017-04-30 00:35:12 +00:00
|
|
|
default:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool System_GetPropertyBool(SystemProperty prop) {
|
|
|
|
switch (prop) {
|
2017-03-07 09:33:53 +00:00
|
|
|
case SYSPROP_HAS_BACK_BUTTON:
|
2017-04-30 00:35:12 +00:00
|
|
|
return true;
|
2017-04-05 14:21:08 +00:00
|
|
|
case SYSPROP_APP_GOLD:
|
|
|
|
#ifdef GOLD
|
2017-04-30 00:35:12 +00:00
|
|
|
return true;
|
2017-04-05 14:21:08 +00:00
|
|
|
#else
|
2017-04-30 00:35:12 +00:00
|
|
|
return false;
|
2017-04-05 14:21:08 +00:00
|
|
|
#endif
|
2015-01-11 13:15:26 +00:00
|
|
|
default:
|
2017-04-30 00:35:12 +00:00
|
|
|
return false;
|
2015-01-11 13:15:26 +00:00
|
|
|
}
|
|
|
|
}
|
2014-07-20 10:04:22 +00:00
|
|
|
|
2012-04-10 09:59:57 +00:00
|
|
|
extern void mixaudio(void *userdata, Uint8 *stream, int len) {
|
2012-10-31 13:01:32 +00:00
|
|
|
NativeMix((short *)stream, len / 4);
|
2012-04-10 09:59:57 +00:00
|
|
|
}
|
|
|
|
|
2013-12-16 13:05:51 +00:00
|
|
|
// returns -1 on failure
|
2014-01-21 14:57:15 +00:00
|
|
|
static int parseInt(const char *str) {
|
2013-12-16 13:05:51 +00:00
|
|
|
int val;
|
|
|
|
int retval = sscanf(str, "%d", &val);
|
2014-01-21 14:57:15 +00:00
|
|
|
printf("%i = scanf %s\n", retval, str);
|
2013-12-16 13:05:51 +00:00
|
|
|
if (retval != 1) {
|
|
|
|
return -1;
|
|
|
|
} else {
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-21 14:57:15 +00:00
|
|
|
static float parseFloat(const char *str) {
|
|
|
|
float val;
|
|
|
|
int retval = sscanf(str, "%f", &val);
|
|
|
|
printf("%i = sscanf %s\n", retval, str);
|
|
|
|
if (retval != 1) {
|
|
|
|
return -1.0f;
|
|
|
|
} else {
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-13 22:22:21 +00:00
|
|
|
void ToggleFullScreenIfFlagSet(SDL_Window *window) {
|
2014-01-03 14:15:35 +00:00
|
|
|
if (g_ToggleFullScreenNextFrame) {
|
|
|
|
g_ToggleFullScreenNextFrame = false;
|
|
|
|
|
2017-12-13 22:22:21 +00:00
|
|
|
Uint32 window_flags = SDL_GetWindowFlags(window);
|
2017-07-30 14:51:53 +00:00
|
|
|
if (g_ToggleFullScreenType == -1) {
|
|
|
|
window_flags ^= SDL_WINDOW_FULLSCREEN_DESKTOP;
|
|
|
|
} else if (g_ToggleFullScreenType == 1) {
|
|
|
|
window_flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
|
|
|
|
} else {
|
|
|
|
window_flags &= ~SDL_WINDOW_FULLSCREEN_DESKTOP;
|
|
|
|
}
|
2017-12-13 22:22:21 +00:00
|
|
|
SDL_SetWindowFullscreen(window, window_flags);
|
2014-01-03 14:15:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-21 17:03:19 +00:00
|
|
|
enum class EmuThreadState {
|
|
|
|
DISABLED,
|
|
|
|
START_REQUESTED,
|
|
|
|
RUNNING,
|
|
|
|
QUIT_REQUESTED,
|
|
|
|
STOPPED,
|
|
|
|
};
|
|
|
|
|
|
|
|
static std::thread emuThread;
|
|
|
|
static std::atomic<int> emuThreadState((int)EmuThreadState::DISABLED);
|
|
|
|
|
2018-02-04 11:00:56 +00:00
|
|
|
static void EmuThreadFunc(GraphicsContext *graphicsContext) {
|
2018-01-21 17:47:07 +00:00
|
|
|
setCurrentThreadName("Emu");
|
|
|
|
|
2018-01-21 17:03:19 +00:00
|
|
|
// There's no real requirement that NativeInit happen on this thread.
|
|
|
|
// We just call the update/render loop here.
|
|
|
|
emuThreadState = (int)EmuThreadState::RUNNING;
|
2018-02-04 11:00:56 +00:00
|
|
|
|
|
|
|
NativeInitGraphics(graphicsContext);
|
|
|
|
|
2018-01-21 17:03:19 +00:00
|
|
|
while (emuThreadState != (int)EmuThreadState::QUIT_REQUESTED) {
|
|
|
|
UpdateRunLoop();
|
|
|
|
}
|
|
|
|
emuThreadState = (int)EmuThreadState::STOPPED;
|
2018-02-04 11:00:56 +00:00
|
|
|
|
|
|
|
NativeShutdownGraphics();
|
2018-01-21 17:03:19 +00:00
|
|
|
}
|
|
|
|
|
2018-02-04 11:00:56 +00:00
|
|
|
static void EmuThreadStart(GraphicsContext *context) {
|
2018-01-21 17:03:19 +00:00
|
|
|
emuThreadState = (int)EmuThreadState::START_REQUESTED;
|
2018-02-04 11:00:56 +00:00
|
|
|
emuThread = std::thread(&EmuThreadFunc, context);
|
2018-01-21 17:03:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void EmuThreadStop() {
|
|
|
|
emuThreadState = (int)EmuThreadState::QUIT_REQUESTED;
|
2018-02-07 11:22:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void EmuThreadJoin() {
|
2018-01-21 17:03:19 +00:00
|
|
|
emuThread.join();
|
|
|
|
emuThread = std::thread();
|
|
|
|
}
|
|
|
|
|
2012-04-10 09:59:57 +00:00
|
|
|
#ifdef _WIN32
|
|
|
|
#undef main
|
|
|
|
#endif
|
|
|
|
int main(int argc, char *argv[]) {
|
2017-12-13 21:58:45 +00:00
|
|
|
glslang::InitializeProcess();
|
|
|
|
|
2016-10-12 15:32:52 +00:00
|
|
|
#if PPSSPP_PLATFORM(RPI)
|
2014-02-09 22:31:31 +00:00
|
|
|
bcm_host_init();
|
|
|
|
#endif
|
2014-05-30 15:21:43 +00:00
|
|
|
putenv((char*)"SDL_VIDEO_CENTERED=1");
|
2017-02-22 21:44:32 +00:00
|
|
|
SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0");
|
2014-02-05 18:44:43 +00:00
|
|
|
|
2017-12-15 14:29:19 +00:00
|
|
|
if (VulkanMayBeAvailable()) {
|
|
|
|
printf("Vulkan might be available.\n");
|
|
|
|
} else {
|
|
|
|
printf("Vulkan is not available.\n");
|
|
|
|
}
|
|
|
|
|
2017-12-13 21:58:45 +00:00
|
|
|
int set_xres = -1;
|
|
|
|
int set_yres = -1;
|
|
|
|
bool portrait = false;
|
|
|
|
bool set_ipad = false;
|
|
|
|
float set_dpi = 1.0f;
|
|
|
|
float set_scale = 1.0f;
|
|
|
|
|
|
|
|
// Produce a new set of arguments with the ones we skip.
|
|
|
|
int remain_argc = 1;
|
|
|
|
const char *remain_argv[256] = { argv[0] };
|
|
|
|
|
|
|
|
Uint32 mode = 0;
|
|
|
|
for (int i = 1; i < argc; i++) {
|
|
|
|
if (!strcmp(argv[i],"--fullscreen"))
|
|
|
|
mode |= SDL_WINDOW_FULLSCREEN_DESKTOP;
|
|
|
|
else if (set_xres == -2)
|
|
|
|
set_xres = parseInt(argv[i]);
|
|
|
|
else if (set_yres == -2)
|
|
|
|
set_yres = parseInt(argv[i]);
|
|
|
|
else if (set_dpi == -2)
|
|
|
|
set_dpi = parseFloat(argv[i]);
|
|
|
|
else if (set_scale == -2)
|
|
|
|
set_scale = parseFloat(argv[i]);
|
|
|
|
else if (!strcmp(argv[i],"--xres"))
|
|
|
|
set_xres = -2;
|
|
|
|
else if (!strcmp(argv[i],"--yres"))
|
|
|
|
set_yres = -2;
|
|
|
|
else if (!strcmp(argv[i],"--dpi"))
|
|
|
|
set_dpi = -2;
|
|
|
|
else if (!strcmp(argv[i],"--scale"))
|
|
|
|
set_scale = -2;
|
|
|
|
else if (!strcmp(argv[i],"--ipad"))
|
|
|
|
set_ipad = true;
|
|
|
|
else if (!strcmp(argv[i],"--portrait"))
|
|
|
|
portrait = true;
|
|
|
|
else {
|
|
|
|
remain_argv[remain_argc++] = argv[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-04-10 09:59:57 +00:00
|
|
|
std::string app_name;
|
|
|
|
std::string app_name_nice;
|
2014-12-18 21:50:17 +00:00
|
|
|
std::string version;
|
2012-10-31 11:12:24 +00:00
|
|
|
bool landscape;
|
2014-12-18 21:50:17 +00:00
|
|
|
NativeGetAppInfo(&app_name, &app_name_nice, &landscape, &version);
|
2013-07-14 11:51:30 +00:00
|
|
|
|
2016-07-09 07:15:11 +00:00
|
|
|
bool joystick_enabled = true;
|
2016-09-18 22:23:36 +00:00
|
|
|
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER | SDL_INIT_AUDIO) < 0) {
|
2017-12-15 14:29:19 +00:00
|
|
|
fprintf(stderr, "Failed to initialize SDL with joystick support. Retrying without.\n");
|
2016-07-09 07:15:11 +00:00
|
|
|
joystick_enabled = false;
|
|
|
|
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) {
|
|
|
|
fprintf(stderr, "Unable to initialize SDL: %s\n", SDL_GetError());
|
|
|
|
return 1;
|
|
|
|
}
|
2014-09-01 13:46:14 +00:00
|
|
|
}
|
|
|
|
|
2017-12-15 14:29:19 +00:00
|
|
|
// TODO: How do we get this into the GraphicsContext?
|
2014-05-30 15:21:43 +00:00
|
|
|
#ifdef USING_EGL
|
2013-01-14 11:32:39 +00:00
|
|
|
if (EGL_Open())
|
|
|
|
return 1;
|
2013-01-12 15:38:37 +00:00
|
|
|
#endif
|
2012-04-10 09:59:57 +00:00
|
|
|
|
2014-05-02 21:46:24 +00:00
|
|
|
// Get the video info before doing anything else, so we don't get skewed resolution results.
|
2014-09-01 13:46:14 +00:00
|
|
|
// TODO: support multiple displays correctly
|
|
|
|
SDL_DisplayMode displayMode;
|
|
|
|
int should_be_zero = SDL_GetCurrentDisplayMode(0, &displayMode);
|
|
|
|
if (should_be_zero != 0) {
|
|
|
|
fprintf(stderr, "Could not get display mode: %s\n", SDL_GetError());
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
g_DesktopWidth = displayMode.w;
|
|
|
|
g_DesktopHeight = displayMode.h;
|
2014-05-02 21:46:24 +00:00
|
|
|
|
2012-04-10 09:59:57 +00:00
|
|
|
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_DEPTH_SIZE, 24);
|
|
|
|
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
|
|
|
|
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
2014-09-01 13:46:14 +00:00
|
|
|
SDL_GL_SetSwapInterval(1);
|
2012-04-10 09:59:57 +00:00
|
|
|
|
2014-11-13 15:38:50 +00:00
|
|
|
// Is resolution is too low to run windowed
|
2014-11-13 17:13:01 +00:00
|
|
|
if (g_DesktopWidth < 480 * 2 && g_DesktopHeight < 272 * 2) {
|
2014-11-13 15:38:50 +00:00
|
|
|
mode |= SDL_WINDOW_FULLSCREEN_DESKTOP;
|
|
|
|
}
|
|
|
|
|
2014-09-01 13:46:14 +00:00
|
|
|
if (mode & SDL_WINDOW_FULLSCREEN_DESKTOP) {
|
|
|
|
pixel_xres = g_DesktopWidth;
|
|
|
|
pixel_yres = g_DesktopHeight;
|
2013-11-05 02:58:30 +00:00
|
|
|
g_Config.bFullScreen = true;
|
2013-11-04 13:49:30 +00:00
|
|
|
} else {
|
2013-10-30 11:22:22 +00:00
|
|
|
// set a sensible default resolution (2x)
|
2014-01-23 13:31:56 +00:00
|
|
|
pixel_xres = 480 * 2 * set_scale;
|
|
|
|
pixel_yres = 272 * 2 * set_scale;
|
2014-04-13 15:08:36 +00:00
|
|
|
if (portrait) {
|
|
|
|
std::swap(pixel_xres, pixel_yres);
|
|
|
|
}
|
2013-11-05 02:58:30 +00:00
|
|
|
g_Config.bFullScreen = false;
|
2013-10-30 11:22:22 +00:00
|
|
|
}
|
2013-12-13 11:49:38 +00:00
|
|
|
|
2014-01-23 13:31:56 +00:00
|
|
|
set_dpi = 1.0f / set_dpi;
|
|
|
|
|
2014-04-08 13:27:14 +00:00
|
|
|
if (set_ipad) {
|
|
|
|
pixel_xres = 1024;
|
|
|
|
pixel_yres = 768;
|
|
|
|
}
|
2013-12-13 11:49:38 +00:00
|
|
|
if (!landscape) {
|
|
|
|
std::swap(pixel_xres, pixel_yres);
|
|
|
|
}
|
|
|
|
|
2013-12-16 13:05:51 +00:00
|
|
|
if (set_xres > 0) {
|
|
|
|
pixel_xres = set_xres;
|
|
|
|
}
|
|
|
|
if (set_yres > 0) {
|
|
|
|
pixel_yres = set_yres;
|
|
|
|
}
|
2014-01-21 14:57:15 +00:00
|
|
|
float dpi_scale = 1.0f;
|
|
|
|
if (set_dpi > 0) {
|
|
|
|
dpi_scale = set_dpi;
|
|
|
|
}
|
2013-12-16 13:05:51 +00:00
|
|
|
|
2014-01-21 14:57:15 +00:00
|
|
|
dp_xres = (float)pixel_xres * dpi_scale;
|
|
|
|
dp_yres = (float)pixel_yres * dpi_scale;
|
2013-10-30 11:22:22 +00:00
|
|
|
|
2017-10-01 20:59:21 +00:00
|
|
|
// Mac / Linux
|
|
|
|
char path[2048];
|
|
|
|
const char *the_path = getenv("HOME");
|
|
|
|
if (!the_path) {
|
|
|
|
struct passwd* pwd = getpwuid(getuid());
|
|
|
|
if (pwd)
|
|
|
|
the_path = pwd->pw_dir;
|
|
|
|
}
|
|
|
|
strcpy(path, the_path);
|
|
|
|
if (path[strlen(path)-1] != '/')
|
|
|
|
strcat(path, "/");
|
|
|
|
|
|
|
|
NativeInit(remain_argc, (const char **)remain_argv, path, "/tmp", nullptr);
|
|
|
|
|
|
|
|
// Use the setting from the config when initing the window.
|
|
|
|
if (g_Config.bFullScreen)
|
|
|
|
mode |= SDL_WINDOW_FULLSCREEN_DESKTOP;
|
|
|
|
|
2017-11-13 06:49:31 +00:00
|
|
|
int x = SDL_WINDOWPOS_UNDEFINED_DISPLAY(getDisplayNumber());
|
|
|
|
int y = SDL_WINDOWPOS_UNDEFINED;
|
|
|
|
|
2017-08-07 11:18:19 +00:00
|
|
|
pixel_in_dps_x = (float)pixel_xres / dp_xres;
|
|
|
|
pixel_in_dps_y = (float)pixel_yres / dp_yres;
|
|
|
|
g_dpi_scale_x = dp_xres / (float)pixel_xres;
|
|
|
|
g_dpi_scale_y = dp_yres / (float)pixel_yres;
|
|
|
|
g_dpi_scale_real_x = g_dpi_scale_x;
|
|
|
|
g_dpi_scale_real_y = g_dpi_scale_y;
|
2012-11-21 14:30:43 +00:00
|
|
|
|
2012-10-31 11:12:24 +00:00
|
|
|
printf("Pixels: %i x %i\n", pixel_xres, pixel_yres);
|
|
|
|
printf("Virtual pixels: %i x %i\n", dp_xres, dp_yres);
|
2014-02-10 14:12:55 +00:00
|
|
|
|
2017-12-15 14:29:19 +00:00
|
|
|
GraphicsContext *graphicsContext = nullptr;
|
|
|
|
SDL_Window *window = nullptr;
|
2018-02-04 11:00:56 +00:00
|
|
|
|
|
|
|
bool useEmuThread;
|
|
|
|
|
2017-12-15 14:29:19 +00:00
|
|
|
std::string error_message;
|
2017-12-26 23:55:24 +00:00
|
|
|
if (g_Config.iGPUBackend == (int)GPUBackend::OPENGL) {
|
2017-12-15 14:29:19 +00:00
|
|
|
SDLGLGraphicsContext *ctx = new SDLGLGraphicsContext();
|
2017-12-21 13:13:55 +00:00
|
|
|
if (ctx->Init(window, x, y, mode, &error_message) != 0) {
|
|
|
|
printf("GL init error '%s'\n", error_message.c_str());
|
2017-12-15 14:29:19 +00:00
|
|
|
}
|
|
|
|
graphicsContext = ctx;
|
2018-02-04 11:00:56 +00:00
|
|
|
useEmuThread = true;
|
2017-12-26 23:55:24 +00:00
|
|
|
} else if (g_Config.iGPUBackend == (int)GPUBackend::VULKAN) {
|
2017-12-13 21:58:45 +00:00
|
|
|
SDLVulkanGraphicsContext *ctx = new SDLVulkanGraphicsContext();
|
2017-12-15 14:29:19 +00:00
|
|
|
if (!ctx->Init(window, x, y, mode, &error_message)) {
|
2017-12-13 21:58:45 +00:00
|
|
|
printf("Vulkan init error '%s' - falling back to GL\n", error_message.c_str());
|
2017-12-26 23:55:24 +00:00
|
|
|
g_Config.iGPUBackend = (int)GPUBackend::OPENGL;
|
|
|
|
SetGPUBackend((GPUBackend)g_Config.iGPUBackend);
|
2017-12-13 21:58:45 +00:00
|
|
|
delete ctx;
|
2017-12-15 14:29:19 +00:00
|
|
|
SDLGLGraphicsContext *glctx = new SDLGLGraphicsContext();
|
|
|
|
glctx->Init(window, x, y, mode, &error_message);
|
|
|
|
graphicsContext = glctx;
|
2017-12-13 21:58:45 +00:00
|
|
|
} else {
|
|
|
|
graphicsContext = ctx;
|
|
|
|
}
|
2018-02-04 11:00:56 +00:00
|
|
|
useEmuThread = false;
|
2017-12-13 21:58:45 +00:00
|
|
|
}
|
2017-12-15 14:29:19 +00:00
|
|
|
|
2018-01-21 17:03:19 +00:00
|
|
|
// Since we render from the main thread, there's nothing done here, but we call it to avoid confusion.
|
|
|
|
if (!graphicsContext->InitFromRenderThread(&error_message)) {
|
|
|
|
printf("Init from thread error: '%s'\n", error_message.c_str());
|
|
|
|
}
|
|
|
|
|
2017-12-15 14:29:19 +00:00
|
|
|
SDL_SetWindowTitle(window, (app_name_nice + " " + PPSSPP_GIT_VERSION).c_str());
|
|
|
|
|
|
|
|
#ifdef MOBILE_DEVICE
|
|
|
|
SDL_ShowCursor(SDL_DISABLE);
|
|
|
|
#endif
|
|
|
|
|
2018-02-04 11:00:56 +00:00
|
|
|
if (!useEmuThread) {
|
|
|
|
NativeInitGraphics(graphicsContext);
|
|
|
|
NativeResized();
|
|
|
|
}
|
2012-07-15 15:04:27 +00:00
|
|
|
|
2014-05-04 10:33:28 +00:00
|
|
|
SDL_AudioSpec fmt, ret_fmt;
|
|
|
|
memset(&fmt, 0, sizeof(fmt));
|
2012-07-06 20:32:32 +00:00
|
|
|
fmt.freq = 44100;
|
|
|
|
fmt.format = AUDIO_S16;
|
|
|
|
fmt.channels = 2;
|
2013-10-22 03:29:34 +00:00
|
|
|
fmt.samples = 2048;
|
2012-07-06 20:32:32 +00:00
|
|
|
fmt.callback = &mixaudio;
|
|
|
|
fmt.userdata = (void *)0;
|
|
|
|
|
2014-05-04 10:33:28 +00:00
|
|
|
if (SDL_OpenAudio(&fmt, &ret_fmt) < 0) {
|
2012-07-06 20:32:32 +00:00
|
|
|
ELOG("Failed to open audio: %s", SDL_GetError());
|
2014-05-04 10:33:28 +00:00
|
|
|
} else {
|
2014-06-25 05:40:28 +00:00
|
|
|
if (ret_fmt.samples != fmt.samples) // Notify, but still use it
|
|
|
|
ELOG("Output audio samples: %d (requested: %d)", ret_fmt.samples, fmt.samples);
|
|
|
|
if (ret_fmt.freq != fmt.freq || ret_fmt.format != fmt.format || ret_fmt.channels != fmt.channels) {
|
2014-05-04 10:33:28 +00:00
|
|
|
ELOG("Sound buffer format does not match requested format.");
|
2014-06-25 05:40:28 +00:00
|
|
|
ELOG("Output audio freq: %d (requested: %d)", ret_fmt.freq, fmt.freq);
|
|
|
|
ELOG("Output audio format: %d (requested: %d)", ret_fmt.format, fmt.format);
|
|
|
|
ELOG("Output audio channels: %d (requested: %d)", ret_fmt.channels, fmt.channels);
|
2014-05-04 10:33:28 +00:00
|
|
|
ELOG("Provided output format does not match requirement, turning audio off");
|
|
|
|
SDL_CloseAudio();
|
|
|
|
}
|
2013-12-16 13:05:51 +00:00
|
|
|
}
|
2012-07-06 20:32:32 +00:00
|
|
|
|
|
|
|
// Audio must be unpaused _after_ NativeInit()
|
2013-11-24 05:13:24 +00:00
|
|
|
SDL_PauseAudio(0);
|
2016-07-09 07:15:11 +00:00
|
|
|
if (joystick_enabled) {
|
|
|
|
joystick = new SDLJoystick();
|
|
|
|
} else {
|
|
|
|
joystick = nullptr;
|
|
|
|
}
|
2013-11-22 00:59:18 +00:00
|
|
|
EnableFZ();
|
2013-07-14 11:51:30 +00:00
|
|
|
|
2012-07-06 20:32:32 +00:00
|
|
|
int framecount = 0;
|
2017-03-15 04:09:43 +00:00
|
|
|
bool mouseDown = false;
|
2012-10-31 11:12:24 +00:00
|
|
|
|
2018-02-04 11:00:56 +00:00
|
|
|
if (useEmuThread) {
|
|
|
|
EmuThreadStart(graphicsContext);
|
2018-01-21 19:02:22 +00:00
|
|
|
}
|
2018-01-21 17:03:19 +00:00
|
|
|
graphicsContext->ThreadStart();
|
|
|
|
|
2017-03-15 04:09:43 +00:00
|
|
|
while (true) {
|
2012-10-31 11:12:24 +00:00
|
|
|
SDL_Event event;
|
2012-04-10 09:59:57 +00:00
|
|
|
while (SDL_PollEvent(&event)) {
|
2017-08-07 15:09:16 +00:00
|
|
|
float mx = event.motion.x * g_dpi_scale_x;
|
|
|
|
float my = event.motion.y * g_dpi_scale_y;
|
2012-07-15 15:04:27 +00:00
|
|
|
|
2013-07-14 11:51:30 +00:00
|
|
|
switch (event.type) {
|
|
|
|
case SDL_QUIT:
|
2014-02-07 10:33:31 +00:00
|
|
|
g_QuitRequested = 1;
|
2013-07-14 11:51:30 +00:00
|
|
|
break;
|
2015-12-27 20:03:26 +00:00
|
|
|
|
2014-04-26 04:08:20 +00:00
|
|
|
#if !defined(MOBILE_DEVICE)
|
2014-09-01 13:46:14 +00:00
|
|
|
case SDL_WINDOWEVENT:
|
2015-12-27 20:03:26 +00:00
|
|
|
switch (event.window.event) {
|
2014-09-01 13:46:14 +00:00
|
|
|
case SDL_WINDOWEVENT_RESIZED:
|
2015-12-27 20:03:26 +00:00
|
|
|
{
|
2017-12-13 22:22:21 +00:00
|
|
|
Uint32 window_flags = SDL_GetWindowFlags(window);
|
2015-12-27 20:03:26 +00:00
|
|
|
bool fullscreen = (window_flags & SDL_WINDOW_FULLSCREEN);
|
2014-09-04 08:20:04 +00:00
|
|
|
|
2015-12-27 20:03:26 +00:00
|
|
|
pixel_xres = event.window.data1;
|
|
|
|
pixel_yres = event.window.data2;
|
|
|
|
dp_xres = (float)pixel_xres * dpi_scale;
|
|
|
|
dp_yres = (float)pixel_yres * dpi_scale;
|
|
|
|
NativeResized();
|
2014-09-04 08:20:04 +00:00
|
|
|
|
2015-12-27 20:03:26 +00:00
|
|
|
// Set variable here in case fullscreen was toggled by hotkey
|
|
|
|
g_Config.bFullScreen = fullscreen;
|
|
|
|
|
|
|
|
// Hide/Show cursor correctly toggling fullscreen
|
|
|
|
if (lastUIState == UISTATE_INGAME && fullscreen && !g_Config.bShowTouchControls) {
|
|
|
|
SDL_ShowCursor(SDL_DISABLE);
|
|
|
|
} else if (lastUIState != UISTATE_INGAME || !fullscreen) {
|
|
|
|
SDL_ShowCursor(SDL_ENABLE);
|
2014-09-01 13:46:14 +00:00
|
|
|
}
|
|
|
|
break;
|
2014-04-26 04:08:20 +00:00
|
|
|
}
|
2015-12-27 20:03:26 +00:00
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
2014-04-26 04:08:20 +00:00
|
|
|
#endif
|
2013-07-14 11:51:30 +00:00
|
|
|
case SDL_KEYDOWN:
|
|
|
|
{
|
2017-03-23 02:54:41 +00:00
|
|
|
if (event.key.repeat > 0) { break;}
|
2013-07-14 11:51:30 +00:00
|
|
|
int k = event.key.keysym.sym;
|
|
|
|
KeyInput key;
|
|
|
|
key.flags = KEY_DOWN;
|
2015-12-27 20:04:18 +00:00
|
|
|
auto mapped = KeyMapRawSDLtoNative.find(k);
|
|
|
|
if (mapped == KeyMapRawSDLtoNative.end() || mapped->second == NKCODE_UNKNOWN) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
key.keyCode = mapped->second;
|
2013-07-14 11:51:30 +00:00
|
|
|
key.deviceId = DEVICE_ID_KEYBOARD;
|
|
|
|
NativeKey(key);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SDL_KEYUP:
|
|
|
|
{
|
2017-03-23 02:54:41 +00:00
|
|
|
if (event.key.repeat > 0) { break;}
|
2013-07-14 11:51:30 +00:00
|
|
|
int k = event.key.keysym.sym;
|
|
|
|
KeyInput key;
|
|
|
|
key.flags = KEY_UP;
|
2015-12-27 20:04:18 +00:00
|
|
|
auto mapped = KeyMapRawSDLtoNative.find(k);
|
|
|
|
if (mapped == KeyMapRawSDLtoNative.end() || mapped->second == NKCODE_UNKNOWN) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
key.keyCode = mapped->second;
|
2013-07-14 11:51:30 +00:00
|
|
|
key.deviceId = DEVICE_ID_KEYBOARD;
|
|
|
|
NativeKey(key);
|
|
|
|
break;
|
|
|
|
}
|
2014-11-21 18:20:19 +00:00
|
|
|
case SDL_TEXTINPUT:
|
|
|
|
{
|
|
|
|
int pos = 0;
|
|
|
|
int c = u8_nextchar(event.text.text, &pos);
|
|
|
|
KeyInput key;
|
|
|
|
key.flags = KEY_CHAR;
|
|
|
|
key.keyCode = c;
|
|
|
|
key.deviceId = DEVICE_ID_KEYBOARD;
|
|
|
|
NativeKey(key);
|
|
|
|
break;
|
|
|
|
}
|
2013-07-14 11:51:30 +00:00
|
|
|
case SDL_MOUSEBUTTONDOWN:
|
2013-07-08 09:03:14 +00:00
|
|
|
switch (event.button.button) {
|
|
|
|
case SDL_BUTTON_LEFT:
|
|
|
|
{
|
2017-03-15 04:09:43 +00:00
|
|
|
mouseDown = true;
|
2013-07-08 09:03:14 +00:00
|
|
|
TouchInput input;
|
|
|
|
input.x = mx;
|
|
|
|
input.y = my;
|
2014-07-20 20:20:09 +00:00
|
|
|
input.flags = TOUCH_DOWN | TOUCH_MOUSE;
|
2013-07-08 09:03:14 +00:00
|
|
|
input.id = 0;
|
|
|
|
NativeTouch(input);
|
2013-08-04 17:30:34 +00:00
|
|
|
KeyInput key(DEVICE_ID_MOUSE, NKCODE_EXT_MOUSEBUTTON_1, KEY_DOWN);
|
2013-07-09 14:33:05 +00:00
|
|
|
NativeKey(key);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SDL_BUTTON_RIGHT:
|
|
|
|
{
|
2013-08-04 17:30:34 +00:00
|
|
|
KeyInput key(DEVICE_ID_MOUSE, NKCODE_EXT_MOUSEBUTTON_2, KEY_DOWN);
|
2013-07-09 14:33:05 +00:00
|
|
|
NativeKey(key);
|
2013-07-08 09:03:14 +00:00
|
|
|
}
|
|
|
|
break;
|
2014-09-01 13:46:14 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SDL_MOUSEWHEEL:
|
|
|
|
{
|
|
|
|
KeyInput key;
|
|
|
|
key.deviceId = DEVICE_ID_MOUSE;
|
|
|
|
if (event.wheel.y > 0) {
|
2013-08-04 17:30:34 +00:00
|
|
|
key.keyCode = NKCODE_EXT_MOUSEWHEEL_UP;
|
2014-09-01 13:46:14 +00:00
|
|
|
} else {
|
2013-08-04 17:30:34 +00:00
|
|
|
key.keyCode = NKCODE_EXT_MOUSEWHEEL_DOWN;
|
2013-07-08 09:03:14 +00:00
|
|
|
}
|
2014-09-01 13:46:14 +00:00
|
|
|
key.flags = KEY_DOWN;
|
|
|
|
NativeKey(key);
|
|
|
|
|
|
|
|
// SDL2 doesn't consider the mousewheel a button anymore
|
2014-09-04 08:20:04 +00:00
|
|
|
// so let's send the KEY_UP right away.
|
|
|
|
// Maybe KEY_UP alone will suffice?
|
2014-09-01 13:46:14 +00:00
|
|
|
key.flags = KEY_UP;
|
|
|
|
NativeKey(key);
|
2013-05-25 13:12:46 +00:00
|
|
|
}
|
2013-07-14 11:51:30 +00:00
|
|
|
case SDL_MOUSEMOTION:
|
2017-03-15 04:09:43 +00:00
|
|
|
if (mouseDown) {
|
2013-05-02 22:21:39 +00:00
|
|
|
TouchInput input;
|
|
|
|
input.x = mx;
|
|
|
|
input.y = my;
|
2014-07-20 20:20:09 +00:00
|
|
|
input.flags = TOUCH_MOVE | TOUCH_MOUSE;
|
2013-05-02 22:21:39 +00:00
|
|
|
input.id = 0;
|
|
|
|
NativeTouch(input);
|
2012-04-10 09:59:57 +00:00
|
|
|
}
|
2013-07-14 11:51:30 +00:00
|
|
|
break;
|
|
|
|
case SDL_MOUSEBUTTONUP:
|
2013-07-08 09:03:14 +00:00
|
|
|
switch (event.button.button) {
|
|
|
|
case SDL_BUTTON_LEFT:
|
|
|
|
{
|
2017-03-15 04:09:43 +00:00
|
|
|
mouseDown = false;
|
2013-07-08 09:03:14 +00:00
|
|
|
TouchInput input;
|
|
|
|
input.x = mx;
|
|
|
|
input.y = my;
|
2014-07-20 20:20:09 +00:00
|
|
|
input.flags = TOUCH_UP | TOUCH_MOUSE;
|
2013-07-08 09:03:14 +00:00
|
|
|
input.id = 0;
|
|
|
|
NativeTouch(input);
|
2013-08-04 17:30:34 +00:00
|
|
|
KeyInput key(DEVICE_ID_MOUSE, NKCODE_EXT_MOUSEBUTTON_1, KEY_UP);
|
2013-07-09 14:33:05 +00:00
|
|
|
NativeKey(key);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SDL_BUTTON_RIGHT:
|
|
|
|
{
|
2013-08-04 17:30:34 +00:00
|
|
|
KeyInput key(DEVICE_ID_MOUSE, NKCODE_EXT_MOUSEBUTTON_2, KEY_UP);
|
2013-07-09 14:33:05 +00:00
|
|
|
NativeKey(key);
|
2013-07-08 09:03:14 +00:00
|
|
|
}
|
|
|
|
break;
|
2012-07-06 20:32:32 +00:00
|
|
|
}
|
2013-07-14 11:51:30 +00:00
|
|
|
break;
|
2013-11-22 00:59:18 +00:00
|
|
|
default:
|
2016-07-09 07:15:11 +00:00
|
|
|
if (joystick) {
|
|
|
|
joystick->ProcessInput(event);
|
|
|
|
}
|
2013-11-22 12:19:32 +00:00
|
|
|
break;
|
2012-04-10 09:59:57 +00:00
|
|
|
}
|
|
|
|
}
|
2014-02-07 10:33:31 +00:00
|
|
|
if (g_QuitRequested)
|
2012-07-06 20:32:32 +00:00
|
|
|
break;
|
2015-09-06 18:14:00 +00:00
|
|
|
const uint8_t *keys = SDL_GetKeyboardState(NULL);
|
2018-01-21 17:03:19 +00:00
|
|
|
if (emuThreadState == (int)EmuThreadState::DISABLED) {
|
|
|
|
UpdateRunLoop();
|
|
|
|
}
|
2014-02-12 11:45:26 +00:00
|
|
|
if (g_QuitRequested)
|
|
|
|
break;
|
2017-09-28 18:27:31 +00:00
|
|
|
#if !defined(MOBILE_DEVICE)
|
2014-06-22 08:34:22 +00:00
|
|
|
if (lastUIState != GetUIState()) {
|
|
|
|
lastUIState = GetUIState();
|
2013-11-05 02:58:30 +00:00
|
|
|
if (lastUIState == UISTATE_INGAME && g_Config.bFullScreen && !g_Config.bShowTouchControls)
|
|
|
|
SDL_ShowCursor(SDL_DISABLE);
|
|
|
|
if (lastUIState != UISTATE_INGAME && g_Config.bFullScreen)
|
|
|
|
SDL_ShowCursor(SDL_ENABLE);
|
|
|
|
}
|
|
|
|
#endif
|
2012-10-26 16:42:17 +00:00
|
|
|
|
2012-07-06 20:32:32 +00:00
|
|
|
if (framecount % 60 == 0) {
|
2012-10-31 11:12:24 +00:00
|
|
|
// glsl_refresh(); // auto-reloads modified GLSL shaders once per second.
|
2012-07-06 20:32:32 +00:00
|
|
|
}
|
2012-10-31 11:12:24 +00:00
|
|
|
|
2018-01-21 17:03:19 +00:00
|
|
|
if (emuThreadState != (int)EmuThreadState::DISABLED) {
|
|
|
|
if (!graphicsContext->ThreadFrame())
|
|
|
|
break;
|
|
|
|
}
|
2017-12-13 22:22:21 +00:00
|
|
|
graphicsContext->SwapBuffers();
|
2014-01-03 14:15:35 +00:00
|
|
|
|
2017-12-13 22:22:21 +00:00
|
|
|
ToggleFullScreenIfFlagSet(window);
|
2012-07-06 20:32:32 +00:00
|
|
|
time_update();
|
|
|
|
framecount++;
|
2012-04-10 09:59:57 +00:00
|
|
|
}
|
2018-01-21 17:03:19 +00:00
|
|
|
|
2018-02-07 11:22:19 +00:00
|
|
|
if (useEmuThread) {
|
|
|
|
EmuThreadStop();
|
|
|
|
while (emuThreadState != (int)EmuThreadState::STOPPED) {
|
|
|
|
// Need to keep eating frames to allow the EmuThread to exit correctly.
|
|
|
|
graphicsContext->ThreadFrame();
|
|
|
|
}
|
|
|
|
EmuThreadJoin();
|
|
|
|
}
|
2018-01-21 17:03:19 +00:00
|
|
|
|
2014-06-07 14:55:51 +00:00
|
|
|
delete joystick;
|
2018-02-04 11:26:35 +00:00
|
|
|
|
|
|
|
if (!useEmuThread) {
|
|
|
|
NativeShutdownGraphics();
|
|
|
|
}
|
2016-01-01 11:14:09 +00:00
|
|
|
graphicsContext->Shutdown();
|
2018-01-31 17:35:48 +00:00
|
|
|
graphicsContext->ThreadEnd();
|
|
|
|
graphicsContext->ShutdownFromRenderThread();
|
2018-02-07 11:22:19 +00:00
|
|
|
|
2015-12-26 03:39:52 +00:00
|
|
|
NativeShutdown();
|
2017-03-11 11:54:13 +00:00
|
|
|
delete graphicsContext;
|
2017-12-13 21:58:45 +00:00
|
|
|
|
2012-07-06 20:32:32 +00:00
|
|
|
SDL_PauseAudio(1);
|
|
|
|
SDL_CloseAudio();
|
2012-04-10 09:59:57 +00:00
|
|
|
SDL_Quit();
|
2016-10-12 15:32:52 +00:00
|
|
|
#if PPSSPP_PLATFORM(RPI)
|
2014-02-09 22:31:31 +00:00
|
|
|
bcm_host_deinit();
|
|
|
|
#endif
|
|
|
|
|
2017-12-13 21:58:45 +00:00
|
|
|
glslang::FinalizeProcess();
|
2018-02-04 12:38:58 +00:00
|
|
|
ILOG("Leaving main");
|
2012-04-10 09:59:57 +00:00
|
|
|
return 0;
|
|
|
|
}
|