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
// If your platform is not supported, it is suggested to use Qt instead.
2012-04-10 09:59:57 +00:00
2021-10-20 04:26:37 +00:00
# include <cstdlib>
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
2020-03-04 06:53:03 +00:00
# include "ppsspp_config.h"
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-12-13 22:22:21 +00:00
# include <cmath>
2022-04-02 23:07:51 +00:00
# include <csignal>
2018-01-21 17:03:19 +00:00
# include <thread>
2018-08-28 19:40:58 +00:00
# include <locale>
2013-12-13 11:49:38 +00:00
2020-10-04 08:10:55 +00:00
# include "Common/System/Display.h"
2020-10-04 08:30:18 +00:00
# include "Common/System/System.h"
# include "Common/System/NativeApp.h"
2017-12-07 16:02:00 +00:00
# include "ext/glslang/glslang/Public/ShaderLang.h"
2020-10-04 18:48:47 +00:00
# include "Common/Data/Format/PNGLoad.h"
# include "Common/Net/Resolve.h"
2019-08-06 15:12:19 +00:00
# include "NKCodeFromSDL.h"
2020-10-03 22:25:21 +00:00
# include "Common/Math/math_util.h"
2020-10-04 21:24:14 +00:00
# include "Common/GPU/OpenGL/GLRenderManager.h"
2021-02-19 06:59:56 +00:00
# include "Common/Profiler/Profiler.h"
2017-12-13 21:58:45 +00:00
2017-12-21 21:09:26 +00:00
# include "SDL_syswm.h"
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
2020-10-01 11:05:04 +00:00
# include "Common/GraphicsContext.h"
# include "Common/TimeUtil.h"
# include "Common/Input/InputState.h"
# include "Common/Input/KeyCodes.h"
# include "Common/Data/Collections/ConstMap.h"
# include "Common/Data/Encoding/Utf8.h"
# include "Common/Thread/ThreadUtil.h"
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"
2018-06-17 01:42:31 +00:00
# include "Core/ConfigValues.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 ;
2020-01-05 17:04:07 +00:00
static float g_RefreshRate = 60.f ;
2014-05-02 21:46:24 +00:00
2020-05-16 15:46:21 +00:00
static SDL_AudioSpec g_retFmt ;
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
}
2020-05-16 19:15:51 +00:00
void sdl_mixaudio_callback ( void * userdata , Uint8 * stream , int len ) {
NativeMix ( ( short * ) stream , len / ( 2 * 2 ) ) ;
2019-09-15 20:23:48 +00:00
}
static SDL_AudioDeviceID audioDev = 0 ;
// Must be called after NativeInit().
2019-09-15 20:42:56 +00:00
static void InitSDLAudioDevice ( const std : : string & name = " " ) {
2020-05-16 15:46:21 +00:00
SDL_AudioSpec fmt ;
2019-09-15 20:23:48 +00:00
memset ( & fmt , 0 , sizeof ( fmt ) ) ;
fmt . freq = 44100 ;
fmt . format = AUDIO_S16 ;
fmt . channels = 2 ;
2020-05-17 09:50:34 +00:00
fmt . samples = 1024 ;
2020-05-16 19:15:51 +00:00
fmt . callback = & sdl_mixaudio_callback ;
2019-09-15 20:23:48 +00:00
fmt . userdata = nullptr ;
2019-09-15 20:42:56 +00:00
std : : string startDevice = name ;
if ( startDevice . empty ( ) ) {
startDevice = g_Config . sAudioDevice ;
}
2019-09-15 20:23:48 +00:00
audioDev = 0 ;
2019-09-15 20:42:56 +00:00
if ( ! startDevice . empty ( ) ) {
2020-05-16 15:46:21 +00:00
audioDev = SDL_OpenAudioDevice ( startDevice . c_str ( ) , 0 , & fmt , & g_retFmt , SDL_AUDIO_ALLOW_FREQUENCY_CHANGE ) ;
2019-09-15 20:23:48 +00:00
if ( audioDev < = 0 ) {
2020-08-15 14:25:50 +00:00
WARN_LOG ( AUDIO , " Failed to open audio device: %s " , startDevice . c_str ( ) ) ;
2019-09-15 20:23:48 +00:00
}
}
if ( audioDev < = 0 ) {
2020-08-15 14:25:50 +00:00
INFO_LOG ( AUDIO , " SDL: Trying a different audio device " ) ;
2020-05-16 15:46:21 +00:00
audioDev = SDL_OpenAudioDevice ( nullptr , 0 , & fmt , & g_retFmt , SDL_AUDIO_ALLOW_FREQUENCY_CHANGE ) ;
2019-09-15 20:23:48 +00:00
}
if ( audioDev < = 0 ) {
2020-08-15 14:25:50 +00:00
ERROR_LOG ( AUDIO , " Failed to open audio device: %s " , SDL_GetError ( ) ) ;
2019-09-15 20:23:48 +00:00
} else {
2020-05-16 15:46:21 +00:00
if ( g_retFmt . samples ! = fmt . samples ) // Notify, but still use it
2020-08-15 14:25:50 +00:00
ERROR_LOG ( AUDIO , " Output audio samples: %d (requested: %d) " , g_retFmt . samples , fmt . samples ) ;
2020-05-16 19:15:51 +00:00
if ( g_retFmt . format ! = fmt . format | | g_retFmt . channels ! = fmt . channels ) {
2020-08-15 14:25:50 +00:00
ERROR_LOG ( AUDIO , " Sound buffer format does not match requested format. " ) ;
ERROR_LOG ( AUDIO , " Output audio freq: %d (requested: %d) " , g_retFmt . freq , fmt . freq ) ;
ERROR_LOG ( AUDIO , " Output audio format: %d (requested: %d) " , g_retFmt . format , fmt . format ) ;
ERROR_LOG ( AUDIO , " Output audio channels: %d (requested: %d) " , g_retFmt . channels , fmt . channels ) ;
ERROR_LOG ( AUDIO , " Provided output format does not match requirement, turning audio off " ) ;
2019-09-15 20:23:48 +00:00
SDL_CloseAudioDevice ( audioDev ) ;
}
SDL_PauseAudioDevice ( audioDev , 0 ) ;
}
}
static void StopSDLAudioDevice ( ) {
if ( audioDev > 0 ) {
SDL_PauseAudioDevice ( audioDev , 1 ) ;
SDL_CloseAudioDevice ( audioDev ) ;
}
}
2013-01-12 15:38:37 +00:00
// Simple implementations of System functions
2012-07-16 13:00:52 +00:00
2022-07-10 20:34:44 +00:00
void System_Toast ( const char * text ) {
2012-07-05 21:30:35 +00:00
# ifdef _WIN32
2018-03-25 21:18:31 +00:00
std : : wstring str = ConvertUTF8ToWString ( text ) ;
MessageBox ( 0 , str . c_str ( ) , L " 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 ;
2019-02-23 09:55:28 +00:00
} else if ( ! strcmp ( command , " graphics_restart " ) ) {
// Not sure how we best do this, but do a clean exit, better than being stuck in a bad state.
g_QuitRequested = true ;
2019-05-15 21:29:40 +00:00
} else if ( ! strcmp ( command , " setclipboardtext " ) ) {
SDL_SetClipboardText ( parameter ) ;
2019-09-15 20:23:48 +00:00
} else if ( ! strcmp ( command , " audio_resetDevice " ) ) {
StopSDLAudioDevice ( ) ;
InitSDLAudioDevice ( ) ;
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 ; }
2019-05-21 21:22:56 +00:00
void OpenDirectory ( const char * path ) {
2021-10-09 22:56:39 +00:00
# if PPSSPP_PLATFORM(WINDOWS)
2021-10-09 22:58:49 +00:00
SFGAOF flags ;
PIDLIST_ABSOLUTE pidl = nullptr ;
HRESULT hr = SHParseDisplayName ( ConvertUTF8ToWString ( ReplaceAll ( path , " / " , " \\ " ) ) . c_str ( ) , nullptr , & pidl , 0 , & flags ) ;
2019-05-21 21:22:56 +00:00
if ( pidl ) {
2021-10-09 22:58:49 +00:00
if ( SUCCEEDED ( hr ) )
SHOpenFolderAndSelectItems ( pidl , 0 , NULL , 0 ) ;
CoTaskMemFree ( pidl ) ;
2019-05-21 21:22:56 +00:00
}
2021-10-20 04:26:37 +00:00
# elif PPSSPP_PLATFORM(MAC) || (PPSSPP_PLATFORM(LINUX) && !PPSSPP_PLATFORM(ANDROID))
pid_t pid = fork ( ) ;
if ( pid < 0 )
return ;
if ( pid = = 0 ) {
# if PPSSPP_PLATFORM(MAC)
execlp ( " open " , " open " , path , nullptr ) ;
# else
execlp ( " xdg-open " , " xdg-open " , path , nullptr ) ;
# endif
exit ( 1 ) ;
}
2019-05-21 21:22:56 +00:00
# endif
}
2013-12-04 16:38:37 +00:00
void LaunchBrowser ( const char * url ) {
2020-03-04 06:53:03 +00:00
# if PPSSPP_PLATFORM(SWITCH)
2020-03-15 14:56:38 +00:00
Uuid uuid = { 0 } ;
2020-03-04 06:53:03 +00:00
WebWifiConfig conf ;
2020-03-15 14:56:38 +00:00
webWifiCreate ( & conf , NULL , url , uuid , 0 ) ;
2020-03-04 06:53:03 +00:00
webWifiShow ( & conf , NULL ) ;
# elif defined(MOBILE_DEVICE)
2020-08-15 14:25:50 +00:00
INFO_LOG ( SYSTEM , " Would have gone to %s but LaunchBrowser is not implemented on this platform " , url ) ;
2015-05-26 14:51:53 +00:00
# elif defined(_WIN32)
2018-03-25 21:18:31 +00:00
std : : wstring wurl = ConvertUTF8ToWString ( url ) ;
ShellExecute ( NULL , L " open " , wurl . c_str ( ) , 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 ) {
2020-08-15 14:25:50 +00:00
INFO_LOG ( SYSTEM , " Would have gone to %s but xdg-utils seems not to be installed " , url ) ;
2014-01-30 16:23:04 +00:00
}
2012-04-10 09:59:57 +00:00
# endif
}
2013-12-04 16:38:37 +00:00
void LaunchMarket ( const char * url ) {
2020-03-04 06:53:03 +00:00
# if PPSSPP_PLATFORM(SWITCH)
2020-03-15 14:56:38 +00:00
Uuid uuid = { 0 } ;
2020-03-04 06:53:03 +00:00
WebWifiConfig conf ;
2020-03-15 14:56:38 +00:00
webWifiCreate ( & conf , NULL , url , uuid , 0 ) ;
2020-03-04 06:53:03 +00:00
webWifiShow ( & conf , NULL ) ;
# elif defined(MOBILE_DEVICE)
2020-08-15 14:25:50 +00:00
INFO_LOG ( SYSTEM , " Would have gone to %s but LaunchMarket is not implemented on this platform " , url ) ;
2015-05-26 14:51:53 +00:00
# elif defined(_WIN32)
2018-03-25 21:18:31 +00:00
std : : wstring wurl = ConvertUTF8ToWString ( url ) ;
ShellExecute ( NULL , L " open " , wurl . c_str ( ) , 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 ) {
2020-08-15 14:25:50 +00:00
INFO_LOG ( SYSTEM , " Would have gone to %s but xdg-utils seems not to be installed " , url ) ;
2014-01-30 16:23:04 +00:00
}
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)
2020-08-15 14:25:50 +00:00
INFO_LOG ( SYSTEM , " Would have opened your email client for %s but LaunchEmail is not implemented on this platform " , email_address ) ;
2015-05-26 14:51:53 +00:00
# elif defined(_WIN32)
2018-03-25 21:18:31 +00:00
std : : wstring mailto = std : : wstring ( L " mailto: " ) + ConvertUTF8ToWString ( email_address ) ;
ShellExecute ( NULL , L " open " , mailto . 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 ) {
2020-08-15 14:25:50 +00:00
INFO_LOG ( SYSTEM , " Would have gone to %s but xdg-utils seems not to be installed " , email_address ) ;
2014-01-30 16:23:04 +00:00
}
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__
2020-01-24 17:39:37 +00:00
return " SDL:macOS " ;
2020-03-04 06:53:03 +00:00
# elif PPSSPP_PLATFORM(SWITCH)
return " SDL:Horizon " ;
2013-11-26 06:38:00 +00:00
# else
2013-09-04 09:28:19 +00:00
return " SDL: " ;
2013-11-26 06:38:00 +00:00
# endif
2018-08-28 19:40:58 +00:00
case SYSPROP_LANGREGION : {
// Get user-preferred locale from OS
setlocale ( LC_ALL , " " ) ;
std : : string locale ( setlocale ( LC_ALL , NULL ) ) ;
// Set c and c++ strings back to POSIX
std : : locale : : global ( std : : locale ( " POSIX " ) ) ;
if ( ! locale . empty ( ) ) {
2020-09-27 19:55:02 +00:00
// Technically, this is an opaque string, but try to find the locale code.
size_t messagesPos = locale . find ( " LC_MESSAGES= " ) ;
if ( messagesPos ! = std : : string : : npos ) {
messagesPos + = strlen ( " LC_MESSAGES= " ) ;
size_t semi = locale . find ( ' ; ' , messagesPos ) ;
locale = locale . substr ( messagesPos , semi - messagesPos ) ;
}
2018-08-28 19:40:58 +00:00
if ( locale . find ( " _ " , 0 ) ! = std : : string : : npos ) {
if ( locale . find ( " . " , 0 ) ! = std : : string : : npos ) {
return locale . substr ( 0 , locale . find ( " . " , 0 ) ) ;
}
2020-09-27 19:55:02 +00:00
return locale ;
2018-08-28 19:40:58 +00:00
}
}
2013-09-04 09:28:19 +00:00
return " en_US " ;
2018-08-28 19:40:58 +00:00
}
2019-05-15 21:29:40 +00:00
case SYSPROP_CLIPBOARD_TEXT :
return SDL_HasClipboardText ( ) ? SDL_GetClipboardText ( ) : " " ;
2019-09-15 20:23:48 +00:00
case SYSPROP_AUDIO_DEVICE_LIST :
{
std : : string result ;
for ( int i = 0 ; i < SDL_GetNumAudioDevices ( 0 ) ; + + i ) {
const char * name = SDL_GetAudioDeviceName ( i , 0 ) ;
if ( ! name ) {
continue ;
}
if ( i = = 0 ) {
result = name ;
} else {
result . append ( 1 , ' \0 ' ) ;
result . append ( name ) ;
}
}
return result ;
}
2013-09-04 09:28:19 +00:00
default :
return " " ;
}
2013-08-17 22:14:25 +00:00
}
2021-01-06 15:37:04 +00:00
std : : vector < std : : string > System_GetPropertyStringVec ( SystemProperty prop ) {
2021-01-09 22:45:49 +00:00
std : : vector < std : : string > result ;
2021-01-06 15:37:04 +00:00
switch ( prop ) {
2021-01-09 22:45:49 +00:00
case SYSPROP_TEMP_DIRS :
if ( getenv ( " TMPDIR " ) & & strlen ( getenv ( " TMPDIR " ) ) ! = 0 )
result . push_back ( getenv ( " TMPDIR " ) ) ;
2021-01-10 19:33:17 +00:00
if ( getenv ( " TMP " ) & & strlen ( getenv ( " TMP " ) ) ! = 0 )
2021-01-09 22:45:49 +00:00
result . push_back ( getenv ( " TMP " ) ) ;
2021-01-10 19:33:17 +00:00
if ( getenv ( " TEMP " ) & & strlen ( getenv ( " TEMP " ) ) ! = 0 )
2021-01-09 22:45:49 +00:00
result . push_back ( getenv ( " TEMP " ) ) ;
return result ;
2021-01-06 15:37:04 +00:00
default :
2021-01-09 22:45:49 +00:00
return result ;
2021-01-06 15:37:04 +00:00
}
}
2015-01-11 13:15:26 +00:00
int System_GetPropertyInt ( SystemProperty prop ) {
switch ( prop ) {
case SYSPROP_AUDIO_SAMPLE_RATE :
2020-05-16 15:46:21 +00:00
return g_retFmt . freq ;
case SYSPROP_AUDIO_FRAMES_PER_BUFFER :
return g_retFmt . samples ;
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
2020-01-19 09:34:21 +00:00
case SYSPROP_DISPLAY_COUNT :
return SDL_GetNumVideoDisplays ( ) ;
2022-01-10 00:11:08 +00:00
case SYSPROP_KEYBOARD_LAYOUT :
{
char q , w , y ;
q = SDL_GetKeyFromScancode ( SDL_SCANCODE_Q ) ;
w = SDL_GetKeyFromScancode ( SDL_SCANCODE_W ) ;
y = SDL_GetKeyFromScancode ( SDL_SCANCODE_Y ) ;
if ( q = = ' a ' & & w = = ' z ' & & y = = ' y ' )
return KEYBOARD_LAYOUT_AZERTY ;
else if ( q = = ' q ' & & w = = ' w ' & & y = = ' z ' )
return KEYBOARD_LAYOUT_QWERTZ ;
return KEYBOARD_LAYOUT_QWERTY ;
}
2017-04-30 00:35:12 +00:00
default :
return - 1 ;
}
}
2020-01-05 17:04:07 +00:00
float System_GetPropertyFloat ( SystemProperty prop ) {
switch ( prop ) {
case SYSPROP_DISPLAY_REFRESH_RATE :
return g_RefreshRate ;
2020-04-06 00:06:36 +00:00
case SYSPROP_DISPLAY_SAFE_INSET_LEFT :
case SYSPROP_DISPLAY_SAFE_INSET_RIGHT :
case SYSPROP_DISPLAY_SAFE_INSET_TOP :
case SYSPROP_DISPLAY_SAFE_INSET_BOTTOM :
return 0.0f ;
2020-01-05 17:04:07 +00:00
default :
return - 1 ;
}
}
2017-04-30 00:35:12 +00:00
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
2021-02-21 21:02:11 +00:00
case SYSPROP_CAN_JIT :
return true ;
2022-07-10 20:34:44 +00:00
case SYSPROP_SUPPORTS_OPEN_FILE_IN_EDITOR :
return true ; // FileUtil.cpp: OpenFileInEditor
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
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 ) {
2020-11-30 23:46:26 +00:00
SetCurrentThreadName ( " Emu " ) ;
2018-01-21 17:47:07 +00:00
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 ;
2021-03-11 22:13:57 +00:00
graphicsContext - > StopThread ( ) ;
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 [ ] ) {
2018-05-09 19:22:47 +00:00
for ( int i = 1 ; i < argc ; i + + ) {
if ( ! strcmp ( argv [ i ] , " --version " ) ) {
printf ( " %s \n " , PPSSPP_GIT_VERSION ) ;
return 0 ;
}
}
2020-03-15 14:56:38 +00:00
# ifdef HAVE_LIBNX
socketInitializeDefault ( ) ;
nxlinkStdio ( ) ;
2022-04-02 23:07:51 +00:00
# else // HAVE_LIBNX
// Ignore sigpipe.
if ( signal ( SIGPIPE , SIG_IGN ) = = SIG_ERR ) {
perror ( " Unable to ignore SIGPIPE " ) ;
}
2020-03-15 14:56:38 +00:00
# endif // HAVE_LIBNX
2021-02-19 06:59:56 +00:00
PROFILE_INIT ( ) ;
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
2020-10-06 02:50:25 +00:00
# ifdef SDL_HINT_TOUCH_MOUSE_EVENTS
SDL_SetHint ( SDL_HINT_TOUCH_MOUSE_EVENTS , " 0 " ) ;
# endif
2017-12-15 14:29:19 +00:00
if ( VulkanMayBeAvailable ( ) ) {
2019-03-23 15:32:18 +00:00
printf ( " DEBUG: Vulkan might be available. \n " ) ;
2017-12-15 14:29:19 +00:00
} else {
2019-03-23 15:32:18 +00:00
printf ( " DEBUG: Vulkan is not available, not using Vulkan. \n " ) ;
2017-12-15 14:29:19 +00:00
}
2019-03-11 15:06:06 +00:00
SDL_version compiled ;
SDL_version linked ;
2017-12-13 21:58:45 +00:00
int set_xres = - 1 ;
int set_yres = - 1 ;
2018-09-07 10:44:41 +00:00
int w = 0 , h = 0 ;
2017-12-13 21:58:45 +00:00
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
}
2019-03-11 15:06:06 +00:00
SDL_VERSION ( & compiled ) ;
SDL_GetVersion ( & linked ) ;
printf ( " Info: We compiled against SDL version %d.%d.%d " , compiled . major , compiled . minor , compiled . patch ) ;
if ( compiled . minor ! = linked . minor | | compiled . patch ! = linked . patch ) {
printf ( " , but we are linking against SDL version %d.%d.%d., be aware that this can lead to unexpected behaviors \n " , linked . major , linked . minor , linked . patch ) ;
} else {
printf ( " and we are linking against SDL version %d.%d.%d. :) \n " , linked . major , linked . minor , linked . patch ) ;
}
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 ;
2020-01-05 17:04:07 +00:00
g_RefreshRate = displayMode . refresh_rate ;
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 ) ;
2019-02-17 14:27:41 +00:00
// Force fullscreen if the 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 ;
}
2018-06-06 03:39:56 +00:00
// If we're on mobile, don't try for windowed either.
2020-03-15 14:56:38 +00:00
# if defined(MOBILE_DEVICE) && !PPSSPP_PLATFORM(SWITCH)
2019-02-18 13:00:28 +00:00
mode | = SDL_WINDOW_FULLSCREEN ;
2020-03-15 14:56:38 +00:00
# elif defined(USING_FBDEV) || PPSSPP_PLATFORM(SWITCH)
2019-02-17 14:27:41 +00:00
mode | = SDL_WINDOW_FULLSCREEN_DESKTOP ;
2018-06-06 03:39:56 +00:00
# else
2019-02-18 13:00:28 +00:00
mode | = SDL_WINDOW_RESIZABLE ;
2018-06-06 03:39:56 +00:00
# endif
2014-09-01 13:46:14 +00:00
if ( mode & SDL_WINDOW_FULLSCREEN_DESKTOP ) {
pixel_xres = g_DesktopWidth ;
pixel_yres = g_DesktopHeight ;
2022-05-28 22:47:12 +00:00
g_Config . iForceFullScreen = 1 ;
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 ) ;
}
2022-05-28 22:47:12 +00:00
g_Config . iForceFullScreen = 0 ;
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 ] ;
2020-03-15 14:56:38 +00:00
# if PPSSPP_PLATFORM(SWITCH)
strcpy ( path , " /switch/ppsspp/ " ) ;
# else
2017-10-01 20:59:21 +00:00
const char * the_path = getenv ( " HOME " ) ;
if ( ! the_path ) {
2020-03-15 14:56:38 +00:00
struct passwd * pwd = getpwuid ( getuid ( ) ) ;
2017-10-01 20:59:21 +00:00
if ( pwd )
the_path = pwd - > pw_dir ;
}
2020-03-15 14:56:38 +00:00
if ( the_path )
strcpy ( path , the_path ) ;
# endif
if ( strlen ( path ) > 0 & & path [ strlen ( path ) - 1 ] ! = ' / ' )
2017-10-01 20:59:21 +00:00
strcat ( path , " / " ) ;
2021-11-06 06:22:08 +00:00
# if PPSSPP_PLATFORM(MAC)
std : : string external_dir_str ;
if ( SDL_GetBasePath ( ) )
external_dir_str = std : : string ( SDL_GetBasePath ( ) ) + " /assets " ;
else
external_dir_str = " /tmp " ;
const char * external_dir = external_dir_str . c_str ( ) ;
# else
const char * external_dir = " /tmp " ;
# endif
NativeInit ( remain_argc , ( const char * * ) remain_argv , path , external_dir , nullptr ) ;
2017-10-01 20:59:21 +00:00
// Use the setting from the config when initing the window.
2022-05-28 22:47:12 +00:00
if ( g_Config . UseFullScreen ( ) )
2017-10-01 20:59:21 +00:00
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
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 ;
2022-04-02 23:34:13 +00:00
# if !PPSSPP_PLATFORM(SWITCH)
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 ;
}
2022-04-02 23:34:13 +00:00
# endif
2017-12-13 21:58:45 +00:00
}
2017-12-15 14:29:19 +00:00
2018-02-10 08:09:13 +00:00
bool useEmuThread = g_Config . iGPUBackend = = ( int ) GPUBackend : : OPENGL ;
2018-02-07 14:52:19 +00:00
SDL_SetWindowTitle ( window , ( app_name_nice + " " + PPSSPP_GIT_VERSION ) . c_str ( ) ) ;
2020-01-17 13:53:56 +00:00
char iconPath [ PATH_MAX ] ;
snprintf ( iconPath , PATH_MAX , " %sassets/icon_regular_72.png " , SDL_GetBasePath ( ) ? SDL_GetBasePath ( ) : " " ) ;
int width = 0 , height = 0 ;
unsigned char * imageData ;
2020-10-04 18:48:47 +00:00
if ( pngLoad ( iconPath , & width , & height , & imageData ) = = 1 ) {
2020-01-17 13:53:56 +00:00
SDL_Surface * surface = SDL_CreateRGBSurface ( 0 , width , height , 32 ,
0x000000ff , 0x0000ff00 , 0x00ff0000 , 0xff000000 ) ;
memcpy ( surface - > pixels , imageData , width * height * 4 ) ;
SDL_SetWindowIcon ( window , surface ) ;
SDL_FreeSurface ( surface ) ;
free ( imageData ) ;
imageData = NULL ;
}
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
# 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
2019-10-03 05:50:24 +00:00
// Ensure that the swap interval is set after context creation (needed for kmsdrm)
SDL_GL_SetSwapInterval ( 1 ) ;
2019-09-15 20:23:48 +00:00
InitSDLAudioDevice ( ) ;
2012-07-06 20:32:32 +00:00
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 ( ) ;
2020-02-04 08:54:01 +00:00
float mouseDeltaX = 0 ;
float mouseDeltaY = 0 ;
2020-03-05 15:36:26 +00:00
int mouseWheelMovedUpFrames = 0 ;
int mouseWheelMovedDownFrames = 0 ;
2020-02-04 06:47:02 +00:00
bool mouseCaptured = false ;
2019-02-18 13:26:57 +00:00
bool windowHidden = false ;
2017-03-15 04:09:43 +00:00
while ( true ) {
2018-03-25 13:32:51 +00:00
double startTime = time_now_d ( ) ;
2020-03-05 14:57:45 +00:00
// SDL2 doesn't consider the mousewheel a button anymore
2020-03-05 15:36:26 +00:00
// so let's send the KEY_UP if it was moved after some frames
if ( mouseWheelMovedUpFrames > 0 ) {
mouseWheelMovedUpFrames - - ;
if ( mouseWheelMovedUpFrames = = 0 ) {
KeyInput key ;
key . deviceId = DEVICE_ID_MOUSE ;
key . keyCode = NKCODE_EXT_MOUSEWHEEL_UP ;
key . flags = KEY_UP ;
NativeKey ( key ) ;
}
}
if ( mouseWheelMovedDownFrames > 0 ) {
mouseWheelMovedDownFrames - - ;
if ( mouseWheelMovedDownFrames = = 0 ) {
KeyInput key ;
key . deviceId = DEVICE_ID_MOUSE ;
key . keyCode = NKCODE_EXT_MOUSEWHEEL_DOWN ;
key . flags = KEY_UP ;
NativeKey ( key ) ;
}
2020-03-05 14:57:45 +00:00
}
2018-09-07 10:44:41 +00:00
SDL_Event event , touchEvent ;
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 ) {
2018-10-07 07:54:57 +00:00
case SDL_WINDOWEVENT_SIZE_CHANGED : // better than RESIZED, more general
2015-12-27 20:03:26 +00:00
{
2020-06-23 23:04:41 +00:00
int new_width = event . window . data1 ;
int new_height = event . window . data2 ;
2019-09-01 14:22:29 +00:00
windowHidden = false ;
Core_NotifyWindowHidden ( windowHidden ) ;
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
2020-06-23 23:04:41 +00:00
// This one calls NativeResized if the size changed.
UpdateScreenScale ( new_width , new_height ) ;
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
2022-05-28 22:47:12 +00:00
if ( g_Config . UseFullScreen ( ) ! = fullscreen ) {
g_Config . bFullScreen = fullscreen ;
g_Config . iForceFullScreen = - 1 ;
}
2015-12-27 20:03:26 +00:00
// 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
2019-02-18 13:26:57 +00:00
case SDL_WINDOWEVENT_MINIMIZED :
case SDL_WINDOWEVENT_HIDDEN :
windowHidden = true ;
Core_NotifyWindowHidden ( windowHidden ) ;
break ;
case SDL_WINDOWEVENT_EXPOSED :
case SDL_WINDOWEVENT_SHOWN :
2019-09-01 21:00:35 +00:00
windowHidden = false ;
Core_NotifyWindowHidden ( windowHidden ) ;
break ;
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 ;
}
2019-02-17 12:53:30 +00:00
// This behavior doesn't feel right on a macbook with a touchpad.
# if !PPSSPP_PLATFORM(MAC)
2018-09-07 10:44:41 +00:00
case SDL_FINGERMOTION :
{
SDL_GetWindowSize ( window , & w , & h ) ;
2020-10-06 02:50:25 +00:00
TouchInput input ;
input . id = event . tfinger . fingerId ;
input . x = event . tfinger . x * w ;
input . y = event . tfinger . y * h ;
input . flags = TOUCH_MOVE ;
input . timestamp = event . tfinger . timestamp ;
NativeTouch ( input ) ;
2018-09-07 10:44:41 +00:00
break ;
}
case SDL_FINGERDOWN :
{
SDL_GetWindowSize ( window , & w , & h ) ;
2020-10-06 02:50:25 +00:00
TouchInput input ;
input . id = event . tfinger . fingerId ;
input . x = event . tfinger . x * w ;
input . y = event . tfinger . y * h ;
input . flags = TOUCH_DOWN ;
input . timestamp = event . tfinger . timestamp ;
NativeTouch ( input ) ;
KeyInput key ;
key . deviceId = DEVICE_ID_MOUSE ;
key . keyCode = NKCODE_EXT_MOUSEBUTTON_1 ;
key . flags = KEY_DOWN ;
NativeKey ( key ) ;
2018-09-07 10:44:41 +00:00
break ;
}
case SDL_FINGERUP :
{
SDL_GetWindowSize ( window , & w , & h ) ;
2020-10-06 02:50:25 +00:00
TouchInput input ;
input . id = event . tfinger . fingerId ;
input . x = event . tfinger . x * w ;
input . y = event . tfinger . y * h ;
input . flags = TOUCH_UP ;
input . timestamp = event . tfinger . timestamp ;
NativeTouch ( input ) ;
KeyInput key ;
key . deviceId = DEVICE_ID_MOUSE ;
key . keyCode = NKCODE_EXT_MOUSEBUTTON_1 ;
key . flags = KEY_UP ;
NativeKey ( key ) ;
2018-09-07 10:44:41 +00:00
break ;
}
2019-02-17 12:53:30 +00:00
# endif
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 ;
2020-03-05 14:57:45 +00:00
case SDL_BUTTON_MIDDLE :
{
KeyInput key ( DEVICE_ID_MOUSE , NKCODE_EXT_MOUSEBUTTON_3 , KEY_DOWN ) ;
NativeKey ( key ) ;
}
break ;
case SDL_BUTTON_X1 :
{
KeyInput key ( DEVICE_ID_MOUSE , NKCODE_EXT_MOUSEBUTTON_4 , KEY_DOWN ) ;
NativeKey ( key ) ;
}
break ;
case SDL_BUTTON_X2 :
{
KeyInput key ( DEVICE_ID_MOUSE , NKCODE_EXT_MOUSEBUTTON_5 , KEY_DOWN ) ;
NativeKey ( key ) ;
}
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 ;
2020-03-05 15:36:26 +00:00
mouseWheelMovedUpFrames = 5 ;
2014-09-01 13:46:14 +00:00
} else {
2013-08-04 17:30:34 +00:00
key . keyCode = NKCODE_EXT_MOUSEWHEEL_DOWN ;
2020-03-05 15:36:26 +00:00
mouseWheelMovedDownFrames = 5 ;
2013-07-08 09:03:14 +00:00
}
2014-09-01 13:46:14 +00:00
key . flags = KEY_DOWN ;
NativeKey ( key ) ;
2013-05-25 13:12:46 +00:00
}
2020-03-03 16:17:58 +00:00
break ;
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
}
2020-03-03 16:17:58 +00:00
mouseDeltaX + = event . motion . xrel ;
mouseDeltaY + = event . motion . yrel ;
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 ;
2020-03-05 14:57:45 +00:00
case SDL_BUTTON_MIDDLE :
{
KeyInput key ( DEVICE_ID_MOUSE , NKCODE_EXT_MOUSEBUTTON_3 , KEY_UP ) ;
NativeKey ( key ) ;
}
break ;
case SDL_BUTTON_X1 :
{
KeyInput key ( DEVICE_ID_MOUSE , NKCODE_EXT_MOUSEBUTTON_4 , KEY_UP ) ;
NativeKey ( key ) ;
}
break ;
case SDL_BUTTON_X2 :
{
KeyInput key ( DEVICE_ID_MOUSE , NKCODE_EXT_MOUSEBUTTON_5 , 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 ;
2019-09-15 20:42:56 +00:00
# if SDL_VERSION_ATLEAST(2, 0, 4)
case SDL_AUDIODEVICEADDED :
// Automatically switch to the new device.
if ( event . adevice . iscapture = = 0 ) {
const char * name = SDL_GetAudioDeviceName ( event . adevice . which , 0 ) ;
if ( ! name ) {
break ;
}
2020-01-01 15:47:26 +00:00
// Don't start auto switching for a second, because some devices init on start.
bool doAutoSwitch = g_Config . bAutoAudioDevice & & framecount > 60 ;
if ( doAutoSwitch | | g_Config . sAudioDevice = = name ) {
2019-09-15 20:42:56 +00:00
StopSDLAudioDevice ( ) ;
InitSDLAudioDevice ( name ? name : " " ) ;
}
}
break ;
case SDL_AUDIODEVICEREMOVED :
if ( event . adevice . iscapture = = 0 & & event . adevice . which = = audioDev ) {
StopSDLAudioDevice ( ) ;
InitSDLAudioDevice ( ) ;
}
break ;
# endif
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 ( ) ;
2022-05-28 22:47:12 +00:00
if ( lastUIState = = UISTATE_INGAME & & g_Config . UseFullScreen ( ) & & ! g_Config . bShowTouchControls )
2013-11-05 02:58:30 +00:00
SDL_ShowCursor ( SDL_DISABLE ) ;
2022-05-28 22:47:12 +00:00
if ( lastUIState ! = UISTATE_INGAME | | ! g_Config . UseFullScreen ( ) )
2013-11-05 02:58:30 +00:00
SDL_ShowCursor ( SDL_ENABLE ) ;
}
# endif
2012-10-26 16:42:17 +00:00
2020-02-04 04:11:07 +00:00
// Disabled by default, needs a workaround to map to psp keys.
if ( g_Config . bMouseControl ) {
float scaleFactor_x = g_dpi_scale_x * 0.1 * g_Config . fMouseSensitivity ;
float scaleFactor_y = g_dpi_scale_y * 0.1 * g_Config . fMouseSensitivity ;
AxisInput axisX , axisY ;
axisX . axisId = JOYSTICK_AXIS_MOUSE_REL_X ;
axisX . deviceId = DEVICE_ID_MOUSE ;
2020-02-04 06:47:02 +00:00
axisX . value = std : : max ( - 1.0f , std : : min ( 1.0f , mouseDeltaX * scaleFactor_x ) ) ;
2020-02-04 04:11:07 +00:00
axisY . axisId = JOYSTICK_AXIS_MOUSE_REL_Y ;
axisY . deviceId = DEVICE_ID_MOUSE ;
2020-02-04 06:47:02 +00:00
axisY . value = std : : max ( - 1.0f , std : : min ( 1.0f , mouseDeltaY * scaleFactor_y ) ) ;
2020-02-04 04:11:07 +00:00
if ( GetUIState ( ) = = UISTATE_INGAME | | g_Config . bMapMouse ) {
NativeAxis ( axisX ) ;
NativeAxis ( axisY ) ;
}
2020-02-04 06:47:02 +00:00
mouseDeltaX * = g_Config . fMouseSmoothing ;
mouseDeltaY * = g_Config . fMouseSmoothing ;
}
bool captureMouseCondition = g_Config . bMouseControl & & ( ( GetUIState ( ) = = UISTATE_INGAME & & g_Config . bMouseConfine ) | | g_Config . bMapMouse ) ;
if ( mouseCaptured ! = captureMouseCondition ) {
mouseCaptured = captureMouseCondition ;
if ( captureMouseCondition )
SDL_SetRelativeMouseMode ( SDL_TRUE ) ;
else
SDL_SetRelativeMouseMode ( SDL_FALSE ) ;
2020-02-04 04:11:07 +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
2019-02-18 13:26:57 +00:00
bool renderThreadPaused = windowHidden & & g_Config . bPauseWhenMinimized & & emuThreadState ! = ( int ) EmuThreadState : : DISABLED ;
if ( emuThreadState ! = ( int ) EmuThreadState : : DISABLED & & ! renderThreadPaused ) {
2018-01-21 17:03:19 +00:00
if ( ! graphicsContext - > ThreadFrame ( ) )
break ;
}
2018-03-25 13:32:51 +00:00
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 ) ;
2018-03-25 13:32:51 +00:00
// Simple throttling to not burn the GPU in the menu.
2019-02-18 13:26:57 +00:00
if ( GetUIState ( ) ! = UISTATE_INGAME | | ! PSP_IsInited ( ) | | renderThreadPaused ) {
2018-03-25 13:32:51 +00:00
double diffTime = time_now_d ( ) - startTime ;
int sleepTime = ( int ) ( 1000.0 / 60.0 ) - ( int ) ( diffTime * 1000.0 ) ;
if ( sleepTime > 0 )
sleep_ms ( sleepTime ) ;
}
2012-07-06 20:32:32 +00:00
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 ( ) ;
2018-02-11 19:40:11 +00:00
while ( graphicsContext - > ThreadFrame ( ) ) {
2018-02-07 11:22:19 +00:00
// Need to keep eating frames to allow the EmuThread to exit correctly.
2018-02-11 19:40:11 +00:00
continue ;
2018-02-07 11:22:19 +00:00
}
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 ( ) ;
}
2018-01-31 17:35:48 +00:00
graphicsContext - > ThreadEnd ( ) ;
2018-02-07 11:22:19 +00:00
2015-12-26 03:39:52 +00:00
NativeShutdown ( ) ;
2018-03-25 21:19:02 +00:00
// Destroys Draw, which is used in NativeShutdown to shutdown.
graphicsContext - > ShutdownFromRenderThread ( ) ;
2020-08-11 20:28:36 +00:00
graphicsContext - > Shutdown ( ) ;
2017-03-11 11:54:13 +00:00
delete graphicsContext ;
2017-12-13 21:58:45 +00:00
2019-09-15 19:42:49 +00:00
if ( audioDev > 0 ) {
SDL_PauseAudioDevice ( audioDev , 1 ) ;
SDL_CloseAudioDevice ( audioDev ) ;
}
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 ( ) ;
2020-08-11 20:28:36 +00:00
printf ( " Leaving main " ) ;
2020-03-15 14:56:38 +00:00
# ifdef HAVE_LIBNX
socketExit ( ) ;
# endif
2012-04-10 09:59:57 +00:00
return 0 ;
}