diff --git a/CMakeLists.txt b/CMakeLists.txt index 3ceaafea7..7818a50e4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,6 +4,7 @@ set(BINARY_NAME gbac CACHE INTERNAL "Name of output binaries") set(CMAKE_C_FLAGS_DEBUG "-g -Wall -Wextra -Wno-error=type-limits --std=gnu99") set(CMAKE_C_FLAGS_RELEASE "-O3 -Wall -Wextra --std=gnu99") set(USE_DEBUGGER ON CACHE BOOL "Whether or not to enable the ARM debugger") +set(BUILD_QT ON CACHE BOOL "Build Qt frontend") set(EXTRA_LIB "") file(GLOB ARM_SRC ${CMAKE_SOURCE_DIR}/src/arm/*.c) file(GLOB GBA_SRC ${CMAKE_SOURCE_DIR}/src/gba/*.c) @@ -67,3 +68,7 @@ target_link_libraries(${BINARY_NAME} m ${DEBUGGER_LIB} ${PLATFORM_LIBRARY} ${OPE add_executable(${BINARY_NAME}-bin WIN32 ${MAIN_SRC}) target_link_libraries(${BINARY_NAME}-bin ${BINARY_NAME}) set_target_properties(${BINARY_NAME}-bin PROPERTIES OUTPUT_NAME ${BINARY_NAME}) + +if(BUILD_QT) + add_subdirectory(src/platform/qt) +endif() diff --git a/src/platform/qt/CMakeLists.txt b/src/platform/qt/CMakeLists.txt new file mode 100644 index 000000000..d991aeebc --- /dev/null +++ b/src/platform/qt/CMakeLists.txt @@ -0,0 +1,24 @@ +cmake_minimum_required(VERSION 2.8.8) +project(QGBAc) + +set(CMAKE_CXX_FLAGS_DEBUG "-g -Wall -Wextra --std=c++11") +set(CMAKE_CXX_FLAGS_RELEASE "-O3 -Wall -Wextra --std=c++11") + +if(APPLE) + set(CMAKE_PREFIX_PATH "/usr/local/opt/qt5") # Temporary stopgap measure +endif() + +set(CMAKE_AUTOMOC ON) +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +find_package(Qt5Widgets REQUIRED) + +set(UI_FILES Window.ui) +set(SOURCE_FILES Display.cpp GameController.cpp Window.cpp) + +qt5_wrap_ui(UI_HEADERS ${UI_FILES}) + +add_executable(QGBAc WIN32 MACOSX_BUNDLE ${UI_FILES} ${UI_HEADERS} main.cpp ${SOURCE_FILES}) + +qt5_use_modules(QGBAc Widgets OpenGL) +target_link_libraries(QGBAc ${OPENGL_LIBRARY} ${BINARY_NAME}) diff --git a/src/platform/qt/Display.cpp b/src/platform/qt/Display.cpp new file mode 100644 index 000000000..deeb4a9cd --- /dev/null +++ b/src/platform/qt/Display.cpp @@ -0,0 +1,56 @@ +#include "Display.h" + +#include + +using namespace QGBA; + +static const GLint _glVertices[] = { + 0, 0, + 256, 0, + 256, 256, + 0, 256 +}; + +static const GLint _glTexCoords[] = { + 0, 0, + 1, 0, + 1, 1, + 0, 1 +}; + +Display::Display(QWidget* parent) : QGLWidget(QGLFormat(QGL::Rgba | QGL::DoubleBuffer), parent) { + setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored); + QTimer* timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), this, SLOT(updateGL())); + timer->setInterval(0); + timer->start(); +} + +void Display::initializeGL() { + glEnable(GL_TEXTURE_2D); + glGenTextures(1, &m_tex); + glBindTexture(GL_TEXTURE_2D, m_tex); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); +} + +void Display::draw(const QImage& image) { + makeCurrent(); + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, m_tex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, image.constBits()); +} + +void Display::paintGL() { + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(2, GL_INT, 0, _glVertices); + glTexCoordPointer(2, GL_INT, 0, _glTexCoords); + glMatrixMode (GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, 240, 160, 0, 0, 1); + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, m_tex); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); +} diff --git a/src/platform/qt/Display.h b/src/platform/qt/Display.h new file mode 100644 index 000000000..1dce61075 --- /dev/null +++ b/src/platform/qt/Display.h @@ -0,0 +1,27 @@ +#ifndef QGBA_DISPLAY +#define QGBA_DISPLAY + +#include + +namespace QGBA { + +class Display : public QGLWidget { +Q_OBJECT + +public: + Display(QWidget* parent = 0); + +protected: + virtual void initializeGL(); + +public slots: + void draw(const QImage& image); + void paintGL(); + +private: + GLuint m_tex; +}; + +} + +#endif diff --git a/src/platform/qt/GameController.cpp b/src/platform/qt/GameController.cpp new file mode 100644 index 000000000..67fd0a452 --- /dev/null +++ b/src/platform/qt/GameController.cpp @@ -0,0 +1,48 @@ +#include "GameController.h" + +extern "C" { +#include "renderers/video-software.h" +} + +using namespace QGBA; + +GameController::GameController(QObject* parent) + : QObject(parent) + , m_drawContext(256, 256, QImage::Format_RGB32) +{ + m_renderer = new GBAVideoSoftwareRenderer; + GBAVideoSoftwareRendererCreate(m_renderer); + m_renderer->outputBuffer = (color_t*) m_drawContext.bits(); + m_renderer->outputBufferStride = m_drawContext.bytesPerLine() / 4; + m_threadContext.useDebugger = 0; + m_threadContext.frameskip = 0; + m_threadContext.renderer = &m_renderer->d; + m_threadContext.frameskip = 0; + m_threadContext.sync.videoFrameWait = 0; + m_threadContext.sync.audioWait = 0; + m_threadContext.startCallback = 0; + m_threadContext.cleanCallback = 0; + m_threadContext.frameCallback = [] (GBAThread* context) { + GameController* controller = (GameController*) context->userData; + controller->frameAvailable(controller->m_drawContext); + }; + m_threadContext.userData = this; + m_threadContext.rewindBufferCapacity = 0; +} + +GameController::~GameController() { + delete m_renderer; +} + +bool GameController::loadGame(const QString& path) { + m_rom = new QFile(path); + if (!m_rom->open(QIODevice::ReadOnly)) { + delete m_rom; + m_rom = 0; + return false; + } + m_threadContext.fd = m_rom->handle(); + m_threadContext.fname = path.toLocal8Bit().constData(); + GBAThreadStart(&m_threadContext); + return true; +} diff --git a/src/platform/qt/GameController.h b/src/platform/qt/GameController.h new file mode 100644 index 000000000..c3e8b525c --- /dev/null +++ b/src/platform/qt/GameController.h @@ -0,0 +1,41 @@ +#ifndef QGBA_GAME_CONTROLLER +#define QGBA_GAME_CONTROLLER + +#include +#include +#include +#include + +extern "C" { +#include "gba-thread.h" +} + +struct GBAVideoSoftwareRenderer; + +namespace QGBA { + +class GameController : public QObject { +Q_OBJECT + +public: + GameController(QObject* parent = 0); + ~GameController(); + +signals: + void frameAvailable(const QImage&); + +public slots: + bool loadGame(const QString& path); + +private: + QImage m_drawContext; + GBAThread m_threadContext; + GBAVideoSoftwareRenderer* m_renderer; + + QFile* m_rom; + QFile* m_bios; +}; + +} + +#endif diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp new file mode 100644 index 000000000..cd215d4e0 --- /dev/null +++ b/src/platform/qt/Window.cpp @@ -0,0 +1,23 @@ +#include "Window.h" + +#include + +using namespace QGBA; + +Window::Window(QWidget* parent) : QMainWindow(parent) { + setupUi(this); + + m_controller = new GameController(this); + m_display = new Display(this); + setCentralWidget(m_display); + connect(m_controller, SIGNAL(frameAvailable(const QImage&)), m_display, SLOT(draw(const QImage&))); + + connect(actionOpen, SIGNAL(triggered()), this, SLOT(selectROM())); +} + +void Window::selectROM() { + QString filename = QFileDialog::getOpenFileName(this, tr("Select ROM")); + if (!filename.isEmpty()) { + m_controller->loadGame(filename); + } +} diff --git a/src/platform/qt/Window.h b/src/platform/qt/Window.h new file mode 100644 index 000000000..bbed0b405 --- /dev/null +++ b/src/platform/qt/Window.h @@ -0,0 +1,29 @@ +#ifndef QGBA_WINDOW +#define QGBA_WINDOW + +#include + +#include "GameController.h" +#include "Display.h" + +#include "ui_Window.h" + +namespace QGBA { + +class Window : public QMainWindow, Ui::GBAWindow { +Q_OBJECT + +public: + Window(QWidget* parent = 0); + +public slots: + void selectROM(); + +private: + GameController* m_controller; + Display* m_display; +}; + +} + +#endif diff --git a/src/platform/qt/Window.ui b/src/platform/qt/Window.ui new file mode 100644 index 000000000..f25f2e996 --- /dev/null +++ b/src/platform/qt/Window.ui @@ -0,0 +1,73 @@ + + + GBAWindow + + + + 0 + 0 + 240 + 160 + + + + + 0 + 0 + + + + + 240 + 160 + + + + GBA + + + + + 0 + 0 + + + + + 240 + 160 + + + + + + + 0 + 0 + 240 + 22 + + + + false + + + + File + + + + + + + + Open + + + Ctrl+O + + + + + + diff --git a/src/platform/qt/main.cpp b/src/platform/qt/main.cpp new file mode 100644 index 000000000..226d74fce --- /dev/null +++ b/src/platform/qt/main.cpp @@ -0,0 +1,10 @@ +#include +#include "Window.h" + +int main(int argc, char* argv[]) { + QApplication application(argc, argv); + QGBA::Window window; + window.show(); + + return application.exec(); +}