yuzu-android/src/citra_qt/bootmanager.cpp

242 lines
7.1 KiB
C++
Raw Normal View History

2014-03-31 19:26:50 -07:00
#include <QHBoxLayout>
#include <QKeyEvent>
#include <QApplication>
2014-03-31 19:26:50 -07:00
2014-04-10 17:50:10 -07:00
#include "common/common.h"
2014-03-31 19:26:50 -07:00
#include "bootmanager.hxx"
2014-04-10 17:50:10 -07:00
#include "core/core.h"
#include "core/settings.h"
#include "video_core/video_core.h"
2014-03-31 19:26:50 -07:00
#include "citra_qt/version.h"
2014-03-31 19:26:50 -07:00
#define APP_NAME "citra"
#define APP_VERSION "0.1-" VERSION
#define APP_TITLE APP_NAME " " APP_VERSION
#define COPYRIGHT "Copyright (C) 2013-2014 Citra Team"
EmuThread::EmuThread(GRenderWindow* render_window) :
filename(""), exec_cpu_step(false), cpu_running(false),
stop_run(false), render_window(render_window)
2014-03-31 19:26:50 -07:00
{
}
void EmuThread::SetFilename(std::string filename)
2014-03-31 19:26:50 -07:00
{
this->filename = filename;
2014-03-31 19:26:50 -07:00
}
void EmuThread::run()
{
stop_run = false;
while (!stop_run)
2014-04-03 18:24:07 -07:00
{
for (int tight_loop = 0; tight_loop < 10000; ++tight_loop)
2014-04-03 18:24:07 -07:00
{
if (cpu_running || exec_cpu_step)
{
if (exec_cpu_step)
exec_cpu_step = false;
2014-04-03 18:24:07 -07:00
Core::SingleStep();
if (!cpu_running) {
emit CPUStepped();
yieldCurrentThread();
}
}
2014-03-31 19:26:50 -07:00
}
}
render_window->moveContext();
2014-04-03 18:24:07 -07:00
2014-03-31 19:26:50 -07:00
Core::Stop();
}
void EmuThread::Stop()
{
2014-04-30 20:12:01 -07:00
if (!isRunning())
2014-03-31 19:26:50 -07:00
{
INFO_LOG(MASTER_LOG, "EmuThread::Stop called while emu thread wasn't running, returning...");
return;
}
stop_run = true;
2014-03-31 19:26:50 -07:00
//core::g_state = core::SYS_DIE;
wait(500);
2014-03-31 19:26:50 -07:00
if (isRunning())
{
WARN_LOG(MASTER_LOG, "EmuThread still running, terminating...");
quit();
2014-03-31 19:26:50 -07:00
wait(1000);
if (isRunning())
{
2014-04-30 20:12:01 -07:00
WARN_LOG(MASTER_LOG, "EmuThread STILL running, something is wrong here...");
terminate();
}
2014-03-31 19:26:50 -07:00
}
INFO_LOG(MASTER_LOG, "EmuThread stopped");
}
// This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL context.
// The corresponding functionality is handled in EmuThread instead
class GGLWidgetInternal : public QGLWidget
{
public:
GGLWidgetInternal(QGLFormat fmt, GRenderWindow* parent) : QGLWidget(fmt, parent)
2014-03-31 19:26:50 -07:00
{
parent_ = parent;
}
void paintEvent(QPaintEvent* ev) override
2014-03-31 19:26:50 -07:00
{
}
void resizeEvent(QResizeEvent* ev) override {
2014-04-10 17:50:10 -07:00
parent_->SetClientAreaWidth(size().width());
parent_->SetClientAreaHeight(size().height());
2014-03-31 19:26:50 -07:00
}
private:
GRenderWindow* parent_;
};
EmuThread& GRenderWindow::GetEmuThread()
{
return emu_thread;
}
GRenderWindow::GRenderWindow(QWidget* parent) : QWidget(parent), emu_thread(this), keyboard_id(0)
2014-03-31 19:26:50 -07:00
{
keyboard_id = KeyMap::NewDeviceId();
ReloadSetKeymaps();
2014-03-31 19:26:50 -07:00
// TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, WA_DontShowOnScreen, WA_DeleteOnClose
2014-04-30 20:12:01 -07:00
QGLFormat fmt;
fmt.setVersion(3,2);
fmt.setProfile(QGLFormat::CoreProfile);
// Requests a forward-compatible context, which is required to get a 3.2+ context on OS X
fmt.setOption(QGL::NoDeprecatedFunctions);
2014-04-30 20:12:01 -07:00
child = new GGLWidgetInternal(fmt, this);
2014-03-31 19:26:50 -07:00
QBoxLayout* layout = new QHBoxLayout(this);
resize(VideoCore::kScreenTopWidth, VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight);
2014-03-31 19:26:50 -07:00
layout->addWidget(child);
layout->setMargin(0);
setLayout(layout);
QObject::connect(&emu_thread, SIGNAL(started()), this, SLOT(moveContext()));
2014-03-31 19:26:50 -07:00
BackupGeometry();
}
void GRenderWindow::moveContext()
{
DoneCurrent();
// We need to move GL context to the swapping thread in Qt5
#if QT_VERSION > QT_VERSION_CHECK(5, 0, 0)
// If the thread started running, move the GL Context to the new thread. Otherwise, move it back.
child->context()->moveToThread((QThread::currentThread() == qApp->thread()) ? &emu_thread : qApp->thread());
#endif
}
2014-03-31 19:26:50 -07:00
GRenderWindow::~GRenderWindow()
{
if (emu_thread.isRunning())
emu_thread.Stop();
2014-03-31 19:26:50 -07:00
}
void GRenderWindow::SwapBuffers()
{
// MakeCurrent is already called in renderer_opengl
2014-03-31 19:26:50 -07:00
child->swapBuffers();
}
void GRenderWindow::closeEvent(QCloseEvent* event)
{
if (emu_thread.isRunning())
emu_thread.Stop();
2014-03-31 19:26:50 -07:00
QWidget::closeEvent(event);
}
void GRenderWindow::MakeCurrent()
{
child->makeCurrent();
}
void GRenderWindow::DoneCurrent()
{
child->doneCurrent();
}
void GRenderWindow::PollEvents() {
// TODO(ShizZy): Does this belong here? This is a reasonable place to update the window title
// from the main thread, but this should probably be in an event handler...
2014-04-30 20:12:01 -07:00
/*
static char title[128];
2014-03-31 19:26:50 -07:00
sprintf(title, "%s (FPS: %02.02f)", window_title_.c_str(),
video_core::g_renderer->current_fps());
setWindowTitle(title);
2014-04-30 20:12:01 -07:00
*/
2014-03-31 19:26:50 -07:00
}
void GRenderWindow::BackupGeometry()
{
geometry = ((QGLWidget*)this)->saveGeometry();
}
void GRenderWindow::RestoreGeometry()
{
// We don't want to back up the geometry here (obviously)
QWidget::restoreGeometry(geometry);
}
void GRenderWindow::restoreGeometry(const QByteArray& geometry)
{
// Make sure users of this class don't need to deal with backing up the geometry themselves
QWidget::restoreGeometry(geometry);
BackupGeometry();
}
QByteArray GRenderWindow::saveGeometry()
{
// If we are a top-level widget, store the current geometry
// otherwise, store the last backup
if (parent() == NULL)
return ((QGLWidget*)this)->saveGeometry();
else
return geometry;
}
void GRenderWindow::keyPressEvent(QKeyEvent* event)
{
EmuWindow::KeyPressed({event->key(), keyboard_id});
HID_User::PadUpdateComplete();
2014-03-31 19:26:50 -07:00
}
void GRenderWindow::keyReleaseEvent(QKeyEvent* event)
{
EmuWindow::KeyReleased({event->key(), keyboard_id});
HID_User::PadUpdateComplete();
}
void GRenderWindow::ReloadSetKeymaps()
{
KeyMap::SetKeyMapping({Settings::values.pad_a_key, keyboard_id}, HID_User::PAD_A);
KeyMap::SetKeyMapping({Settings::values.pad_b_key, keyboard_id}, HID_User::PAD_B);
KeyMap::SetKeyMapping({Settings::values.pad_select_key, keyboard_id}, HID_User::PAD_SELECT);
KeyMap::SetKeyMapping({Settings::values.pad_start_key, keyboard_id}, HID_User::PAD_START);
KeyMap::SetKeyMapping({Settings::values.pad_dright_key, keyboard_id}, HID_User::PAD_RIGHT);
KeyMap::SetKeyMapping({Settings::values.pad_dleft_key, keyboard_id}, HID_User::PAD_LEFT);
KeyMap::SetKeyMapping({Settings::values.pad_dup_key, keyboard_id}, HID_User::PAD_UP);
KeyMap::SetKeyMapping({Settings::values.pad_ddown_key, keyboard_id}, HID_User::PAD_DOWN);
KeyMap::SetKeyMapping({Settings::values.pad_r_key, keyboard_id}, HID_User::PAD_R);
KeyMap::SetKeyMapping({Settings::values.pad_l_key, keyboard_id}, HID_User::PAD_L);
KeyMap::SetKeyMapping({Settings::values.pad_x_key, keyboard_id}, HID_User::PAD_X);
KeyMap::SetKeyMapping({Settings::values.pad_y_key, keyboard_id}, HID_User::PAD_Y);
KeyMap::SetKeyMapping({Settings::values.pad_sright_key, keyboard_id}, HID_User::PAD_CIRCLE_RIGHT);
KeyMap::SetKeyMapping({Settings::values.pad_sleft_key, keyboard_id}, HID_User::PAD_CIRCLE_LEFT);
KeyMap::SetKeyMapping({Settings::values.pad_sup_key, keyboard_id}, HID_User::PAD_CIRCLE_UP);
KeyMap::SetKeyMapping({Settings::values.pad_sdown_key, keyboard_id}, HID_User::PAD_CIRCLE_DOWN);
}