ppsspp/Qt/QtHost.cpp
Peter Tissen b40278fff6 Set Qt to build debug menus on non Linux platforms and fix some debugger crashes
This makes Common.pro and Native.pro use their own distinct object directories
to prevent the two fbo.cpp and sha1.c files repectively to clash into the same
*.obj file.

The registerlist had polled register values of -1 in some cases when resizing
the window. Making sure it doesn't poll values smaller than 0 fixes it.

The memoryview crashed if it was switched into symbol mode outside of the
symbol definition range. Also, under Windows the monospace font wasn't
used because there's not literal font called "monospace" so a font family
hint had to be used.

The UpdateDisassembly() function jumped to the current PC, which sounds
reasonable at first but the issue is that this function gets called when a
breakpoint get set, so if you set a breakpoint somewhere completely
different then you suddenly lose that position for no good reason.

Enable the desktop QT interface for all non ARM QT platforms

make sure the QtMain.cpp in the native submodule is also updated
2013-11-05 13:17:27 +01:00

517 lines
12 KiB
C++

// This file is Qt's equivalent of NativeApp.cpp
#include <QFileInfo>
#include <QDebug>
#include <QDir>
#include <QCoreApplication>
#include "QtHost.h"
#include "LogManager.h"
#include "Core/Debugger/SymbolMap.h"
#include "Core/Config.h"
#include "base/NativeApp.h"
#include "UI/EmuScreen.h"
#include "UI/UIShader.h"
#include "UI/MiscScreens.h"
#include "UI/GameInfoCache.h"
#include "UI/OnScreenDisplay.h"
#include "UI/ui_atlas.h"
#include "ui/ui.h"
#include "ui/ui_context.h"
#include "GPU/ge_constants.h"
#include "EmuThread.h"
static UI::Theme ui_theme;
const char *stateToLoad = NULL;
std::string boot_filename = "";
Texture *uiTexture;
UIContext *uiContext;
ScreenManager *screenManager;
std::string game_title;
event m_hGPUStepEvent;
recursive_mutex m_hGPUStepMutex;
recursive_mutex pendingMutex;
static bool isMessagePending;
static std::string pendingMessage;
static std::string pendingValue;
QtHost::QtHost(MainWindow *mainWindow_)
: mainWindow(mainWindow_)
, m_GPUStep(false)
, m_GPUFlag(0)
{
QObject::connect(this,SIGNAL(BootDoneSignal()),mainWindow,SLOT(Boot()));
}
bool QtHost::InitGL(std::string *error_string)
{
return true;
}
void QtHost::ShutdownGL()
{
}
void QtHost::SetWindowTitle(const char *message)
{
QString title = "PPSSPP " + QString(PPSSPP_GIT_VERSION) + " - " + QString::fromUtf8(message);
mainWindow->setWindowTitle(title);
}
void QtHost::UpdateUI()
{
mainWindow->UpdateMenus();
}
void QtHost::UpdateMemView()
{
if(mainWindow->GetDialogMemory())
mainWindow->GetDialogMemory()->Update();
}
void QtHost::UpdateDisassembly()
{
if(mainWindow->GetDialogDisasm())
{
mainWindow->GetDialogDisasm()->Update();
}
if(mainWindow->GetDialogDisplaylist())
{
mainWindow->GetDialogDisplaylist()->Update();
}
}
void QtHost::SetDebugMode(bool mode)
{
if(mainWindow->GetDialogDisasm())
mainWindow->GetDialogDisasm()->SetDebugMode(mode);
}
void QtHost::BeginFrame()
{
mainWindow->Update();
}
void QtHost::EndFrame()
{
}
void QtHost::InitSound(PMixer *mixer)
{
g_mixer = mixer;
}
void QtHost::ShutdownSound()
{
g_mixer = 0;
}
void QtHost::BootDone()
{
symbolMap.SortSymbols();
emit BootDoneSignal();
}
static QString SymbolMapFilename(QString currentFilename)
{
std::string result = currentFilename.toStdString();
size_t dot = result.rfind('.');
if (dot == result.npos)
return (result + ".map").c_str();
result.replace(dot, result.npos, ".map");
return result.c_str();
}
bool QtHost::AttemptLoadSymbolMap()
{
return symbolMap.LoadSymbolMap(SymbolMapFilename(GetCurrentFilename()).toStdString().c_str());
}
void QtHost::PrepareShutdown()
{
symbolMap.SaveSymbolMap(SymbolMapFilename(GetCurrentFilename()).toStdString().c_str());
}
void QtHost::AddSymbol(std::string name, u32 addr, u32 size, int type=0)
{
symbolMap.AddSymbol(name.c_str(), addr, size, (SymbolType)type);
}
bool QtHost::IsDebuggingEnabled()
{
#ifdef _DEBUG
return true;
#else
return false;
#endif
}
bool QtHost::GPUDebuggingActive()
{
auto dialogDisplayList = mainWindow->GetDialogDisplaylist();
if (dialogDisplayList && dialogDisplayList->isVisible())
{
if (GpuStep())
SendGPUStart();
return true;
}
return false;
}
void QtHost::GPUNotifyCommand(u32 pc)
{
u32 op = Memory::ReadUnchecked_U32(pc);
u32 cmd = op >> 24;
if (GpuStep())
SendGPUWait(cmd, pc, &gstate);
}
void QtHost::SendCoreWait(bool isWaiting)
{
mainWindow->CoreEmitWait(isWaiting);
}
bool QtHost::GpuStep()
{
return m_GPUStep;
}
void QtHost::SendGPUStart()
{
EmuThread_LockDraw(false);
if(m_GPUFlag == -1)
{
m_GPUFlag = 0;
}
EmuThread_LockDraw(true);
}
void QtHost::SendGPUWait(u32 cmd, u32 addr, void *data)
{
EmuThread_LockDraw(false);
if((m_GPUFlag == 1 && (cmd == GE_CMD_PRIM || cmd == GE_CMD_BEZIER || cmd == GE_CMD_SPLINE)))
{
// Break after the draw
m_GPUFlag = 0;
}
else if(m_GPUFlag == 0)
{
mainWindow->GetDialogDisasm()->UpdateDisplayList();
mainWindow->GetDialogDisplaylist()->Update();
m_hGPUStepEvent.wait(m_hGPUStepMutex);
}
else if(m_GPUFlag == 2 && addr == m_GPUData)
{
mainWindow->GetDialogDisasm()->UpdateDisplayList();
mainWindow->GetDialogDisplaylist()->Update();
m_hGPUStepEvent.wait(m_hGPUStepMutex);
}
else if(m_GPUFlag == 3 && (cmd == GE_CMD_PRIM || cmd == GE_CMD_BEZIER || cmd == GE_CMD_SPLINE))
{
GPUgstate *state = (GPUgstate*)data;
u32 texAddr = (state->texaddr[0] & 0xFFFFF0) | ((state->texbufwidth[0]<<8) & 0x0F000000);
if(texAddr == m_GPUData)
{
mainWindow->GetDialogDisasm()->UpdateDisplayList();
mainWindow->GetDialogDisplaylist()->Update();
m_hGPUStepEvent.wait(m_hGPUStepMutex);
}
}
EmuThread_LockDraw(true);
}
void QtHost::SetGPUStep(bool value, int flag, u32 data)
{
m_GPUStep = value;
m_GPUFlag = flag;
m_GPUData = data;
}
void QtHost::NextGPUStep()
{
m_hGPUStepEvent.notify_one();
}
void NativeInit(int argc, const char *argv[], const char *savegame_directory, const char *external_directory, const char *installID)
{
Common::EnableCrashingOnCrashes();
isMessagePending = false;
std::string user_data_path = savegame_directory;
std::string memcard_path = QDir::homePath().toStdString() + "/.ppsspp/";
VFSRegister("", new DirectoryAssetReader("assets/"));
VFSRegister("", new DirectoryAssetReader(user_data_path.c_str()));
g_Config.AddSearchPath(user_data_path);
g_Config.AddSearchPath(memcard_path + "PSP/SYSTEM/");
g_Config.SetDefaultPath(g_Config.memCardDirectory + "PSP/SYSTEM/");
g_Config.Load();
const char *fileToLog = 0;
// Parse command line
for (int i = 1; i < argc; i++) {
if (argv[i][0] == '-') {
switch (argv[i][1]) {
case 'j':
g_Config.bJit = true;
g_Config.bSaveSettings = false;
break;
case 'i':
g_Config.bJit = false;
g_Config.bSaveSettings = false;
break;
case 's':
g_Config.bAutoRun = false;
g_Config.bSaveSettings = false;
break;
case '-':
if (!strcmp(argv[i], "--log") && i < argc - 1)
fileToLog = argv[++i];
if (!strncmp(argv[i], "--log=", strlen("--log=")) && strlen(argv[i]) > strlen("--log="))
fileToLog = argv[i] + strlen("--log=");
if (!strcmp(argv[i], "--state") && i < argc - 1)
stateToLoad = argv[++i];
if (!strncmp(argv[i], "--state=", strlen("--state=")) && strlen(argv[i]) > strlen("--state="))
stateToLoad = argv[i] + strlen("--state=");
break;
}
}
else if (fileToStart.isNull())
{
fileToStart = QString(argv[i]);
if (!QFile::exists(fileToStart))
{
qCritical("File '%s' does not exist!", qPrintable(fileToStart));
exit(1);
}
}
else
{
qCritical("Can only boot one file. Ignoring file '%s'", qPrintable(fileToStart));
}
}
if (g_Config.currentDirectory == "")
{
g_Config.currentDirectory = QDir::homePath().toStdString();
}
g_Config.memCardDirectory = QDir::homePath().toStdString() + "/.ppsspp/";
#if defined(Q_OS_LINUX) && !defined(ARM)
std::string program_path = QCoreApplication::applicationDirPath().toStdString();
if (File::Exists(program_path + "/flash0"))
g_Config.flash0Directory = program_path + "/flash0/";
else if (File::Exists(program_path + "/../flash0"))
g_Config.flash0Directory = program_path + "/../flash0/";
else
g_Config.flash0Directory = g_Config.memCardDirectory + "/flash0/";
#else
g_Config.flash0Directory = g_Config.memCardDirectory + "/flash0/";
#endif
LogManager::Init();
if (fileToLog != NULL)
LogManager::GetInstance()->ChangeFileLog(fileToLog);
g_gameInfoCache.Init();
#if !defined(ARM)
// Start Desktop UI
MainWindow* mainWindow = new MainWindow();
mainWindow->show();
#endif
}
int NativeMix(short *audio, int num_samples)
{
if (g_mixer)
return g_mixer->Mix(audio, num_samples);
else
return 0;
}
void NativeInitGraphics()
{
gl_lost_manager_init();
ui_draw2d.SetAtlas(&ui_atlas);
screenManager = new ScreenManager();
if (boot_filename.empty()) {
screenManager->switchScreen(new LogoScreen());
} else {
// Go directly into the game.
screenManager->switchScreen(new EmuScreen(boot_filename));
}
UIShader_Init();
UITheme theme = {0};
theme.uiFont = UBUNTU24;
theme.uiFontSmall = UBUNTU24;
theme.uiFontSmaller = UBUNTU24;
theme.buttonImage = I_SOLIDWHITE; // not using classic buttons
theme.buttonSelected = I_SOLIDWHITE;
theme.checkOn = I_CHECKEDBOX;
theme.checkOff = I_SQUARE;
#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);
#else
ui_theme.uiFont = UI::FontStyle(UBUNTU24, "", 20);
ui_theme.uiFontSmall = UI::FontStyle(UBUNTU24, "", 14);
ui_theme.uiFontSmaller = UI::FontStyle(UBUNTU24, "", 11);
#endif
ui_theme.checkOn = I_CHECKEDBOX;
ui_theme.checkOff = I_SQUARE;
ui_theme.whiteImage = I_SOLIDWHITE;
ui_theme.sliderKnob = I_CIRCLE;
ui_theme.dropShadow4Grid = I_DROP_SHADOW;
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);
ui_theme.itemDownStyle.fgColor = 0xFFFFFFFF;
ui_theme.itemDisabledStyle.background = UI::Drawable(0x55E0D4AF);
ui_theme.itemDisabledStyle.fgColor = 0xFFcccccc;
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.popupTitle.fgColor = 0xFFE3BE59;
ui_draw2d.Init();
ui_draw2d_front.Init();
UIInit(&ui_atlas, theme);
uiTexture = new Texture();
if (!uiTexture->Load("ui_atlas.zim"))
{
qDebug() << "Failed to load texture";
}
uiTexture->Bind(0);
uiContext = new UIContext();
uiContext->theme = &ui_theme;
uiContext->Init(UIShader_Get(), UIShader_GetPlain(), uiTexture, &ui_draw2d, &ui_draw2d_front);
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);
glstate.viewport.set(0, 0, pixel_xres, pixel_yres);
}
void NativeRender()
{
// 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,0,0,1);
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glstate.Restore();
glViewport(0, 0, pixel_xres, pixel_yres);
Matrix4x4 ortho;
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();
}
void NativeMessageReceived(const char *message, const char *value)
{
lock_guard lock(pendingMutex);
if (!isMessagePending) {
pendingMessage = message;
pendingValue = value;
isMessagePending = true;
}
}
void NativeUpdate(InputState &input)
{
{
lock_guard lock(pendingMutex);
if (isMessagePending) {
screenManager->sendMessage(pendingMessage.c_str(), pendingValue.c_str());
isMessagePending = false;
}
}
UIUpdateMouse(0, input.pointer_x[0], input.pointer_y[0], input.pointer_down[0]);
screenManager->update(input);
}
void NativeTouch(const TouchInput &touch) {
if (screenManager)
screenManager->touch(touch);
}
void NativeKey(const KeyInput &key) {
g_buttonTracker.Process(key);
if (screenManager)
screenManager->key(key);
}
void NativeAxis(const AxisInput &key) {
if (screenManager)
screenManager->axis(key);
}
void NativeShutdownGraphics()
{
delete uiTexture;
uiTexture = NULL;
screenManager->shutdown();
delete screenManager;
screenManager = 0;
ui_draw2d.Shutdown();
ui_draw2d_front.Shutdown();
UIShader_Shutdown();
gl_lost_manager_shutdown();
}
void NativeShutdown()
{
delete host;
host = 0;
g_Config.Save();
LogManager::Shutdown();
// 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
exit(0);
#endif
}