Skip to content

Commit

Permalink
Merge branch 'wheremyfoodat:master' into ui
Browse files Browse the repository at this point in the history
  • Loading branch information
GabrielBRDeveloper authored Dec 17, 2023
2 parents 3298bd1 + 03292b5 commit b42c7a1
Show file tree
Hide file tree
Showing 13 changed files with 190 additions and 14 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,6 @@
[submodule "third_party/hydra_core"]
path = third_party/hydra_core
url = https://github.com/hydra-emu/core
[submodule "third_party/zep"]
path = third_party/zep
url = https://github.com/Panda3DS-emu/zep
15 changes: 13 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -186,12 +186,20 @@ set(RENDERER_SW_SOURCE_FILES src/core/renderer_sw/renderer_sw.cpp)
# Frontend source files
if(NOT ANDROID)
if(ENABLE_QT_GUI)
set(FRONTEND_SOURCE_FILES src/panda_qt/main.cpp src/panda_qt/screen.cpp src/panda_qt/main_window.cpp src/panda_qt/about_window.cpp src/panda_qt/config_window.cpp)
set(FRONTEND_HEADER_FILES include/panda_qt/screen.hpp include/panda_qt/main_window.hpp include/panda_qt/about_window.hpp include/panda_qt/config_window.hpp)
set(FRONTEND_SOURCE_FILES src/panda_qt/main.cpp src/panda_qt/screen.cpp src/panda_qt/main_window.cpp src/panda_qt/about_window.cpp
src/panda_qt/config_window.cpp src/panda_qt/zep.cpp src/panda_qt/text_editor.cpp
)
set(FRONTEND_HEADER_FILES include/panda_qt/screen.hpp include/panda_qt/main_window.hpp include/panda_qt/about_window.hpp
include/panda_qt/config_window.hpp include/panda_qt/text_editor.hpp
)

source_group("Source Files\\Qt" FILES ${FRONTEND_SOURCE_FILES})
source_group("Header Files\\Qt" FILES ${FRONTEND_HEADER_FILES})
include_directories(${Qt6Gui_PRIVATE_INCLUDE_DIRS})

include_directories(third_party/zep/include) # Include zep for text editor usage
configure_file(third_party/zep/cmake/config_app.h.cmake ${CMAKE_BINARY_DIR}/zep_config/config_app.h)
include_directories(${CMAKE_BINARY_DIR}/zep_config)
else()
set(FRONTEND_SOURCE_FILES src/panda_sdl/main.cpp src/panda_sdl/frontend_sdl.cpp)
set(FRONTEND_HEADER_FILES "")
Expand Down Expand Up @@ -428,6 +436,9 @@ endif()

if(ENABLE_QT_GUI)
target_compile_definitions(Alber PUBLIC "PANDA3DS_FRONTEND_QT=1")
target_compile_definitions(Alber PUBLIC "ZEP_QT=1")
target_compile_definitions(Alber PUBLIC "ZEP_FEATURE_CPP_FILE_SYSTEM=1")

target_link_libraries(Alber PRIVATE Qt6::Widgets)

if(LINUX OR FREEBSD)
Expand Down
2 changes: 2 additions & 0 deletions include/emulator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ class Emulator {
EmulatorConfig& getConfig() { return config; }
Cheats& getCheats() { return cheats; }
ServiceManager& getServiceManager() { return kernel.getServiceManager(); }
LuaManager& getLua() { return lua; }

RendererType getRendererType() const { return config.rendererType; }
Renderer* getRenderer() { return gpu.getRenderer(); }
u64 getTicks() { return cpu.getTicks(); }
Expand Down
5 changes: 5 additions & 0 deletions include/lua_manager.hpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#pragma once
#include <string>

#include "helpers.hpp"
#include "memory.hpp"

Expand Down Expand Up @@ -36,6 +38,8 @@ class LuaManager {
void initialize();
void initializeThunks();
void loadFile(const char* path);
void loadString(const std::string& code);

void reset();
void signalEvent(LuaEvent e) {
if (haveScript) [[unlikely]] {
Expand All @@ -52,6 +56,7 @@ class LuaManager {
void close() {}
void initialize() {}
void loadFile(const char* path) {}
void loadString(const std::string& code) {}
void reset() {}
void signalEvent(LuaEvent e) {}
};
Expand Down
15 changes: 11 additions & 4 deletions include/panda_qt/main_window.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,15 @@
#include "panda_qt/about_window.hpp"
#include "panda_qt/config_window.hpp"
#include "panda_qt/screen.hpp"
#include "panda_qt/text_editor.hpp"
#include "services/hid.hpp"

class MainWindow : public QMainWindow {
Q_OBJECT

private:
// Types of messages we might send from the GUI thread to the emulator thread
enum class MessageType {
LoadROM, Reset, Pause, Resume, TogglePause, DumpRomFS, PressKey, ReleaseKey
};
enum class MessageType { LoadROM, Reset, Pause, Resume, TogglePause, DumpRomFS, PressKey, ReleaseKey, LoadLuaScript };

// Tagged union representing our message queue messages
struct EmulatorMessage {
Expand All @@ -36,27 +35,34 @@ class MainWindow : public QMainWindow {
struct {
u32 key;
} key;

struct {
std::string* str;
} string;
};
};

// This would normally be an std::unique_ptr but it's shared between threads so definitely not
Emulator* emu = nullptr;
std::thread emuThread;

std::atomic<bool> appRunning = true; // Is the application itself running?
std::atomic<bool> appRunning = true; // Is the application itself running?
// Used for synchronizing messages between the emulator and UI
std::mutex messageQueueMutex;
std::vector<EmulatorMessage> messageQueue;

ScreenWidget screen;
AboutWindow* aboutWindow;
ConfigWindow* configWindow;
TextEditorWindow* luaEditor;
QMenuBar* menuBar = nullptr;

void swapEmuBuffer();
void emuThreadMainLoop();
void selectLuaFile();
void selectROM();
void dumpRomFS();
void openLuaEditor();
void showAboutMenu();
void sendMessage(const EmulatorMessage& message);
void dispatchMessage(const EmulatorMessage& message);
Expand All @@ -71,4 +77,5 @@ class MainWindow : public QMainWindow {

void keyPressEvent(QKeyEvent* event) override;
void keyReleaseEvent(QKeyEvent* event) override;
void loadLuaScript(const std::string& code);
};
23 changes: 23 additions & 0 deletions include/panda_qt/text_editor.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#pragma once

#include <QApplication>
#include <QDialog>
#include <QWidget>
#include <string>

#include "zep.h"
#include "zep/mode_repl.h"
#include "zep/regress.h"

class TextEditorWindow : public QDialog {
Q_OBJECT

private:
Zep::ZepWidget_Qt zepWidget;
Zep::IZepReplProvider replProvider;
static constexpr float fontSize = 14.0f;

public:
TextEditorWindow(QWidget* parent, const std::string& filename, const std::string& initialText);
void setText(const std::string& text) { zepWidget.GetEditor().GetMRUBuffer()->SetText(text); }
};
13 changes: 8 additions & 5 deletions src/core/services/apt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include "ipc.hpp"
#include "kernel.hpp"

#include <algorithm>
#include <vector>

namespace APTCommands {
Expand Down Expand Up @@ -311,12 +312,14 @@ void APTService::setApplicationCpuTimeLimit(u32 messagePointer) {
log("APT::SetApplicationCpuTimeLimit (percentage = %d%%)\n", percentage);

if (percentage < 5 || percentage > 89 || fixed != 1) {
Helpers::panic("Invalid parameters passed to APT::SetApplicationCpuTimeLimit");
} else {
mem.write32(messagePointer, IPC::responseHeader(0x4F, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
cpuTimeLimit = percentage;
Helpers::warn("Invalid parameter passed to APT::SetApplicationCpuTimeLimit: (percentage, fixed) = (%d, %d)\n", percentage, fixed);
// TODO: Does the clamp operation happen? Verify with getApplicationCpuTimeLimit on hardware
percentage = std::clamp<u32>(percentage, 5, 89);
}

mem.write32(messagePointer, IPC::responseHeader(0x4F, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
cpuTimeLimit = percentage;
}

void APTService::getApplicationCpuTimeLimit(u32 messagePointer) {
Expand Down
25 changes: 25 additions & 0 deletions src/lua.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,31 @@ void LuaManager::loadFile(const char* path) {
}
}

void LuaManager::loadString(const std::string& code) {
// Initialize Lua if it has not been initialized
if (!initialized) {
initialize();
}

// If init failed, don't execute
if (!initialized) {
printf("Lua initialization failed, file won't run\n");
haveScript = false;

return;
}

int status = luaL_loadstring(L, code.c_str()); // load Lua script
int ret = lua_pcall(L, 0, 0, 0); // tell Lua to run the script

if (ret != 0) {
haveScript = false;
fprintf(stderr, "%s\n", lua_tostring(L, -1)); // tell us what mistake we made
} else {
haveScript = true;
}
}

void LuaManager::signalEventInternal(LuaEvent e) {
lua_getglobal(L, "eventHandler"); // We want to call the event handler
lua_pushnumber(L, static_cast<int>(e)); // Push event type
Expand Down
54 changes: 52 additions & 2 deletions src/panda_qt/main_window.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include <QFileDialog>
#include <cstdio>
#include <fstream>

MainWindow::MainWindow(QApplication* app, QWidget* parent) : QMainWindow(parent), screen(this) {
setWindowTitle("Alber");
Expand All @@ -23,8 +24,10 @@ MainWindow::MainWindow(QApplication* app, QWidget* parent) : QMainWindow(parent)
auto aboutMenu = menuBar->addMenu(tr("About"));

// Create and bind actions for them
auto pandaAction = fileMenu->addAction(tr("Load game"));
connect(pandaAction, &QAction::triggered, this, &MainWindow::selectROM);
auto loadGameAction = fileMenu->addAction(tr("Load game"));
auto loadLuaAction = fileMenu->addAction(tr("Load Lua script"));
connect(loadGameAction, &QAction::triggered, this, &MainWindow::selectROM);
connect(loadLuaAction, &QAction::triggered, this, &MainWindow::selectLuaFile);

auto pauseAction = emulationMenu->addAction(tr("Pause"));
auto resumeAction = emulationMenu->addAction(tr("Resume"));
Expand All @@ -36,14 +39,17 @@ MainWindow::MainWindow(QApplication* app, QWidget* parent) : QMainWindow(parent)
connect(configureAction, &QAction::triggered, this, [this]() { configWindow->show(); });

auto dumpRomFSAction = toolsMenu->addAction(tr("Dump RomFS"));
auto luaEditorAction = toolsMenu->addAction(tr("Open Lua Editor"));
connect(dumpRomFSAction, &QAction::triggered, this, &MainWindow::dumpRomFS);
connect(luaEditorAction, &QAction::triggered, this, &MainWindow::openLuaEditor);

auto aboutAction = aboutMenu->addAction(tr("About Panda3DS"));
connect(aboutAction, &QAction::triggered, this, &MainWindow::showAboutMenu);

// Set up misc objects
aboutWindow = new AboutWindow(nullptr);
configWindow = new ConfigWindow(this);
luaEditor = new TextEditorWindow(this, "script.lua", "");

emu = new Emulator();
emu->setOutputSize(screen.surfaceWidth, screen.surfaceHeight);
Expand Down Expand Up @@ -121,6 +127,34 @@ void MainWindow::selectROM() {
}
}

void MainWindow::selectLuaFile() {
auto path = QFileDialog::getOpenFileName(this, tr("Select Lua script to load"), "", tr("Lua scripts (*.lua *.txt)"));

if (!path.isEmpty()) {
std::ifstream file(std::filesystem::path(path.toStdU16String()), std::ios::in);

if (file.fail()) {
printf("Failed to load selected lua file\n");
return;
}

// Read whole file into an std::string string
// Get file size, preallocate std::string to avoid furthermemory allocations
std::string code;
file.seekg(0, std::ios::end);
code.resize(file.tellg());

// Rewind and read the whole file
file.seekg(0, std::ios::beg);
file.read(&code[0], code.size());
file.close();

loadLuaScript(code);
// Copy the Lua script to the Lua editor
luaEditor->setText(code);
}
}

// Cleanup when the main window closes
MainWindow::~MainWindow() {
appRunning = false; // Set our running atomic to false in order to make the emulator thread stop, and join it
Expand All @@ -133,6 +167,7 @@ MainWindow::~MainWindow() {
delete menuBar;
delete aboutWindow;
delete configWindow;
delete luaEditor;
}

// Send a message to the emulator thread. Lock the mutex and just push back to the vector.
Expand Down Expand Up @@ -181,6 +216,8 @@ void MainWindow::showAboutMenu() {
about.exec();
}

void MainWindow::openLuaEditor() { luaEditor->show(); }

void MainWindow::dispatchMessage(const EmulatorMessage& message) {
switch (message.type) {
case MessageType::LoadROM:
Expand All @@ -189,6 +226,11 @@ void MainWindow::dispatchMessage(const EmulatorMessage& message) {
delete message.path.p;
break;

case MessageType::LoadLuaScript:
emu->getLua().loadString(*message.string.str);
delete message.string.str;
break;

case MessageType::Pause: emu->pause(); break;
case MessageType::Resume: emu->resume(); break;
case MessageType::TogglePause: emu->togglePause(); break;
Expand Down Expand Up @@ -253,3 +295,11 @@ void MainWindow::keyReleaseEvent(QKeyEvent* event) {
case Qt::Key_Backspace: releaseKey(HID::Keys::Select); break;
}
}

void MainWindow::loadLuaScript(const std::string& code) {
EmulatorMessage message{.type = MessageType::LoadLuaScript};

// Make a copy of the code on the heap to send via the message queue
message.string.str = new std::string(code);
sendMessage(message);
}
44 changes: 44 additions & 0 deletions src/panda_qt/text_editor.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#include "panda_qt/text_editor.hpp"

#include <QPushButton>
#include <QVBoxLayout>

#include "panda_qt/main_window.hpp"

using namespace Zep;

TextEditorWindow::TextEditorWindow(QWidget* parent, const std::string& filename, const std::string& initialText)
: QDialog(parent), zepWidget(this, qApp->applicationDirPath().toStdString(), fontSize) {
resize(600, 600);

// Register our extensions
ZepRegressExCommand::Register(zepWidget.GetEditor());
ZepReplExCommand::Register(zepWidget.GetEditor(), &replProvider);

// Default to standard mode instead of vim mode, initialize text box
zepWidget.GetEditor().SetGlobalMode(Zep::ZepMode_Standard::StaticName());
zepWidget.GetEditor().InitWithText(filename, initialText);

// Layout for widgets
QVBoxLayout* mainLayout = new QVBoxLayout();
setLayout(mainLayout);

QPushButton* button = new QPushButton(tr("Load script"), this);
button->setFixedSize(100, 20);

// When the Load Script button is pressed, send the current text to the MainWindow, which will upload it to the emulator's lua object
connect(button, &QPushButton::pressed, this, [this]() {
if (parentWidget()) {
auto buffer = zepWidget.GetEditor().GetMRUBuffer();
const std::string text = buffer->GetBufferText(buffer->Begin(), buffer->End());

static_cast<MainWindow*>(parentWidget())->loadLuaScript(text);
} else {
// This should be unreachable, only here for safety purposes
printf("Text editor does not have any parent widget, click doesn't work :(\n");
}
});

mainLayout->addWidget(button);
mainLayout->addWidget(&zepWidget);
}
2 changes: 2 additions & 0 deletions src/panda_qt/zep.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#define ZEP_SINGLE_HEADER_BUILD
#include "zep.h"
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,4 @@ private Key(String name, T defaultValue) {
this.defaultValue = defaultValue;
}
}
}
}
1 change: 1 addition & 0 deletions third_party/zep
Submodule zep added at 75406e

0 comments on commit b42c7a1

Please sign in to comment.