ppsspp/UI/NativeApp.cpp

712 lines
19 KiB
C++
Raw Normal View History

2012-11-01 15:19:01 +00:00
// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
2012-11-01 15:19:01 +00:00
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2012-11-01 15:19:01 +00:00
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
// NativeApp implementation for platforms that will use that framework, like:
// Android, Linux, MacOSX.
//
// Native is a cross platform framework. It's not very mature and mostly
// just built according to the needs of my own apps.
//
// Windows has its own code that bypasses the framework entirely.
// Background worker threads should be spawned in NativeInit and joined
// in NativeShutdown.
#include <locale.h>
#ifdef _WIN32
#include <png.h>
#include "ext/jpge/jpge.h"
#endif
2012-11-01 15:19:01 +00:00
#include "base/logging.h"
2013-06-01 21:34:50 +00:00
#include "base/mutex.h"
2012-11-01 15:19:01 +00:00
#include "base/NativeApp.h"
#include "file/vfs.h"
#include "file/zip_read.h"
#include "thread/thread.h"
#include "net/http_client.h"
#include "gfx_es2/gl_state.h"
#include "gfx_es2/draw_text.h"
#include "gfx_es2/draw_buffer.h"
2012-11-01 15:19:01 +00:00
#include "gfx/gl_lost_manager.h"
#include "gfx/texture.h"
#include "i18n/i18n.h"
2012-11-01 15:19:01 +00:00
#include "input/input_state.h"
#include "math/math_util.h"
2012-11-01 15:19:01 +00:00
#include "math/lin/matrix4x4.h"
#include "ui/ui.h"
2012-11-01 15:19:01 +00:00
#include "ui/screen.h"
#include "ui/ui_context.h"
2013-06-01 21:34:50 +00:00
#include "ui/view.h"
#include "util/text/utf8.h"
2013-06-01 21:34:50 +00:00
#include "Common/FileUtil.h"
#include "Common/LogManager.h"
#include "Core/PSPMixer.h"
#include "Core/CPU.h"
#include "Core/Config.h"
#include "Core/HLE/sceCtrl.h"
#include "Core/Host.h"
#include "Core/SaveState.h"
#include "Core/Util/GameManager.h"
2013-06-01 21:34:50 +00:00
#include "Common/MemArena.h"
2012-11-01 15:19:01 +00:00
#include "ui_atlas.h"
#include "EmuScreen.h"
#include "GameInfoCache.h"
2012-11-01 15:19:01 +00:00
#include "UIShader.h"
#include "UI/OnScreenDisplay.h"
#include "UI/MiscScreens.h"
2013-06-01 21:34:50 +00:00
// The new UI framework, for initialization
static UI::Theme ui_theme;
#ifdef ARM
2013-03-30 20:14:18 +00:00
#include "../../android/jni/ArmEmitterTest.h"
#endif
2013-06-25 21:18:16 +00:00
#ifdef IOS
#include "ios/iOSCoreAudio.h"
#elif defined(__APPLE__)
#include <mach-o/dyld.h>
2013-06-25 21:18:16 +00:00
#endif
// https://github.com/richq/android-ndk-profiler
#ifdef ANDROID_NDK_PROFILER
#include <stdlib.h>
#include "android/android-ndk-profiler/prof.h"
#endif
2012-11-01 15:19:01 +00:00
Texture *uiTexture;
ScreenManager *screenManager;
2012-11-01 15:19:01 +00:00
std::string config_filename;
std::string game_title;
2012-11-01 15:19:01 +00:00
#ifdef IOS
bool iosCanUseJit;
#endif
// Really need to clean this mess of globals up... but instead I add more :P
bool g_TakeScreenshot;
2013-10-31 10:06:54 +00:00
static bool isOuya;
recursive_mutex pendingMutex;
static bool isMessagePending;
static std::string pendingMessage;
static std::string pendingValue;
static UIContext *uiContext;
std::thread *graphicsLoadThread;
2012-11-01 15:19:01 +00:00
class AndroidLogger : public LogListener
{
public:
void Log(LogTypes::LOG_LEVELS level, const char *msg)
{
switch (level)
{
case LogTypes::LVERBOSE:
case LogTypes::LDEBUG:
case LogTypes::LINFO:
ILOG("%s", msg);
break;
case LogTypes::LERROR:
ELOG("%s", msg);
break;
case LogTypes::LWARNING:
WLOG("%s", msg);
break;
case LogTypes::LNOTICE:
default:
ILOG("%s", msg);
break;
}
}
2012-11-01 15:19:01 +00:00
};
// TODO: Get rid of this junk
class NativeHost : public Host
{
public:
NativeHost() {
// hasRendered = false;
}
virtual void UpdateUI() {}
2012-11-01 15:19:01 +00:00
virtual void UpdateMemView() {}
virtual void UpdateDisassembly() {}
2012-11-01 15:19:01 +00:00
virtual void SetDebugMode(bool mode) { }
2012-11-01 15:19:01 +00:00
2013-03-15 07:43:35 +00:00
virtual bool InitGL(std::string *error_message) { return true; }
virtual void ShutdownGL() {}
2012-11-01 15:19:01 +00:00
virtual void InitSound(PMixer *mixer);
virtual void UpdateSound() {}
virtual void ShutdownSound();
2012-11-01 15:19:01 +00:00
// this is sent from EMU thread! Make sure that Host handles it properly!
virtual void BootDone() {}
2012-11-01 15:19:01 +00:00
virtual bool IsDebuggingEnabled() {return false;}
virtual bool AttemptLoadSymbolMap() {return false;}
virtual void ResetSymbolMap() {}
virtual void AddSymbol(std::string name, u32 addr, u32 size, int type=0) {}
virtual void SetWindowTitle(const char *message) {
2013-08-20 17:09:47 +00:00
if (message)
game_title = message;
else
game_title = "";
}
2012-11-01 15:19:01 +00:00
};
// globals
static PMixer *g_mixer = 0;
2013-03-29 19:03:11 +00:00
#ifndef _WIN32
2012-11-01 15:19:01 +00:00
static AndroidLogger *logger = 0;
2013-03-29 19:03:11 +00:00
#endif
2012-11-01 15:19:01 +00:00
std::string boot_filename = "";
2013-06-01 21:34:50 +00:00
void NativeHost::InitSound(PMixer *mixer) {
2012-11-01 15:19:01 +00:00
g_mixer = mixer;
2013-06-25 21:18:16 +00:00
#ifdef IOS
iOSCoreAudioInit();
2013-06-25 21:18:16 +00:00
#endif
2012-11-01 15:19:01 +00:00
}
2013-06-01 21:34:50 +00:00
void NativeHost::ShutdownSound() {
2013-06-25 21:18:16 +00:00
#ifdef IOS
iOSCoreAudioShutdown();
2013-06-25 21:18:16 +00:00
#endif
2012-11-01 15:19:01 +00:00
g_mixer = 0;
}
2013-06-01 21:34:50 +00:00
int NativeMix(short *audio, int num_samples) {
if (g_mixer) {
2013-06-04 21:54:37 +00:00
num_samples = g_mixer->Mix(audio, num_samples);
2013-06-01 21:34:50 +00:00
} else {
2013-06-04 21:54:37 +00:00
memset(audio, 0, num_samples * 2 * sizeof(short));
2012-11-01 15:19:01 +00:00
}
2013-06-04 21:54:37 +00:00
return num_samples;
2012-11-01 15:19:01 +00:00
}
2013-06-01 21:34:50 +00:00
void NativeGetAppInfo(std::string *app_dir_name, std::string *app_nice_name, bool *landscape) {
2012-11-01 15:19:01 +00:00
*app_nice_name = "PPSSPP";
*app_dir_name = "ppsspp";
*landscape = true;
#if defined(ARM) && defined(ANDROID)
ArmEmitterTest();
2013-03-02 00:36:18 +00:00
#endif
2012-11-01 15:19:01 +00:00
}
2013-06-01 21:34:50 +00:00
void NativeInit(int argc, const char *argv[],
const char *savegame_directory, const char *external_directory, const char *installID) {
#ifdef ANDROID_NDK_PROFILER
setenv("CPUPROFILE_FREQUENCY", "500", 1);
setenv("CPUPROFILE", "/sdcard/gmon.out", 1);
monstartup("ppsspp_jni.so");
#endif
bool skipLogo = false;
EnableFZ();
setlocale( LC_ALL, "C" );
std::string user_data_path = savegame_directory;
isMessagePending = false;
#ifdef IOS
user_data_path += "/";
#endif
// We want this to be FIRST.
#ifdef USING_QT_UI
VFSRegister("", new AssetsAssetReader());
#elif defined(BLACKBERRY) || defined(IOS)
// Packed assets are included in app
VFSRegister("", new DirectoryAssetReader(external_directory));
#elif defined(__APPLE__) || (defined(__linux__) && !defined(ANDROID))
VFSRegister("", new DirectoryAssetReader((File::GetExeDirectory() + "assets/").c_str()));
VFSRegister("", new DirectoryAssetReader((File::GetExeDirectory()).c_str()));
#else
VFSRegister("", new DirectoryAssetReader("assets/"));
#endif
2013-07-10 01:46:27 +00:00
VFSRegister("", new DirectoryAssetReader(savegame_directory));
2012-11-01 15:19:01 +00:00
host = new NativeHost();
2012-11-01 15:19:01 +00:00
#if defined(ANDROID)
g_Config.internalDataDirectory = savegame_directory;
// Maybe there should be an option to use internal memory instead, but I think
// that for most people, using external memory (SDCard/USB Storage) makes the
// most sense.
g_Config.memCardDirectory = std::string(external_directory) + "/";
g_Config.flash0Directory = std::string(external_directory) + "/flash0/";
#elif defined(BLACKBERRY) || defined(__SYMBIAN32__) || defined(MEEGO_EDITION_HARMATTAN) || defined(IOS)
g_Config.memCardDirectory = user_data_path;
g_Config.flash0Directory = std::string(external_directory) + "/flash0/";
#elif !defined(_WIN32)
char* config = getenv("XDG_CONFIG_HOME");
if (!config) {
config = getenv("HOME");
strcat(config, "/.config");
}
g_Config.memCardDirectory = std::string(config) + "/ppsspp/";
g_Config.flash0Directory = File::GetExeDirectory() + "/flash0/";
#endif
2013-03-29 19:03:11 +00:00
#ifndef _WIN32
logger = new AndroidLogger();
2012-11-01 15:19:01 +00:00
LogManager::Init();
LogManager *logman = LogManager::GetInstance();
ILOG("Logman: %p", logman);
2012-11-01 15:19:01 +00:00
g_Config.AddSearchPath(user_data_path);
g_Config.AddSearchPath(g_Config.memCardDirectory + "PSP/SYSTEM/");
g_Config.SetDefaultPath(g_Config.memCardDirectory + "PSP/SYSTEM/");
g_Config.Load();
g_Config.externalDirectory = external_directory;
#endif
2013-06-19 11:13:23 +00:00
#ifdef ANDROID
// On Android, create a PSP directory tree in the external_directory,
// to hopefully reduce confusion a bit.
ILOG("Creating %s", (g_Config.memCardDirectory + "PSP").c_str());
mkDir((g_Config.memCardDirectory + "PSP").c_str());
mkDir((g_Config.memCardDirectory + "PSP/SAVEDATA").c_str());
mkDir((g_Config.memCardDirectory + "PSP/GAME").c_str());
2013-06-19 11:13:23 +00:00
#endif
const char *fileToLog = 0;
const char *stateToLoad = 0;
2012-12-01 22:20:08 +00:00
bool gfxLog = false;
// Parse command line
LogTypes::LOG_LEVELS logLevel = LogTypes::LINFO;
for (int i = 1; i < argc; i++) {
if (argv[i][0] == '-') {
switch (argv[i][1]) {
case 'd':
// Enable debug logging
2013-01-08 23:12:02 +00:00
// Note that you must also change the max log level in Log.h.
logLevel = LogTypes::LDEBUG;
break;
2012-12-01 22:20:08 +00:00
case 'g':
gfxLog = true;
break;
case 'j':
g_Config.bJit = true;
g_Config.bSaveSettings = false;
break;
case 'i':
g_Config.bJit = false;
g_Config.bSaveSettings = false;
break;
case '-':
if (!strncmp(argv[i], "--log=", strlen("--log=")) && strlen(argv[i]) > strlen("--log="))
fileToLog = argv[i] + strlen("--log=");
if (!strncmp(argv[i], "--state=", strlen("--state=")) && strlen(argv[i]) > strlen("--state="))
stateToLoad = argv[i] + strlen("--state=");
break;
}
} else {
if (boot_filename.empty()) {
boot_filename = argv[i];
skipLogo = true;
2013-07-26 12:42:38 +00:00
FileInfo info;
if (!getFileInfo(boot_filename.c_str(), &info) || info.exists == false) {
fprintf(stderr, "File not found: %s\n", boot_filename.c_str());
exit(1);
}
} else {
fprintf(stderr, "Can only boot one file");
exit(1);
}
2012-11-01 15:19:01 +00:00
}
}
if (fileToLog != NULL)
LogManager::GetInstance()->ChangeFileLog(fileToLog);
#ifndef _WIN32
2012-11-01 15:19:01 +00:00
if (g_Config.currentDirectory == "") {
#if defined(ANDROID)
2012-11-01 15:19:01 +00:00
g_Config.currentDirectory = external_directory;
#elif defined(BLACKBERRY) || defined(__SYMBIAN32__) || defined(MEEGO_EDITION_HARMATTAN) || defined(IOS) || defined(_WIN32)
g_Config.currentDirectory = savegame_directory;
2012-11-01 15:19:01 +00:00
#else
g_Config.currentDirectory = getenv("HOME");
#endif
}
for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; i++)
{
LogTypes::LOG_TYPE type = (LogTypes::LOG_TYPE)i;
logman->SetEnable(type, true);
logman->SetLogLevel(type, gfxLog && i == LogTypes::G3D ? LogTypes::LDEBUG : logLevel);
2012-11-01 15:19:01 +00:00
#ifdef ANDROID
logman->AddListener(type, logger);
2012-11-01 15:19:01 +00:00
#endif
}
// Special hack for G3D as it's very spammy. Need to make a flag for this.
2012-12-01 22:20:08 +00:00
if (!gfxLog)
logman->SetLogLevel(LogTypes::G3D, LogTypes::LERROR);
INFO_LOG(BOOT, "Logger inited.");
#endif
i18nrepo.LoadIni(g_Config.sLanguageIni);
I18NCategory *d = GetI18NCategory("DesktopUI");
// Note to translators: do not translate this/add this to PPSSPP-lang's files.
// It's intended to be custom for every user.
2013-08-30 17:52:15 +00:00
// Only add it to your own personal copies of PPSSPP.
2013-08-30 18:19:03 +00:00
#ifdef _WIN32
// TODO: Could allow a setting to specify a font file to load?
// TODO: Make this a constant if we can sanely load the font on other systems?
AddFontResourceEx(L"assets/Roboto-Condensed.ttf", FR_PRIVATE, NULL);
g_Config.sFont = d->T("Font", "Roboto");
2013-08-30 18:19:03 +00:00
#endif
if (!boot_filename.empty() && stateToLoad != NULL)
SaveState::Load(stateToLoad);
2013-10-17 14:44:09 +00:00
g_gameInfoCache.Init();
2012-11-01 15:19:01 +00:00
screenManager = new ScreenManager();
if (skipLogo) {
screenManager->switchScreen(new EmuScreen(boot_filename));
} else {
screenManager->switchScreen(new LogoScreen());
}
2013-10-31 10:06:54 +00:00
std::string sysName = System_GetProperty(SYSPROP_NAME);
isOuya = KeyMap::IsOuya(sysName);
}
void NativeInitGraphics() {
CheckGLExtensions();
gl_lost_manager_init();
ui_draw2d.SetAtlas(&ui_atlas);
ui_draw2d_front.SetAtlas(&ui_atlas);
2012-11-01 15:19:01 +00:00
UIShader_Init();
2013-06-02 21:46:03 +00:00
// memset(&ui_theme, 0, sizeof(ui_theme));
2013-06-01 21:34:50 +00:00
// New style theme
2013-08-30 18:19:03 +00:00
#ifdef _WIN32
ui_theme.uiFont = UI::FontStyle(UBUNTU24, g_Config.sFont.c_str(), 22);
ui_theme.uiFontSmall = UI::FontStyle(UBUNTU24, g_Config.sFont.c_str(), 15);
ui_theme.uiFontSmaller = UI::FontStyle(UBUNTU24, g_Config.sFont.c_str(), 12);
2013-08-30 18:19:03 +00:00
#else
ui_theme.uiFont = UI::FontStyle(UBUNTU24, "", 20);
ui_theme.uiFontSmall = UI::FontStyle(UBUNTU24, "", 14);
ui_theme.uiFontSmaller = UI::FontStyle(UBUNTU24, "", 11);
#endif
2013-06-01 21:34:50 +00:00
ui_theme.checkOn = I_CHECKEDBOX;
ui_theme.checkOff = I_SQUARE;
ui_theme.whiteImage = I_SOLIDWHITE;
2013-07-18 08:26:29 +00:00
ui_theme.sliderKnob = I_CIRCLE;
ui_theme.dropShadow4Grid = I_DROP_SHADOW;
/*
ui_theme.buttonStyle.background = UI::Drawable(UI::DRAW_4GRID, I_BUTTON);
2013-06-02 21:46:03 +00:00
ui_theme.buttonStyle.fgColor = 0xFFFFFFFF;
2013-06-03 23:25:30 +00:00
ui_theme.buttonStyle.image = I_BUTTON;
ui_theme.buttonFocusedStyle.background = UI::Drawable(UI::DRAW_4GRID, I_BUTTON, 0xFFe0e0e0);
2013-06-02 21:46:03 +00:00
ui_theme.buttonFocusedStyle.fgColor = 0xFFFFFFFF;
ui_theme.buttonDownStyle.background = UI::Drawable(UI::DRAW_4GRID, I_BUTTON_SELECTED, 0xFFFFFFFF);
2013-06-02 21:46:03 +00:00
ui_theme.buttonDownStyle.fgColor = 0xFFFFFFFF;
ui_theme.buttonDisabledStyle.background = UI::Drawable(UI::DRAW_4GRID, I_BUTTON, 0xFF404040);
2013-06-02 21:46:03 +00:00
ui_theme.buttonDisabledStyle.fgColor = 0xFF707070;
*/
ui_theme.itemStyle.background = UI::Drawable(0x55000000);
ui_theme.itemStyle.fgColor = 0xFFFFFFFF;
ui_theme.itemFocusedStyle.background = UI::Drawable(0xFFedc24c);
ui_theme.itemDownStyle.background = UI::Drawable(0xFFbd9939);
2013-08-20 16:18:30 +00:00
ui_theme.itemDownStyle.fgColor = 0xFFFFFFFF;
ui_theme.itemDisabledStyle.background = UI::Drawable(0x55E0D4AF);
ui_theme.itemDisabledStyle.fgColor = 0x80EEEEEE;
2013-10-08 13:52:21 +00:00
ui_theme.itemHighlightedStyle.background = UI::Drawable(0x55bdBB39);
ui_theme.itemHighlightedStyle.fgColor = 0xFFFFFFFF;
2013-06-01 21:34:50 +00:00
ui_theme.buttonStyle = ui_theme.itemStyle;
ui_theme.buttonFocusedStyle = ui_theme.itemFocusedStyle;
ui_theme.buttonDownStyle = ui_theme.itemDownStyle;
ui_theme.buttonDisabledStyle = ui_theme.itemDisabledStyle;
ui_theme.buttonHighlightedStyle = ui_theme.itemHighlightedStyle;
ui_theme.popupTitle.fgColor = 0xFFE3BE59;
ui_draw2d.Init();
ui_draw2d_front.Init();
2012-11-01 15:19:01 +00:00
uiTexture = new Texture();
2013-06-05 14:48:16 +00:00
if (!uiTexture->Load("ui_atlas.zim")) {
PanicAlert("Failed to load ui_atlas.zim.\n\nPlace it in the directory \"assets\" under your PPSSPP directory.");
ELOG("Failed to load ui_atlas.zim");
}
uiTexture->Bind(0);
uiContext = new UIContext();
2013-06-01 21:34:50 +00:00
uiContext->theme = &ui_theme;
uiContext->Init(UIShader_Get(), UIShader_GetPlain(), uiTexture, &ui_draw2d, &ui_draw2d_front);
if (uiContext->Text())
uiContext->Text()->SetFont("Tahoma", 20, 0);
screenManager->setUIContext(uiContext);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
2013-01-11 23:00:18 +00:00
glstate.viewport.set(0, 0, pixel_xres, pixel_yres);
2012-11-01 15:19:01 +00:00
}
void NativeShutdownGraphics() {
screenManager->deviceLost();
g_gameInfoCache.Clear();
delete uiTexture;
uiTexture = NULL;
delete uiContext;
uiContext = NULL;
ui_draw2d.Shutdown();
ui_draw2d_front.Shutdown();
UIShader_Shutdown();
gl_lost_manager_shutdown();
}
void TakeScreenshot() {
#ifdef _WIN32
g_TakeScreenshot = false;
mkDir(g_Config.memCardDirectory + "/PSP/SCREENSHOT");
// First, find a free filename.
int i = 0;
char temp[256];
2013-07-07 18:56:18 +00:00
while (i < 10000){
if(g_Config.bScreenshotsAsPNG)
sprintf(temp, "%s/PSP/SCREENSHOT/screen%05d.png", g_Config.memCardDirectory.c_str(), i);
2013-07-07 18:56:18 +00:00
else
sprintf(temp, "%s/PSP/SCREENSHOT/screen%05d.jpg", g_Config.memCardDirectory.c_str(), i);
FileInfo info;
if (!getFileInfo(temp, &info))
break;
i++;
}
// Okay, allocate a buffer.
u8 *buffer = new u8[4 * pixel_xres * pixel_yres];
// Silly openGL reads upside down, we flip to another buffer for simplicity.
u8 *flipbuffer = new u8[4 * pixel_xres * pixel_yres];
glReadPixels(0, 0, pixel_xres, pixel_yres, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
for (int y = 0; y < pixel_yres; y++) {
memcpy(flipbuffer + y * pixel_xres * 4, buffer + (pixel_yres - y - 1) * pixel_xres * 4, pixel_xres * 4);
}
if (g_Config.bScreenshotsAsPNG) {
png_image png;
memset(&png, 0, sizeof(png));
png.version = PNG_IMAGE_VERSION;
png.format = PNG_FORMAT_RGB;
png.width = pixel_xres;
png.height = pixel_yres;
int stride = PNG_IMAGE_ROW_STRIDE(png);
png_image_write_to_file(&png, temp, 0, flipbuffer, pixel_xres * 4, NULL);
png_image_free(&png);
} else {
2013-07-07 18:56:18 +00:00
jpge::params params;
params.m_quality = 90;
compress_image_to_jpeg_file(temp, pixel_xres, pixel_yres, 4, flipbuffer, params);
}
delete [] buffer;
delete [] flipbuffer;
osm.Show(temp);
#endif
}
void DrawDownloadsOverlay(UIContext &ctx) {
// Thin bar at the top of the screen like Chrome.
std::vector<float> progress = g_DownloadManager.GetCurrentProgress();
if (progress.empty()) {
return;
}
static const uint32_t colors[4] = {
0xFFFFFFFF,
0xFFCCCCCC,
0xFFAAAAAA,
0xFF777777,
};
ctx.Begin();
int h = 5;
for (int i = 0; i < progress.size(); i++) {
float barWidth = 10 + (dp_xres - 10) * progress[i];
Bounds bounds(0, h * i, barWidth, h);
UI::Drawable solid(colors[i & 3]);
ctx.FillRect(solid, bounds);
}
ctx.End();
ctx.Flush();
}
2013-06-01 21:34:50 +00:00
void NativeRender() {
g_GameManager.Update();
glstate.depthWrite.set(GL_TRUE);
glstate.colorMask.set(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
// Clearing the screen at the start of the frame is an optimization for tiled mobile GPUs, as it then doesn't need to keep it around between frames.
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
2013-03-29 17:50:08 +00:00
glstate.viewport.set(0, 0, pixel_xres, pixel_yres);
glstate.Restore();
2013-03-29 17:50:08 +00:00
Matrix4x4 ortho;
2012-11-01 15:19:01 +00:00
ortho.setOrtho(0.0f, dp_xres, dp_yres, 0.0f, -1.0f, 1.0f);
glsl_bind(UIShader_Get());
glUniformMatrix4fv(UIShader_Get()->u_worldviewproj, 1, GL_FALSE, ortho.getReadPtr());
screenManager->render();
if (screenManager->getUIContext()->Text()) {
screenManager->getUIContext()->Text()->OncePerFrame();
}
DrawDownloadsOverlay(*screenManager->getUIContext());
if (g_TakeScreenshot) {
TakeScreenshot();
}
2012-11-01 15:19:01 +00:00
}
2013-06-01 21:34:50 +00:00
void NativeUpdate(InputState &input) {
{
lock_guard lock(pendingMutex);
if (isMessagePending) {
screenManager->sendMessage(pendingMessage.c_str(), pendingValue.c_str());
isMessagePending = false;
}
}
g_DownloadManager.Update();
screenManager->update(input);
}
2012-11-01 15:19:01 +00:00
2013-06-01 21:34:50 +00:00
void NativeDeviceLost() {
g_gameInfoCache.Clear();
screenManager->deviceLost();
2012-11-01 15:19:01 +00:00
gl_lost();
glstate.Restore();
2012-11-01 15:19:01 +00:00
// Should dirty EVERYTHING
}
2013-06-01 21:34:50 +00:00
bool NativeIsAtTopLevel() {
Screen *currentScreen = screenManager->topScreen();
if (currentScreen) {
bool top = currentScreen->isTopLevel();
ILOG("Screen toplevel: %i", (int)top);
return currentScreen->isTopLevel();
} else {
ELOG("No current screen");
return false;
}
2012-11-01 15:19:01 +00:00
}
2013-06-01 21:34:50 +00:00
void NativeTouch(const TouchInput &touch) {
if (screenManager)
screenManager->touch(touch);
2012-11-01 15:19:01 +00:00
}
void NativeKey(const KeyInput &key) {
2013-10-31 10:06:54 +00:00
// ILOG("Key code: %i flags: %i", key.keyCode, key.flags);
g_buttonTracker.Process(key);
if (screenManager)
screenManager->key(key);
}
void NativeAxis(const AxisInput &key) {
2013-10-31 10:06:54 +00:00
// ILOG("Axis id: %i value: %f", (int)key.axisId, key.value);
if (key.axisId >= JOYSTICK_AXIS_ACCELEROMETER_X && key.axisId <= JOYSTICK_AXIS_ACCELEROMETER_Z) {
// Disable accelerometer as an axis for now.
return;
}
2013-10-31 10:06:54 +00:00
if (isOuya && key.axisId >= JOYSTICK_AXIS_OUYA_UNKNOWN1 && key.axisId <= JOYSTICK_AXIS_OUYA_UNKNOWN4) {
return;
}
if (screenManager)
screenManager->axis(key);
}
2013-06-01 21:34:50 +00:00
void NativeMessageReceived(const char *message, const char *value) {
// We can only have one message queued.
lock_guard lock(pendingMutex);
if (!isMessagePending) {
pendingMessage = message;
pendingValue = value;
isMessagePending = true;
}
2012-12-01 09:39:20 +00:00
}
2012-11-01 15:19:01 +00:00
void NativeShutdown() {
screenManager->shutdown();
delete screenManager;
screenManager = 0;
2012-11-01 15:19:01 +00:00
g_gameInfoCache.Shutdown();
delete host;
host = 0;
2012-11-01 15:19:01 +00:00
g_Config.Save();
2013-03-29 22:10:40 +00:00
#ifndef _WIN32
LogManager::Shutdown();
#endif
#ifdef ANDROID_NDK_PROFILER
moncleanup();
2013-03-29 22:10:40 +00:00
#endif
2012-11-01 15:19:01 +00:00
// This means that the activity has been completely destroyed. PPSSPP does not
// boot up correctly with "dirty" global variables currently, so we hack around that
// by simply exiting.
#ifdef ANDROID
ILOG("NativeShutdown called");
2012-11-01 15:19:01 +00:00
exit(0);
#endif
#ifdef _WIN32
RemoveFontResourceEx(L"assets/Roboto-Condensed.ttf", FR_PRIVATE, NULL);
#endif
2012-11-01 15:19:01 +00:00
}