ppsspp/ext/native/base/PCMain.cpp

920 lines
24 KiB
C++
Raw Normal View History

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.
// 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.
#ifdef _WIN32
#pragma warning(disable:4091) // workaround bug in VS2015 headers
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <shlobj.h>
#include <shlwapi.h>
#include <ShellAPI.h>
2012-11-27 15:53:22 +00:00
#else
#include <unistd.h>
#include <pwd.h>
#endif
2012-11-25 21:25:54 +00:00
#include "SDL.h"
#ifndef _WIN32
#include "SDL/SDLJoystick.h"
2014-06-07 14:55:51 +00:00
SDLJoystick *joystick = NULL;
2012-11-27 15:53:22 +00:00
#endif
#ifdef RPI
#include <bcm_host.h>
#endif
#include <algorithm>
#include "base/display.h"
#include "base/logging.h"
#include "base/timeutil.h"
2015-09-06 12:38:15 +00:00
#include "gfx/gl_common.h"
#include "gfx_es2/gpu_features.h"
#include "input/input_state.h"
#include "input/keycodes.h"
#include "net/resolve.h"
#include "base/NKCodeFromSDL.h"
#include "util/const_map.h"
#include "util/text/utf8.h"
#include "math/math_util.h"
2014-06-22 09:17:03 +00:00
#include "Core/System.h"
#include "Core/Core.h"
#include "Core/Config.h"
#include "Common/GraphicsContext.h"
2016-01-01 13:08:23 +00:00
class GLDummyGraphicsContext : public DummyGraphicsContext {
public:
Thin3DContext *CreateThin3DContext() override {
2016-01-07 04:10:42 +00:00
CheckGLExtensions();
2016-01-01 13:08:23 +00:00
return T3DCreateGLContext();
}
};
2014-06-22 08:53:06 +00:00
GlobalUIState lastUIState = UISTATE_MENU;
GlobalUIState GetUIState();
2014-09-01 13:46:14 +00:00
static SDL_Window* g_Screen = NULL;
static bool g_ToggleFullScreenNextFrame = false;
static int g_QuitRequested = 0;
static int g_DesktopWidth = 0;
static int g_DesktopHeight = 0;
#if defined(USING_EGL)
2013-01-12 15:38:37 +00:00
#include "EGL/egl.h"
#if !defined(USING_FBDEV)
2013-01-12 15:38:37 +00:00
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#endif
2013-01-12 15:38:37 +00:00
#include "SDL_syswm.h"
2013-05-06 13:56:41 +00:00
#include "math.h"
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() {
#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() {
EGLConfig g_eglConfig;
2013-01-12 15:38:37 +00:00
EGLint g_numConfigs = 0;
EGLint attrib_list[]= {
// 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,
#endif
2013-01-12 15:38:37 +00:00
EGL_DEPTH_SIZE, 16,
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
#ifdef USING_GLES2
2013-01-12 15:38:37 +00:00
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
#else
EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
#endif
2013-01-12 15:38:37 +00:00
EGL_SAMPLE_BUFFERS, 0,
EGL_SAMPLES, 0,
#ifdef MAEMO
EGL_BUFFER_SIZE, 16,
#endif
2013-01-12 15:38:37 +00:00
EGL_NONE};
const EGLint attributes[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
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
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);
#if !defined(USING_FBDEV)
//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
#endif
#ifdef USING_FBDEV
2013-05-06 13:56:41 +00:00
g_Window = (NativeWindowType)NULL;
#else
2013-01-12 15:38:37 +00:00
g_Window = (NativeWindowType)sysInfo.info.x11.window;
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);
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() {
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) {
#if !defined(USING_FBDEV)
2013-01-12 15:38:37 +00:00
XCloseDisplay(g_Display);
#endif
2013-01-12 15:38:37 +00:00
g_Display = NULL;
}
g_eglSurface = NULL;
g_eglContext = NULL;
}
#endif
2013-01-12 15:38:37 +00:00
int getDisplayNumber(void)
{
int displayNumber = 0;
char * displayNumberStr;
//get environment
displayNumberStr=getenv("SDL_VIDEO_FULLSCREEN_HEAD");
if (displayNumberStr)
{
displayNumber = atoi(displayNumberStr);
}
return displayNumber;
}
2013-01-12 15:38:37 +00:00
// Simple implementations of System functions
void SystemToast(const char *text) {
2012-07-05 21:30:35 +00:00
#ifdef _WIN32
MessageBox(0, text, "Toast!", MB_ICONINFORMATION);
2012-07-05 21:30:35 +00:00
#else
puts(text);
2012-07-05 21:30:35 +00:00
#endif
}
2012-04-16 21:30:13 +00:00
void ShowKeyboard() {
// Irrelevant on PC
}
void Vibrate(int length_ms) {
// Ignore on PC
}
void System_SendMessage(const char *command, const char *parameter) {
if (!strcmp(command, "toggle_fullscreen")) {
g_ToggleFullScreenNextFrame = true;
} else if (!strcmp(command, "finish")) {
// Do a clean exit
g_QuitRequested = true;
}
}
void System_AskForPermission(SystemPermission permission) {}
PermissionStatus System_GetPermissionStatus(SystemPermission permission) { return PERMISSION_STATUS_GRANTED; }
void LaunchBrowser(const char *url) {
#if defined(MOBILE_DEVICE)
ILOG("Would have gone to %s but LaunchBrowser is not implemented on this platform", url);
#elif defined(_WIN32)
ShellExecute(NULL, "open", url, NULL, NULL, SW_SHOWNORMAL);
#elif defined(__APPLE__)
std::string command = std::string("open ") + url;
system(command.c_str());
#else
std::string command = std::string("xdg-open ") + url;
int err = system(command.c_str());
if (err) {
ILOG("Would have gone to %s but xdg-utils seems not to be installed", url)
}
#endif
}
void LaunchMarket(const char *url) {
#if defined(MOBILE_DEVICE)
ILOG("Would have gone to %s but LaunchMarket is not implemented on this platform", url);
#elif defined(_WIN32)
ShellExecute(NULL, "open", url, NULL, NULL, SW_SHOWNORMAL);
#elif defined(__APPLE__)
std::string command = std::string("open ") + url;
system(command.c_str());
#else
std::string command = std::string("xdg-open ") + url;
int err = system(command.c_str());
if (err) {
ILOG("Would have gone to %s but xdg-utils seems not to be installed", url)
}
#endif
}
void LaunchEmail(const char *email_address) {
#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)
ShellExecute(NULL, "open", (std::string("mailto:") + email_address).c_str(), NULL, NULL, SW_SHOWNORMAL);
#elif defined(__APPLE__)
std::string command = std::string("open mailto:") + email_address;
system(command.c_str());
#else
std::string command = std::string("xdg-email ") + email_address;
int err = system(command.c_str());
if (err) {
ILOG("Would have gone to %s but xdg-utils seems not to be installed", email_address)
}
#endif
}
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
return "SDL:";
2013-11-26 06:38:00 +00:00
#endif
case SYSPROP_LANGREGION:
return "en_US";
default:
return "";
}
}
int System_GetPropertyInt(SystemProperty prop) {
switch (prop) {
case SYSPROP_AUDIO_SAMPLE_RATE:
return 44100;
case SYSPROP_DISPLAY_REFRESH_RATE:
return 60000;
case SYSPROP_DEVICE_TYPE:
#if defined(MOBILE_DEVICE)
return DEVICE_TYPE_MOBILE;
#else
return DEVICE_TYPE_DESKTOP;
#endif
default:
return -1;
}
}
InputState input_state;
static const int legacyKeyMap[] {
NKCODE_X, //A
NKCODE_S, //B
NKCODE_Z, //X
NKCODE_A, //Y
NKCODE_W, //LBUMPER
NKCODE_Q, //RBUMPER
NKCODE_1, //START
NKCODE_2, //SELECT
NKCODE_DPAD_UP, //UP
NKCODE_DPAD_DOWN, //DOWN
NKCODE_DPAD_LEFT, //LEFT
NKCODE_DPAD_RIGHT, //RIGHT
0, //MENU (SwipeDown)
NKCODE_ESCAPE, //BACK
NKCODE_I, //JOY UP
NKCODE_K, //JOY DOWN
NKCODE_J, //JOY LEFT
NKCODE_L, //JOY RIGHT
};
2015-09-06 18:14:00 +00:00
void SimulateGamepad(const uint8_t *keys, InputState *input) {
// Legacy key mapping.
input->pad_buttons = 0;
input->pad_lstick_x = 0;
input->pad_lstick_y = 0;
input->pad_rstick_x = 0;
input->pad_rstick_y = 0;
}
extern void mixaudio(void *userdata, Uint8 *stream, int len) {
2012-10-31 13:01:32 +00:00
NativeMix((short *)stream, len / 4);
}
// returns -1 on failure
static int parseInt(const char *str) {
int val;
int retval = sscanf(str, "%d", &val);
printf("%i = scanf %s\n", retval, str);
if (retval != 1) {
return -1;
} else {
return val;
}
}
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;
}
}
void ToggleFullScreenIfFlagSet() {
if (g_ToggleFullScreenNextFrame) {
g_ToggleFullScreenNextFrame = false;
2014-09-01 13:46:14 +00:00
Uint32 window_flags = SDL_GetWindowFlags(g_Screen);
SDL_SetWindowFullscreen(g_Screen, window_flags ^ SDL_WINDOW_FULLSCREEN_DESKTOP);
}
}
#ifdef _WIN32
#undef main
#endif
int main(int argc, char *argv[]) {
#ifdef RPI
bcm_host_init();
#endif
putenv((char*)"SDL_VIDEO_CENTERED=1");
2014-02-05 18:44:43 +00:00
std::string app_name;
std::string app_name_nice;
std::string version;
bool landscape;
NativeGetAppInfo(&app_name, &app_name_nice, &landscape, &version);
2013-07-14 11:51:30 +00:00
net::Init();
2014-09-01 13:46:14 +00:00
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_AUDIO) < 0) {
fprintf(stderr, "Unable to initialize SDL: %s\n", SDL_GetError());
return 1;
}
#ifdef __APPLE__
// Make sure to request a somewhat modern GL context at least - the
// latest supported by MacOSX (really, really sad...)
// Requires SDL 2.0
// We really should upgrade to SDL 2.0 soon.
2014-09-01 13:46:14 +00:00
//SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
//SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
2014-09-01 13:46:14 +00:00
//SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
#endif
#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
// 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;
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);
2014-09-01 13:46:14 +00:00
Uint32 mode;
2013-01-12 15:38:37 +00:00
#ifdef USING_GLES2
mode = SDL_WINDOW_OPENGL | SDL_WINDOW_FULLSCREEN;
2013-01-12 15:38:37 +00:00
#else
2014-09-01 13:46:14 +00:00
mode = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE;
2014-05-29 02:12:50 +00:00
#endif
int set_xres = -1;
int set_yres = -1;
2014-04-13 15:08:36 +00:00
bool portrait = false;
2014-05-29 02:12:50 +00:00
bool set_ipad = false;
float set_dpi = 1.0f;
float set_scale = 1.0f;
for (int i = 1; i < argc; i++) {
if (!strcmp(argv[i],"--fullscreen"))
2014-09-01 13:46:14 +00:00
mode |= SDL_WINDOW_FULLSCREEN_DESKTOP;
if (set_xres == -2) {
set_xres = parseInt(argv[i]);
} else if (set_yres == -2) {
set_yres = parseInt(argv[i]);
}
if (set_dpi == -2)
set_dpi = parseFloat(argv[i]);
if (set_scale == -2)
set_scale = parseFloat(argv[i]);
if (!strcmp(argv[i],"--xres"))
set_xres = -2;
if (!strcmp(argv[i],"--yres"))
set_yres = -2;
if (!strcmp(argv[i],"--dpi"))
set_dpi = -2;
if (!strcmp(argv[i],"--scale"))
set_scale = -2;
if (!strcmp(argv[i],"--ipad"))
2014-05-29 02:12:50 +00:00
set_ipad = true;
2014-04-13 15:08:36 +00:00
if (!strcmp(argv[i],"--portrait"))
portrait = true;
}
2014-05-29 02:12: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) {
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;
#ifdef PPSSPP
g_Config.bFullScreen = true;
#endif
} else {
// set a sensible default resolution (2x)
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);
}
#ifdef PPSSPP
g_Config.bFullScreen = false;
#endif
}
set_dpi = 1.0f / set_dpi;
if (set_ipad) {
pixel_xres = 1024;
pixel_yres = 768;
}
if (!landscape) {
std::swap(pixel_xres, pixel_yres);
}
if (set_xres > 0) {
pixel_xres = set_xres;
}
if (set_yres > 0) {
pixel_yres = set_yres;
}
float dpi_scale = 1.0f;
if (set_dpi > 0) {
dpi_scale = set_dpi;
}
dp_xres = (float)pixel_xres * dpi_scale;
dp_yres = (float)pixel_yres * dpi_scale;
g_Screen = SDL_CreateWindow(app_name_nice.c_str(), SDL_WINDOWPOS_UNDEFINED_DISPLAY(getDisplayNumber()),\
SDL_WINDOWPOS_UNDEFINED, pixel_xres, pixel_yres, mode);
if (g_Screen == NULL) {
fprintf(stderr, "SDL_CreateWindow failed: %s\n", SDL_GetError());
SDL_Quit();
return 2;
}
2014-09-01 13:46:14 +00:00
SDL_GLContext glContext = SDL_GL_CreateContext(g_Screen);
if (glContext == NULL) {
fprintf(stderr, "SDL_GL_CreateContext failed: %s\n", SDL_GetError());
SDL_Quit();
return 2;
}
#ifdef USING_EGL
2013-01-12 15:38:37 +00:00
EGL_Init();
#endif
#ifdef PPSSPP
2014-09-01 13:46:14 +00:00
SDL_SetWindowTitle(g_Screen, (app_name_nice + " " + PPSSPP_GIT_VERSION).c_str());
#endif
#ifdef MOBILE_DEVICE
SDL_ShowCursor(SDL_DISABLE);
#endif
2013-01-12 15:38:37 +00:00
#ifndef USING_GLES2
if (GLEW_OK != glewInit()) {
printf("Failed to initialize glew!\n");
return 1;
}
if (GLEW_VERSION_2_0) {
printf("OpenGL 2.0 or higher.\n");
} else {
printf("Sorry, this program requires OpenGL 2.0.\n");
return 1;
}
2013-01-12 15:38:37 +00:00
#endif
#ifdef _MSC_VER
// VFSRegister("temp/", new DirectoryAssetReader("E:\\Temp\\"));
TCHAR path[MAX_PATH];
SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, 0, path);
PathAppend(path, (app_name + "\\").c_str());
#else
// Mac / Linux
2014-09-13 22:13:24 +00:00
char path[2048];
2012-09-17 17:26:28 +00:00
const char *the_path = getenv("HOME");
if (!the_path) {
struct passwd* pwd = getpwuid(getuid());
if (pwd)
2012-09-17 17:26:28 +00:00
the_path = pwd->pw_dir;
}
strcpy(path, the_path);
if (path[strlen(path)-1] != '/')
strcat(path, "/");
#endif
2012-09-28 08:01:01 +00:00
#ifdef _WIN32
NativeInit(argc, (const char **)argv, path, "D:\\", nullptr);
2012-09-28 08:01:01 +00:00
#else
NativeInit(argc, (const char **)argv, path, "/tmp", nullptr);
2012-09-28 08:01:01 +00:00
#endif
pixel_in_dps = (float)pixel_xres / dp_xres;
2013-07-15 12:53:06 +00:00
g_dpi_scale = dp_xres / (float)pixel_xres;
printf("Pixels: %i x %i\n", pixel_xres, pixel_yres);
printf("Virtual pixels: %i x %i\n", dp_xres, dp_yres);
2016-01-01 13:08:23 +00:00
GraphicsContext *graphicsContext = new GLDummyGraphicsContext();
2016-01-01 11:14:09 +00:00
NativeInitGraphics(graphicsContext);
NativeResized();
SDL_AudioSpec fmt, ret_fmt;
memset(&fmt, 0, sizeof(fmt));
fmt.freq = 44100;
fmt.format = AUDIO_S16;
fmt.channels = 2;
fmt.samples = 2048;
fmt.callback = &mixaudio;
fmt.userdata = (void *)0;
if (SDL_OpenAudio(&fmt, &ret_fmt) < 0) {
ELOG("Failed to open audio: %s", SDL_GetError());
} else {
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) {
ELOG("Sound buffer format does not match requested format.");
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);
ELOG("Provided output format does not match requirement, turning audio off");
SDL_CloseAudio();
}
}
// Audio must be unpaused _after_ NativeInit()
SDL_PauseAudio(0);
#ifndef _WIN32
2014-06-07 14:55:51 +00:00
joystick = new SDLJoystick();
2013-01-14 11:32:39 +00:00
#endif
EnableFZ();
2013-07-14 11:51:30 +00:00
int framecount = 0;
2013-07-14 11:51:30 +00:00
float t = 0;
float lastT = 0;
uint32_t pad_buttons = 0; // legacy pad buttons
while (true) {
input_state.accelerometer_valid = false;
input_state.mouse_valid = true;
SDL_Event event;
while (SDL_PollEvent(&event)) {
float mx = event.motion.x * g_dpi_scale;
float my = event.motion.y * g_dpi_scale;
2013-07-14 11:51:30 +00:00
switch (event.type) {
case SDL_QUIT:
g_QuitRequested = 1;
2013-07-14 11:51:30 +00:00
break;
#if !defined(MOBILE_DEVICE)
2014-09-01 13:46:14 +00:00
case SDL_WINDOWEVENT:
switch (event.window.event) {
2014-09-01 13:46:14 +00:00
case SDL_WINDOWEVENT_RESIZED:
{
Uint32 window_flags = SDL_GetWindowFlags(g_Screen);
bool fullscreen = (window_flags & SDL_WINDOW_FULLSCREEN);
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();
#if defined(PPSSPP)
// 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
}
#endif
2014-09-01 13:46:14 +00:00
break;
}
default:
break;
}
break;
#endif
2013-07-14 11:51:30 +00:00
case SDL_KEYDOWN:
{
int k = event.key.keysym.sym;
KeyInput key;
key.flags = KEY_DOWN;
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);
for (int i = 0; i < ARRAY_SIZE(legacyKeyMap); i++) {
if (legacyKeyMap[i] == key.keyCode)
pad_buttons |= 1 << i;
}
2013-07-14 11:51:30 +00:00
break;
}
case SDL_KEYUP:
{
int k = event.key.keysym.sym;
KeyInput key;
key.flags = KEY_UP;
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);
for (int i = 0; i < ARRAY_SIZE(legacyKeyMap); i++) {
if (legacyKeyMap[i] == key.keyCode)
pad_buttons &= ~(1 << i);
}
2013-07-14 11:51:30 +00:00
break;
}
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:
{
input_state.pointer_x[0] = mx;
input_state.pointer_y[0] = my;
input_state.pointer_down[0] = true;
input_state.mouse_valid = true;
2013-07-08 09:03:14 +00:00
TouchInput input;
input.x = mx;
input.y = my;
input.flags = TOUCH_DOWN | TOUCH_MOUSE;
2013-07-08 09:03:14 +00:00
input.id = 0;
NativeTouch(input);
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:
{
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) {
key.keyCode = NKCODE_EXT_MOUSEWHEEL_UP;
2014-09-01 13:46:14 +00:00
} else {
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
// 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:
2013-06-15 13:12:18 +00:00
if (input_state.pointer_down[0]) {
input_state.pointer_x[0] = mx;
input_state.pointer_y[0] = my;
input_state.mouse_valid = true;
2013-05-02 22:21:39 +00:00
TouchInput input;
input.x = mx;
input.y = my;
input.flags = TOUCH_MOVE | TOUCH_MOUSE;
2013-05-02 22:21:39 +00:00
input.id = 0;
NativeTouch(input);
}
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:
{
input_state.pointer_x[0] = mx;
input_state.pointer_y[0] = my;
input_state.pointer_down[0] = false;
input_state.mouse_valid = true;
2013-07-08 09:03:14 +00:00
//input_state.mouse_buttons_up = 1;
TouchInput input;
input.x = mx;
input.y = my;
input.flags = TOUCH_UP | TOUCH_MOUSE;
2013-07-08 09:03:14 +00:00
input.id = 0;
NativeTouch(input);
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:
{
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;
}
2013-07-14 11:51:30 +00:00
break;
default:
2014-06-07 14:55:51 +00:00
#ifndef _WIN32
joystick->ProcessInput(event);
#endif
2013-11-22 12:19:32 +00:00
break;
}
}
if (g_QuitRequested)
break;
2015-09-06 18:14:00 +00:00
const uint8_t *keys = SDL_GetKeyboardState(NULL);
SimulateGamepad(keys, &input_state);
input_state.pad_buttons = pad_buttons;
UpdateInputState(&input_state, true);
UpdateRunLoop(&input_state);
2014-02-12 11:45:26 +00:00
if (g_QuitRequested)
break;
#if defined(PPSSPP) && !defined(MOBILE_DEVICE)
if (lastUIState != GetUIState()) {
lastUIState = GetUIState();
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
if (framecount % 60 == 0) {
// glsl_refresh(); // auto-reloads modified GLSL shaders once per second.
}
#ifdef USING_EGL
2013-01-12 15:38:37 +00:00
eglSwapBuffers(g_eglDisplay, g_eglSurface);
#else
2016-01-01 11:14:09 +00:00
if (!keys[SDLK_TAB] || t - lastT >= 1.0/60.0) {
2014-09-01 13:46:14 +00:00
SDL_GL_SwapWindow(g_Screen);
2013-01-26 01:24:50 +00:00
lastT = t;
}
2013-01-12 15:38:37 +00:00
#endif
ToggleFullScreenIfFlagSet();
time_update();
t = time_now();
framecount++;
}
#ifndef _WIN32
2014-06-07 14:55:51 +00:00
delete joystick;
#endif
NativeShutdownGraphics();
2016-01-01 11:14:09 +00:00
graphicsContext->Shutdown();
delete graphicsContext;
NativeShutdown();
// Faster exit, thanks to the OS. Remove this if you want to debug shutdown
// The speed difference is only really noticable on Linux. On Windows you do notice it though
#ifndef MOBILE_DEVICE
2012-11-27 15:38:24 +00:00
exit(0);
#endif
SDL_PauseAudio(1);
SDL_CloseAudio();
#ifdef USING_EGL
2013-01-12 15:38:37 +00:00
EGL_Close();
#endif
2014-09-01 13:46:14 +00:00
SDL_GL_DeleteContext(glContext);
SDL_Quit();
net::Shutdown();
#ifdef RPI
bcm_host_deinit();
#endif
exit(0);
return 0;
}