Panda3DS/include/panda_qt/main_window.hpp
2024-12-10 00:54:06 +02:00

160 lines
3.9 KiB
C++

#pragma once
#include <SDL.h>
#include <QApplication>
#include <QMenuBar>
#include <QtWidgets>
#include <atomic>
#include <filesystem>
#include <functional>
#include <mutex>
#include <thread>
#include <vector>
#include "emulator.hpp"
#include "input_mappings.hpp"
#include "panda_qt/about_window.hpp"
#include "panda_qt/cheats_window.hpp"
#include "panda_qt/config_window.hpp"
#include "panda_qt/patch_window.hpp"
#include "panda_qt/screen.hpp"
#include "panda_qt/shader_editor.hpp"
#include "panda_qt/text_editor.hpp"
#include "services/hid.hpp"
struct CheatMessage {
u32 handle;
std::vector<uint8_t> cheat;
std::function<void(u32)> callback;
};
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,
SetCirclePadX,
SetCirclePadY,
LoadLuaScript,
EditCheat,
PressTouchscreen,
ReleaseTouchscreen,
ReloadUbershader,
SetScreenSize,
UpdateConfig,
};
// Tagged union representing our message queue messages
struct EmulatorMessage {
MessageType type;
union {
struct {
std::filesystem::path* p;
} path;
struct {
u32 key;
} key;
struct {
s16 value;
} circlepad;
struct {
std::string* str;
} string;
struct {
CheatMessage* c;
} cheat;
struct {
u16 x;
u16 y;
} touchscreen;
struct {
u32 width;
u32 height;
} screenSize;
};
};
// 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?
// Used for synchronizing messages between the emulator and UI
std::mutex messageQueueMutex;
std::vector<EmulatorMessage> messageQueue;
QMenuBar* menuBar = nullptr;
InputMappings keyboardMappings;
ScreenWidget* screen;
AboutWindow* aboutWindow;
ConfigWindow* configWindow;
CheatsWindow* cheatsEditor;
TextEditorWindow* luaEditor;
PatchWindow* patchWindow;
ShaderEditorWindow* shaderEditor;
// We use SDL's game controller API since it's the sanest API that supports as many controllers as possible
SDL_GameController* gameController = nullptr;
int gameControllerID = 0;
void swapEmuBuffer();
void emuThreadMainLoop();
void selectLuaFile();
void selectROM();
void dumpDspFirmware();
void dumpRomFS();
void showAboutMenu();
void initControllers();
void pollControllers();
void setupControllerSensors(SDL_GameController* controller);
void sendMessage(const EmulatorMessage& message);
void dispatchMessage(const EmulatorMessage& message);
void loadTranslation();
// Tracks whether we are using an OpenGL-backed renderer or a Vulkan-backed renderer
bool usingGL = false;
bool usingVk = false;
bool usingMtl = false;
// Variables to keep track of whether the user is controlling the 3DS analog stick with their keyboard
// This is done so when a gamepad is connected, we won't automatically override the 3DS analog stick settings with the gamepad's state
// And so the user can still use the keyboard to control the analog
bool keyboardAnalogX = false;
bool keyboardAnalogY = false;
public:
MainWindow(QApplication* app, QWidget* parent = nullptr);
~MainWindow();
void closeEvent(QCloseEvent* event) override;
void keyPressEvent(QKeyEvent* event) override;
void keyReleaseEvent(QKeyEvent* event) override;
void mousePressEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent* event) override;
void mouseMoveEvent(QMouseEvent* event) override;
void loadLuaScript(const std::string& code);
void reloadShader(const std::string& shader);
void editCheat(u32 handle, const std::vector<uint8_t>& cheat, const std::function<void(u32)>& callback);
void handleScreenResize(u32 width, u32 height);
void handleTouchscreenPress(QMouseEvent* event);
};