Files
Xenia-Canary/src/xenia/app/emulator_window.h
NicknineTheEagle c4e930ed4c Proper 4:3 support
2024-07-01 08:55:15 +02:00

273 lines
8.5 KiB
C++

/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2022 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_APP_EMULATOR_WINDOW_H_
#define XENIA_APP_EMULATOR_WINDOW_H_
#include <memory>
#include <string>
#include "xenia/emulator.h"
#include "xenia/gpu/command_processor.h"
#include "xenia/ui/imgui_dialog.h"
#include "xenia/ui/imgui_drawer.h"
#include "xenia/ui/immediate_drawer.h"
#include "xenia/ui/menu_item.h"
#include "xenia/ui/presenter.h"
#include "xenia/ui/window.h"
#include "xenia/ui/window_listener.h"
#include "xenia/ui/windowed_app_context.h"
#include "xenia/xbox.h"
#define MAX_USERS 4
namespace xe {
namespace app {
struct RecentTitleEntry {
std::string title_name;
std::filesystem::path path_to_file;
std::time_t last_run_time;
};
class EmulatorWindow {
public:
using steady_clock = std::chrono::steady_clock; // stdlib steady clock
enum : size_t {
// The UI is on top of the game and is open in special cases, so
// lowest-priority.
kZOrderHidInput,
kZOrderImGui,
kZOrderProfiler,
// Emulator window controls are expected to be always accessible by the
// user, so highest-priority.
kZOrderEmulatorWindowInput,
};
virtual ~EmulatorWindow();
static std::unique_ptr<EmulatorWindow> Create(
Emulator* emulator, ui::WindowedAppContext& app_context, uint32_t width,
uint32_t height);
std::unique_ptr<xe::threading::Thread> Gamepad_HotKeys_Listener;
int32_t selected_title_index = -1;
static constexpr int64_t diff_in_ms(
const steady_clock::time_point t1,
const steady_clock::time_point t2) noexcept {
using ms = std::chrono::milliseconds;
return std::chrono::duration_cast<ms>(t1 - t2).count();
}
steady_clock::time_point last_mouse_up = steady_clock::now();
steady_clock::time_point last_mouse_down = steady_clock::now();
Emulator* emulator() const { return emulator_; }
ui::WindowedAppContext& app_context() const { return app_context_; }
ui::Window* window() const { return window_.get(); }
ui::ImGuiDrawer* imgui_drawer() const { return imgui_drawer_.get(); }
ui::Presenter* GetGraphicsSystemPresenter() const;
void SetupGraphicsSystemPresenterPainting();
void ShutdownGraphicsSystemPresenterPainting();
void OnEmulatorInitialized();
xe::X_STATUS RunTitle(std::filesystem::path path_to_file);
void UpdateTitle();
void SetFullscreen(bool fullscreen);
void ToggleFullscreen();
void SetInitializingShaderStorage(bool initializing);
void TakeScreenshot();
void ExportScreenshot(const xe::ui::RawImage& image);
void SaveImage(const std::filesystem::path& path,
const xe::ui::RawImage& image);
// Types of button functions for hotkeys.
enum class ButtonFunctions {
ToggleFullscreen,
RunTitle,
CpuTimeScalarSetHalf,
CpuTimeScalarSetDouble,
CpuTimeScalarReset,
ClearGPUCache,
ToggleControllerVibration,
ClearMemoryPageState,
ReadbackResolve,
ToggleLogging,
IncTitleSelect,
DecTitleSelect,
Unknown
};
enum class gpu_cvar {
ClearMemoryPageState,
ReadbackResolve,
};
class ControllerHotKey {
public:
// If true the hotkey can be activated while a title is running, otherwise
// false.
bool title_passthru;
// If true vibrate the controller after activating the hotkey, otherwise
// false.
bool rumble;
std::string pretty;
ButtonFunctions function;
ControllerHotKey(ButtonFunctions fn = ButtonFunctions::Unknown,
std::string pretty = "", bool rumble = false,
bool active = true) {
function = fn;
this->pretty = pretty;
title_passthru = active;
this->rumble = rumble;
}
};
private:
class EmulatorWindowListener final : public ui::WindowListener,
public ui::WindowInputListener {
public:
explicit EmulatorWindowListener(EmulatorWindow& emulator_window)
: emulator_window_(emulator_window) {}
void OnClosing(ui::UIEvent& e) override;
void OnFileDrop(ui::FileDropEvent& e) override;
void OnKeyDown(ui::KeyEvent& e) override;
void OnMouseDown(ui::MouseEvent& e) override;
void OnMouseUp(ui::MouseEvent& e) override;
private:
EmulatorWindow& emulator_window_;
};
class DisplayConfigGameConfigLoadCallback
: public Emulator::GameConfigLoadCallback {
public:
DisplayConfigGameConfigLoadCallback(Emulator& emulator,
EmulatorWindow& emulator_window)
: Emulator::GameConfigLoadCallback(emulator),
emulator_window_(emulator_window) {}
void PostGameConfigLoad() override;
private:
EmulatorWindow& emulator_window_;
};
class DisplayConfigDialog final : public ui::ImGuiDialog {
public:
DisplayConfigDialog(ui::ImGuiDrawer* imgui_drawer,
EmulatorWindow& emulator_window)
: ui::ImGuiDialog(imgui_drawer), emulator_window_(emulator_window) {}
protected:
void OnDraw(ImGuiIO& io) override;
private:
EmulatorWindow& emulator_window_;
};
explicit EmulatorWindow(Emulator* emulator,
ui::WindowedAppContext& app_context, uint32_t width,
uint32_t height);
bool Initialize();
// For comparisons, use GetSwapPostEffectForCvarValue instead as the default
// fallback may be used for multiple values.
static const char* GetCvarValueForSwapPostEffect(
gpu::CommandProcessor::SwapPostEffect effect);
static gpu::CommandProcessor::SwapPostEffect GetSwapPostEffectForCvarValue(
const std::string& cvar_value);
// For comparisons, use GetGuestOutputPaintEffectForCvarValue instead as the
// default fallback may be used for multiple values.
static const char* GetCvarValueForGuestOutputPaintEffect(
ui::Presenter::GuestOutputPaintConfig::Effect effect);
static ui::Presenter::GuestOutputPaintConfig::Effect
GetGuestOutputPaintEffectForCvarValue(const std::string& cvar_value);
static ui::Presenter::GuestOutputPaintConfig
GetGuestOutputPaintConfigForCvars();
void ApplyDisplayConfigForCvars();
void OnKeyDown(ui::KeyEvent& e);
void OnMouseDown(const ui::MouseEvent& e);
void ToggleFullscreenOnDoubleClick();
void FileDrop(const std::filesystem::path& filename);
void OnMouseUp(const ui::MouseEvent& e);
void FileOpen();
void FileClose();
void InstallContent();
void ExtractZarchive();
void CreateZarchive();
void ShowContentDirectory();
void CpuTimeScalarReset();
void CpuTimeScalarSetHalf();
void CpuTimeScalarSetDouble();
void CpuBreakIntoDebugger();
void CpuBreakIntoHostDebugger();
void GpuTraceFrame();
void GpuClearCaches();
void ToggleDisplayConfigDialog();
void ToggleControllerVibration();
void ShowCompatibility();
void ShowFAQ();
void ShowBuildCommit();
EmulatorWindow::ControllerHotKey ProcessControllerHotkey(int buttons);
void VibrateController(xe::hid::InputSystem* input_sys, uint32_t user_index,
bool vibrate = true);
void GamepadHotKeys();
void ToggleGPUSetting(gpu_cvar index);
bool IsUseNexusForGameBarEnabled();
void DisplayHotKeysConfig();
static std::string CanonicalizeFileExtension(
const std::filesystem::path& path);
void RunPreviouslyPlayedTitle();
void FillRecentlyLaunchedTitlesMenu(xe::ui::MenuItem* recent_menu);
void LoadRecentlyLaunchedTitles();
void AddRecentlyLaunchedTitle(std::filesystem::path path_to_file,
std::string title_name);
Emulator* emulator_;
ui::WindowedAppContext& app_context_;
EmulatorWindowListener window_listener_;
std::unique_ptr<ui::Window> window_;
std::unique_ptr<ui::ImGuiDrawer> imgui_drawer_;
std::unique_ptr<DisplayConfigGameConfigLoadCallback>
display_config_game_config_load_callback_;
// Creation may fail, in this case immediate drawer UI must not be drawn.
std::unique_ptr<ui::ImmediateDrawer> immediate_drawer_;
bool emulator_initialized_ = false;
std::string base_title_;
bool initializing_shader_storage_ = false;
std::unique_ptr<DisplayConfigDialog> display_config_dialog_;
std::vector<RecentTitleEntry> recently_launched_titles_;
};
} // namespace app
} // namespace xe
#endif // XENIA_APP_EMULATOR_WINDOW_H_