code:
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <array>
#include <atomic>
#include <vector>
#include <QByteArray>
#include <QMetaType>
#include <QString>
#include <QStringList>
#include <QVector>
#include "common/common_types.h"
#include "common/settings.h"
#include "common/settings_enums.h"
#include "configuration/qt_config.h"

using Settings::Category;
using Settings::ConfirmStop;
using Settings::Setting;
using Settings::SwitchableSetting;

#ifndef CANNOT_EXPLICITLY_INSTANTIATE
namespace Settings {
extern template class Setting<bool>;
extern template class Setting<std::string>;
extern template class Setting<u16, true>;
extern template class Setting<u32>;
extern template class Setting<u8, true>;
extern template class Setting<u8>;
extern template class Setting<unsigned long long>;
} // namespace Settings
#endif

namespace UISettings {

bool IsDarkTheme();

struct ContextualShortcut {
    std::string keyseq;
    std::string controller_keyseq;
    int context;
    bool repeat;
};

struct Shortcut {
    std::string name;
    std::string group;
    ContextualShortcut shortcut;
};

enum class Theme {
    Default,
    DefaultColorful,
    Dark,
    DarkColorful,
    MidnightBlue,
    MidnightBlueColorful,
};

static constexpr Theme default_theme{
#ifdef _WIN32
    Theme::DarkColorful
#else
    Theme::DefaultColorful
#endif
};

using Themes = std::array<std::pair<const char*, const char*>, 6>;
extern const Themes themes;

struct GameDir {
    std::string path;
    bool deep_scan = false;
    bool expanded = false;
    bool operator==(const GameDir& rhs) const {
        return path == rhs.path;
    }
    bool operator!=(const GameDir& rhs) const {
        return !operator==(rhs);
    }
};

struct Values {
    Settings::Linkage linkage{1000};

    QByteArray geometry;
    QByteArray state;

    QByteArray renderwindow_geometry;

    QByteArray gamelist_header_state;

    QByteArray microprofile_geometry;
    Setting<bool> microprofile_visible{linkage, false, "microProfileDialogVisible",
                                       Category::UiLayout};

    Setting<bool> single_window_mode{linkage, true, "singleWindowMode", Category::Ui};
    Setting<bool> fullscreen{linkage, false, "fullscreen", Category::Ui};
    Setting<bool> display_titlebar{linkage, true, "displayTitleBars", Category::Ui};
    Setting<bool> show_filter_bar{linkage, true, "showFilterBar", Category::Ui};
    Setting<bool> show_status_bar{linkage, true, "showStatusBar", Category::Ui};

    SwitchableSetting<ConfirmStop> confirm_before_stopping{linkage,
                                                           ConfirmStop::Ask_Always,
                                                           "confirmStop",
                                                           Category::UiGeneral,
                                                           Settings::Specialization::Default,
                                                           true,
                                                           true};

    Setting<bool> first_start{linkage, true, "firstStart", Category::Ui};
    Setting<bool> pause_when_in_background{linkage,
                                           false,
                                           "pauseWhenInBackground",
                                           Category::UiGeneral,
                                           Settings::Specialization::Default,
                                           true,
                                           true};
    Setting<bool> mute_when_in_background{linkage,
                                          false,
                                          "muteWhenInBackground",
                                          Category::UiAudio,
                                          Settings::Specialization::Default,
                                          true,
                                          true};
    Setting<bool> hide_mouse{
        linkage, true, "hideInactiveMouse", Category::UiGeneral, Settings::Specialization::Default,
        true,    true};
    Setting<bool> controller_applet_disabled{linkage, false, "disableControllerApplet",
                                             Category::UiGeneral};
    // Set when Vulkan is known to crash the application
    bool has_broken_vulkan = false;

    Setting<bool> select_user_on_boot{linkage,
                                      false,
                                      "select_user_on_boot",
                                      Category::UiGeneral,
                                      Settings::Specialization::Default,
                                      true,
                                      true};
    Setting<bool> disable_web_applet{linkage, true, "disable_web_applet", Category::Ui};

    // Discord RPC
    Setting<bool> enable_discord_presence{linkage, true, "enable_discord_presence", Category::Ui};

    // logging
    Setting<bool> show_console{linkage, false, "showConsole", Category::Ui};

    // Screenshots
    Setting<bool> enable_screenshot_save_as{linkage, true, "enable_screenshot_save_as",
                                            Category::Screenshots};
    Setting<u32> screenshot_height{linkage, 0, "screenshot_height", Category::Screenshots};

    std::string roms_path;
    std::string game_dir_deprecated;
    bool game_dir_deprecated_deepscan;
    QVector<GameDir> game_dirs;
    QStringList recent_files;
    Setting<std::string> language{linkage, {}, "language", Category::Paths};

    std::string theme;

    // Shortcut name <Shortcut, context>
    std::vector<Shortcut> shortcuts;

    Setting<u32> callout_flags{linkage, 0, "calloutFlags", Category::Ui};

    // multiplayer settings
    Setting<std::string> multiplayer_nickname{linkage, {}, "nickname", Category::Multiplayer};
    Setting<std::string> multiplayer_filter_text{linkage, {}, "filter_text", Category::Multiplayer};
    Setting<bool> multiplayer_filter_games_owned{linkage, false, "filter_games_owned",
                                                 Category::Multiplayer};
    Setting<bool> multiplayer_filter_hide_empty{linkage, false, "filter_games_hide_empty",
                                                Category::Multiplayer};
    Setting<bool> multiplayer_filter_hide_full{linkage, false, "filter_games_hide_full",
                                               Category::Multiplayer};
    Setting<std::string> multiplayer_ip{linkage, {}, "ip", Category::Multiplayer};
    Setting<u16, true> multiplayer_port{linkage,    24872,  0,
                                        UINT16_MAX, "port", Category::Multiplayer};
    Setting<std::string> multiplayer_room_nickname{
        linkage, {}, "room_nickname", Category::Multiplayer};
    Setting<std::string> multiplayer_room_name{linkage, {}, "room_name", Category::Multiplayer};
    Setting<u8, true> multiplayer_max_player{linkage, 8, 0, 8, "max_player", Category::Multiplayer};
    Setting<u16, true> multiplayer_room_port{linkage,    24872,       0,
                                             UINT16_MAX, "room_port", Category::Multiplayer};
    Setting<u8, true> multiplayer_host_type{linkage, 0, 0, 1, "host_type", Category::Multiplayer};
    Setting<unsigned long long> multiplayer_game_id{linkage, {}, "game_id", Category::Multiplayer};
    Setting<std::string> multiplayer_room_description{
        linkage, {}, "room_description", Category::Multiplayer};
    std::pair<std::vector<std::string>, std::vector<std::string>> multiplayer_ban_list;

    // Game List
    Setting<bool> show_add_ons{linkage, true, "show_add_ons", Category::UiGameList};
    Setting<u32> game_icon_size{linkage, 64, "game_icon_size", Category::UiGameList};
    Setting<u32> folder_icon_size{linkage, 48, "folder_icon_size", Category::UiGameList};
    Setting<u8> row_1_text_id{linkage, 3, "row_1_text_id", Category::UiGameList};
    Setting<u8> row_2_text_id{linkage, 2, "row_2_text_id", Category::UiGameList};
    std::atomic_bool is_game_list_reload_pending{false};
    Setting<bool> cache_game_list{linkage, true, "cache_game_list", Category::UiGameList};
    Setting<bool> favorites_expanded{linkage, true, "favorites_expanded", Category::UiGameList};
    QVector<u64> favorited_ids;

    // Compatibility List
    Setting<bool> show_compat{linkage, false, "show_compat", Category::UiGameList};

    // Size & File Types Column
    Setting<bool> show_size{linkage, true, "show_size", Category::UiGameList};
    Setting<bool> show_types{linkage, true, "show_types", Category::UiGameList};

    // Play time
    Setting<bool> show_play_time{linkage, true, "show_play_time", Category::UiGameList};

    bool configuration_applied;
    bool reset_to_defaults;
    bool shortcut_already_warned{false};
};

extern Values values;

u32 CalculateWidth(u32 height, Settings::AspectRatio ratio);

void SaveWindowState();
void RestoreWindowState(std::unique_ptr<QtConfig>& qtConfig);

// This shouldn't have anything except static initializers (no functions). So
// QKeySequence(...).toString() is NOT ALLOWED HERE.
// This must be in alphabetical order according to action name as it must have the same order as
// UISetting::values.shortcuts, which is alphabetically ordered.
// clang-format off
const std::array<Shortcut, 28> default_hotkeys{{
    {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Mute/Unmute")).toStdString(),        QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+M"),  std::string("Home+Dpad_Right"), Qt::WindowShortcut, false}},
    {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Down")).toStdString(),        QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("-"),       std::string("Home+Dpad_Down"), Qt::ApplicationShortcut, true}},
    {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Up")).toStdString(),          QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("="),       std::string("Home+Dpad_Up"), Qt::ApplicationShortcut, true}},
    {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Capture Screenshot")).toStdString(),       QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+P"),  std::string("Screenshot"), Qt::WidgetWithChildrenShortcut, false}},
    {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Adapting Filter")).toStdString(),   QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F8"),      std::string("Home+L"), Qt::ApplicationShortcut, false}},
    {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Docked Mode")).toStdString(),       QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F10"),     std::string("Home+X"), Qt::ApplicationShortcut, false}},
    {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change GPU Accuracy")).toStdString(),      QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F9"),      std::string("Home+R"), Qt::ApplicationShortcut, false}},
    {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Continue/Pause Emulation")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F4"),      std::string("Home+Plus"), Qt::WindowShortcut, false}},
    {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit Fullscreen")).toStdString(),          QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Esc"),     std::string(""), Qt::WindowShortcut, false}},
    {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit yuzu")).toStdString(),                QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+Q"),  std::string("Home+Minus"), Qt::WindowShortcut, false}},
    {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Fullscreen")).toStdString(),               QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F11"),     std::string("Home+B"), Qt::WindowShortcut, false}},
    {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load File")).toStdString(),                QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+O"),  std::string(""), Qt::WidgetWithChildrenShortcut, false}},
    {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load/Remove Amiibo")).toStdString(),       QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F2"),      std::string("Home+A"), Qt::WidgetWithChildrenShortcut, false}},
    {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Multiplayer Browse Public Game Lobby")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+B"),  std::string(""), Qt::ApplicationShortcut, false}},
    {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Multiplayer Create Room")).toStdString(),  QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+N"),  std::string(""), Qt::ApplicationShortcut, false}},
    {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Multiplayer Direct Connect to Room")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+C"),  std::string(""), Qt::ApplicationShortcut, false}},
    {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Multiplayer Leave Room")).toStdString(),   QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+L"),  std::string(""), Qt::ApplicationShortcut, false}},
    {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Multiplayer Show Current Room")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+R"),  std::string(""), Qt::ApplicationShortcut, false}},
    {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Restart Emulation")).toStdString(),        QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F6"),      std::string("R+Plus+Minus"), Qt::WindowShortcut, false}},
    {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Stop Emulation")).toStdString(),           QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F5"),      std::string("L+Plus+Minus"), Qt::WindowShortcut, false}},
    {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Record")).toStdString(),               QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F7"), std::string(""), Qt::ApplicationShortcut, false}},
    {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Reset")).toStdString(),                QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F6"), std::string(""), Qt::ApplicationShortcut, false}},
    {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Start/Stop")).toStdString(),           QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F5"), std::string(""), Qt::ApplicationShortcut, false}},
    {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Filter Bar")).toStdString(),        QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F"),  std::string(""), Qt::WindowShortcut, false}},
    {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Framerate Limit")).toStdString(),   QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+U"),  std::string("Home+Y"), Qt::ApplicationShortcut, false}},
    {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Mouse Panning")).toStdString(),     QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F9"), std::string(""), Qt::ApplicationShortcut, false}},
    {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Renderdoc Capture")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string(""),        std::string(""), Qt::ApplicationShortcut, false}},
    {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Status Bar")).toStdString(),        QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+S"),  std::string(""), Qt::WindowShortcut, false}},
}};
// clang-format on

} // namespace UISettings

Q_DECLARE_METATYPE(UISettings::GameDir*);

// These metatype declarations cannot be in common/settings.h because core is devoid of QT
Q_DECLARE_METATYPE(Settings::CpuAccuracy);
Q_DECLARE_METATYPE(Settings::GpuAccuracy);
Q_DECLARE_METATYPE(Settings::FullscreenMode);
Q_DECLARE_METATYPE(Settings::NvdecEmulation);
Q_DECLARE_METATYPE(Settings::ResolutionSetup);
Q_DECLARE_METATYPE(Settings::ScalingFilter);
Q_DECLARE_METATYPE(Settings::AntiAliasing);
Q_DECLARE_METATYPE(Settings::RendererBackend);
Q_DECLARE_METATYPE(Settings::ShaderBackend);
Q_DECLARE_METATYPE(Settings::AstcRecompression);
Q_DECLARE_METATYPE(Settings::AstcDecodeMode);


code:
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "yuzu/util/clickable_label.h"

ClickableLabel::ClickableLabel(QWidget* parent, [[maybe_unused]] Qt::WindowFlags f)
    : QLabel(parent) {}

void ClickableLabel::mouseReleaseEvent([[maybe_unused]] QMouseEvent* event) {
    emit clicked();
}

repo_name: litucks/torzu

license: mit


code:
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <QLabel>
#include <QWidget>

class ClickableLabel : public QLabel {
    Q_OBJECT

public:
    explicit ClickableLabel(QWidget* parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags());
    ~ClickableLabel() = default;

signals:
    void clicked();

protected:
    void mouseReleaseEvent(QMouseEvent* event);
};


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "common/settings_input.h"
#include "hid_core/frontend/emulated_controller.h"
#include "hid_core/hid_core.h"
#include "yuzu/util/controller_navigation.h"

ControllerNavigation::ControllerNavigation(Core::HID::HIDCore& hid_core, QWidget* parent) {
    player1_controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
    handheld_controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
    Core::HID::ControllerUpdateCallback engine_callback{
        .on_change = [this](Core::HID::ControllerTriggerType type) { ControllerUpdateEvent(type); },
        .is_npad_service = false,
    };
    player1_callback_key = player1_controller->SetCallback(engine_callback);
    handheld_callback_key = handheld_controller->SetCallback(engine_callback);
    is_controller_set = true;
}

ControllerNavigation::~ControllerNavigation() {
    UnloadController();
}

void ControllerNavigation::UnloadController() {
    if (is_controller_set) {
        player1_controller->DeleteCallback(player1_callback_key);
        handheld_controller->DeleteCallback(handheld_callback_key);
        is_controller_set = false;
    }
}

void ControllerNavigation::TriggerButton(Settings::NativeButton::Values native_button,
                                         Qt::Key key) {
    if (button_values[native_button].value && !button_values[native_button].locked) {
        emit TriggerKeyboardEvent(key);
    }
}

void ControllerNavigation::ControllerUpdateEvent(Core::HID::ControllerTriggerType type) {
    std::scoped_lock lock{mutex};
    if (!Settings::values.controller_navigation) {
        return;
    }
    if (type == Core::HID::ControllerTriggerType::Button) {
        ControllerUpdateButton();
        return;
    }

    if (type == Core::HID::ControllerTriggerType::Stick) {
        ControllerUpdateStick();
        return;
    }
}

void ControllerNavigation::ControllerUpdateButton() {
    const auto controller_type = player1_controller->GetNpadStyleIndex();
    const auto& player1_buttons = player1_controller->GetButtonsValues();
    const auto& handheld_buttons = handheld_controller->GetButtonsValues();

    for (std::size_t i = 0; i < player1_buttons.size(); ++i) {
        const bool button = player1_buttons[i].value || handheld_buttons[i].value;
        // Trigger only once
        button_values[i].locked = button == button_values[i].value;
        button_values[i].value = button;
    }

    switch (controller_type) {
    case Core::HID::NpadStyleIndex::Fullkey:
    case Core::HID::NpadStyleIndex::JoyconDual:
    case Core::HID::NpadStyleIndex::Handheld:
    case Core::HID::NpadStyleIndex::GameCube:
        TriggerButton(Settings::NativeButton::A, Qt::Key_Enter);
        TriggerButton(Settings::NativeButton::B, Qt::Key_Escape);
        TriggerButton(Settings::NativeButton::DDown, Qt::Key_Down);
        TriggerButton(Settings::NativeButton::DLeft, Qt::Key_Left);
        TriggerButton(Settings::NativeButton::DRight, Qt::Key_Right);
        TriggerButton(Settings::NativeButton::DUp, Qt::Key_Up);
        break;
    case Core::HID::NpadStyleIndex::JoyconLeft:
        TriggerButton(Settings::NativeButton::DDown, Qt::Key_Enter);
        TriggerButton(Settings::NativeButton::DLeft, Qt::Key_Escape);
        break;
    case Core::HID::NpadStyleIndex::JoyconRight:
        TriggerButton(Settings::NativeButton::X, Qt::Key_Enter);
        TriggerButton(Settings::NativeButton::A, Qt::Key_Escape);
        break;
    default:
        break;
    }
}

void ControllerNavigation::ControllerUpdateStick() {
    const auto controller_type = player1_controller->GetNpadStyleIndex();
    const auto& player1_sticks = player1_controller->GetSticksValues();
    const auto& handheld_sticks = player1_controller->GetSticksValues();
    bool update = false;

    for (std::size_t i = 0; i < player1_sticks.size(); ++i) {
        const Common::Input::StickStatus stick{
            .left = player1_sticks[i].left || handheld_sticks[i].left,
            .right = player1_sticks[i].right || handheld_sticks[i].right,
            .up = player1_sticks[i].up || handheld_sticks[i].up,
            .down = player1_sticks[i].down || handheld_sticks[i].down,
        };
        // Trigger only once
        if (stick.down != stick_values[i].down || stick.left != stick_values[i].left ||
            stick.right != stick_values[i].right || stick.up != stick_values[i].up) {
            update = true;
        }
        stick_values[i] = stick;
    }

    if (!update) {
        return;
    }

    switch (controller_type) {
    case Core::HID::NpadStyleIndex::Fullkey:
    case Core::HID::NpadStyleIndex::JoyconDual:
    case Core::HID::NpadStyleIndex::Handheld:
    case Core::HID::NpadStyleIndex::GameCube:
        if (stick_values[Settings::NativeAnalog::LStick].down) {
            emit TriggerKeyboardEvent(Qt::Key_Down);
            return;
        }
        if (stick_values[Settings::NativeAnalog::LStick].left) {
            emit TriggerKeyboardEvent(Qt::Key_Left);
            return;
        }
        if (stick_values[Settings::NativeAnalog::LStick].right) {
            emit TriggerKeyboardEvent(Qt::Key_Right);
            return;
        }
        if (stick_values[Settings::NativeAnalog::LStick].up) {
            emit TriggerKeyboardEvent(Qt::Key_Up);
            return;
        }
        break;
    case Core::HID::NpadStyleIndex::JoyconLeft:
        if (stick_values[Settings::NativeAnalog::LStick].left) {
            emit TriggerKeyboardEvent(Qt::Key_Down);
            return;
        }
        if (stick_values[Settings::NativeAnalog::LStick].up) {
            emit TriggerKeyboardEvent(Qt::Key_Left);
            return;
        }
        if (stick_values[Settings::NativeAnalog::LStick].down) {
            emit TriggerKeyboardEvent(Qt::Key_Right);
            return;
        }
        if (stick_values[Settings::NativeAnalog::LStick].right) {
            emit TriggerKeyboardEvent(Qt::Key_Up);
            return;
        }
        break;
    case Core::HID::NpadStyleIndex::JoyconRight:
        if (stick_values[Settings::NativeAnalog::RStick].right) {
            emit TriggerKeyboardEvent(Qt::Key_Down);
            return;
        }
        if (stick_values[Settings::NativeAnalog::RStick].down) {
            emit TriggerKeyboardEvent(Qt::Key_Left);
            return;
        }
        if (stick_values[Settings::NativeAnalog::RStick].up) {
            emit TriggerKeyboardEvent(Qt::Key_Right);
            return;
        }
        if (stick_values[Settings::NativeAnalog::RStick].left) {
            emit TriggerKeyboardEvent(Qt::Key_Up);
            return;
        }
        break;
    default:
        break;
    }
}


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <QKeyEvent>
#include <QObject>

#include "common/input.h"
#include "common/settings_input.h"

namespace Core::HID {
using ButtonValues = std::array<Common::Input::ButtonStatus, Settings::NativeButton::NumButtons>;
using SticksValues = std::array<Common::Input::StickStatus, Settings::NativeAnalog::NumAnalogs>;
enum class ControllerTriggerType;
class EmulatedController;
class HIDCore;
} // namespace Core::HID

class ControllerNavigation : public QObject {
    Q_OBJECT

public:
    explicit ControllerNavigation(Core::HID::HIDCore& hid_core, QWidget* parent = nullptr);
    ~ControllerNavigation();

    /// Disables events from the emulated controller
    void UnloadController();

signals:
    void TriggerKeyboardEvent(Qt::Key key);

private:
    void TriggerButton(Settings::NativeButton::Values native_button, Qt::Key key);
    void ControllerUpdateEvent(Core::HID::ControllerTriggerType type);

    void ControllerUpdateButton();

    void ControllerUpdateStick();

    Core::HID::ButtonValues button_values{};
    Core::HID::SticksValues stick_values{};

    int player1_callback_key{};
    int handheld_callback_key{};
    bool is_controller_set{};
    mutable std::mutex mutex;
    Core::HID::EmulatedController* player1_controller;
    Core::HID::EmulatedController* handheld_controller;
};


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include <QDialogButtonBox>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
#include <QVBoxLayout>
#include "yuzu/util/limitable_input_dialog.h"

LimitableInputDialog::LimitableInputDialog(QWidget* parent) : QDialog{parent} {
    CreateUI();
    ConnectEvents();
}

LimitableInputDialog::~LimitableInputDialog() = default;

void LimitableInputDialog::CreateUI() {
    text_label = new QLabel(this);
    text_entry = new QLineEdit(this);
    text_label_invalid = new QLabel(this);
    buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);

    auto* const layout = new QVBoxLayout;
    layout->addWidget(text_label);
    layout->addWidget(text_entry);
    layout->addWidget(text_label_invalid);
    layout->addWidget(buttons);

    setLayout(layout);
}

void LimitableInputDialog::ConnectEvents() {
    connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
    connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
}

QString LimitableInputDialog::GetText(QWidget* parent, const QString& title, const QString& text,
                                      int min_character_limit, int max_character_limit,
                                      InputLimiter limit_type) {
    Q_ASSERT(min_character_limit <= max_character_limit);

    LimitableInputDialog dialog{parent};
    dialog.setWindowTitle(title);
    dialog.text_label->setText(text);
    dialog.text_entry->setMaxLength(max_character_limit);
    dialog.text_label_invalid->show();

    switch (limit_type) {
    case InputLimiter::Filesystem:
        dialog.invalid_characters = QStringLiteral("<>:;\"/\\|,.!?*");
        break;
    default:
        dialog.invalid_characters.clear();
        dialog.text_label_invalid->hide();
        break;
    }
    dialog.text_label_invalid->setText(
        tr("The text can't contain any of the following characters:\n%1")
            .arg(dialog.invalid_characters));

    auto* const ok_button = dialog.buttons->button(QDialogButtonBox::Ok);
    ok_button->setEnabled(false);
    connect(dialog.text_entry, &QLineEdit::textEdited, [&] {
        if (!dialog.invalid_characters.isEmpty()) {
            dialog.RemoveInvalidCharacters();
        }
        ok_button->setEnabled(dialog.text_entry->text().length() >= min_character_limit);
    });

    if (dialog.exec() != QDialog::Accepted) {
        return {};
    }

    return dialog.text_entry->text();
}

void LimitableInputDialog::RemoveInvalidCharacters() {
    auto cpos = text_entry->cursorPosition();
    for (int i = 0; i < text_entry->text().length(); i++) {
        if (invalid_characters.contains(text_entry->text().at(i))) {
            text_entry->setText(text_entry->text().remove(i, 1));
            i--;
            cpos--;
        }
    }
    text_entry->setCursorPosition(cpos);
}


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <QDialog>

class QDialogButtonBox;
class QLabel;
class QLineEdit;

/// A QDialog that functions similarly to QInputDialog, however, it allows
/// restricting the minimum and total number of characters that can be entered.
class LimitableInputDialog final : public QDialog {
    Q_OBJECT
public:
    explicit LimitableInputDialog(QWidget* parent = nullptr);
    ~LimitableInputDialog() override;

    enum class InputLimiter {
        None,
        Filesystem,
    };

    static QString GetText(QWidget* parent, const QString& title, const QString& text,
                           int min_character_limit, int max_character_limit,
                           InputLimiter limit_type = InputLimiter::None);

private:
    void CreateUI();
    void ConnectEvents();

    void RemoveInvalidCharacters();
    QString invalid_characters;

    QLabel* text_label;
    QLineEdit* text_entry;
    QLabel* text_label_invalid;
    QDialogButtonBox* buttons;
};


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include <QKeyEvent>
#include <QScreen>
#include <QWindow>

#include "core/core.h"
#include "hid_core/frontend/input_interpreter.h"
#include "hid_core/hid_types.h"
#include "ui_overlay_dialog.h"
#include "yuzu/util/overlay_dialog.h"

namespace {

constexpr float BASE_TITLE_FONT_SIZE = 14.0f;
constexpr float BASE_FONT_SIZE = 18.0f;
constexpr float BASE_WIDTH = 1280.0f;
constexpr float BASE_HEIGHT = 720.0f;

} // Anonymous namespace

OverlayDialog::OverlayDialog(QWidget* parent, Core::System& system, const QString& title_text,
                             const QString& body_text, const QString& left_button_text,
                             const QString& right_button_text, Qt::Alignment alignment,
                             bool use_rich_text_)
    : QDialog(parent), ui{std::make_unique<Ui::OverlayDialog>()}, use_rich_text{use_rich_text_} {
    ui->setupUi(this);

    setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint | Qt::WindowTitleHint |
                   Qt::WindowSystemMenuHint | Qt::CustomizeWindowHint);
    setWindowModality(Qt::WindowModal);
    setAttribute(Qt::WA_TranslucentBackground);

    if (use_rich_text) {
        InitializeRichTextDialog(title_text, body_text, left_button_text, right_button_text,
                                 alignment);
    } else {
        InitializeRegularTextDialog(title_text, body_text, left_button_text, right_button_text,
                                    alignment);
    }

    MoveAndResizeWindow();

    // TODO (Morph): Remove this when InputInterpreter no longer relies on the HID backend
    if (system.IsPoweredOn() && !ui->buttonsDialog->isHidden()) {
        input_interpreter = std::make_unique<InputInterpreter>(system);

        StartInputThread();
    }
}

OverlayDialog::~OverlayDialog() {
    StopInputThread();
}

void OverlayDialog::InitializeRegularTextDialog(const QString& title_text, const QString& body_text,
                                                const QString& left_button_text,
                                                const QString& right_button_text,
                                                Qt::Alignment alignment) {
    ui->stackedDialog->setCurrentIndex(0);

    ui->label_title->setText(title_text);
    ui->label_dialog->setText(body_text);
    ui->button_cancel->setText(left_button_text);
    ui->button_ok_label->setText(right_button_text);

    ui->label_dialog->setAlignment(alignment);

    if (title_text.isEmpty()) {
        ui->label_title->hide();
        ui->verticalLayout_2->setStretch(0, 0);
        ui->verticalLayout_2->setStretch(1, 219);
        ui->verticalLayout_2->setStretch(2, 82);
    }

    if (left_button_text.isEmpty()) {
        ui->button_cancel->hide();
        ui->button_cancel->setEnabled(false);
    }

    if (right_button_text.isEmpty()) {
        ui->button_ok_label->hide();
        ui->button_ok_label->setEnabled(false);
    }

    if (ui->button_cancel->isHidden() && ui->button_ok_label->isHidden()) {
        ui->buttonsDialog->hide();
        return;
    }

    connect(
        ui->button_cancel, &QPushButton::clicked, this,
        [this](bool) {
            StopInputThread();
            QDialog::reject();
        },
        Qt::QueuedConnection);
    connect(
        ui->button_ok_label, &QPushButton::clicked, this,
        [this](bool) {
            StopInputThread();
            QDialog::accept();
        },
        Qt::QueuedConnection);
}

void OverlayDialog::InitializeRichTextDialog(const QString& title_text, const QString& body_text,
                                             const QString& left_button_text,
                                             const QString& right_button_text,
                                             Qt::Alignment alignment) {
    ui->stackedDialog->setCurrentIndex(1);

    ui->label_title_rich->setText(title_text);
    ui->text_browser_dialog->setText(body_text);
    ui->button_cancel_rich->setText(left_button_text);
    ui->button_ok_rich->setText(right_button_text);

    // TODO (Morph/Rei): Replace this with something that works better
    ui->text_browser_dialog->setAlignment(alignment);

    if (title_text.isEmpty()) {
        ui->label_title_rich->hide();
        ui->verticalLayout_3->setStretch(0, 0);
        ui->verticalLayout_3->setStretch(1, 438);
        ui->verticalLayout_3->setStretch(2, 82);
    }

    if (left_button_text.isEmpty()) {
        ui->button_cancel_rich->hide();
        ui->button_cancel_rich->setEnabled(false);
    }

    if (right_button_text.isEmpty()) {
        ui->button_ok_rich->hide();
        ui->button_ok_rich->setEnabled(false);
    }

    if (ui->button_cancel_rich->isHidden() && ui->button_ok_rich->isHidden()) {
        ui->buttonsRichDialog->hide();
        return;
    }

    connect(
        ui->button_cancel_rich, &QPushButton::clicked, this,
        [this](bool) {
            StopInputThread();
            QDialog::reject();
        },
        Qt::QueuedConnection);
    connect(
        ui->button_ok_rich, &QPushButton::clicked, this,
        [this](bool) {
            StopInputThread();
            QDialog::accept();
        },
        Qt::QueuedConnection);
}

void OverlayDialog::MoveAndResizeWindow() {
    const auto pos = parentWidget()->mapToGlobal(parentWidget()->rect().topLeft());
    const auto width = static_cast<float>(parentWidget()->width());
    const auto height = static_cast<float>(parentWidget()->height());

    // High DPI
    const float dpi_scale = screen()->logicalDotsPerInch() / 96.0f;

    const auto title_text_font_size = BASE_TITLE_FONT_SIZE * (height / BASE_HEIGHT) / dpi_scale;
    const auto body_text_font_size =
        BASE_FONT_SIZE * (((width / BASE_WIDTH) + (height / BASE_HEIGHT)) / 2.0f) / dpi_scale;
    const auto button_text_font_size = BASE_FONT_SIZE * (height / BASE_HEIGHT) / dpi_scale;

    QFont title_text_font(QStringLiteral("MS Shell Dlg 2"), title_text_font_size, QFont::Normal);
    QFont body_text_font(QStringLiteral("MS Shell Dlg 2"), body_text_font_size, QFont::Normal);
    QFont button_text_font(QStringLiteral("MS Shell Dlg 2"), button_text_font_size, QFont::Normal);

    if (use_rich_text) {
        ui->label_title_rich->setFont(title_text_font);
        ui->text_browser_dialog->setFont(body_text_font);
        ui->button_cancel_rich->setFont(button_text_font);
        ui->button_ok_rich->setFont(button_text_font);
    } else {
        ui->label_title->setFont(title_text_font);
        ui->label_dialog->setFont(body_text_font);
        ui->button_cancel->setFont(button_text_font);
        ui->button_ok_label->setFont(button_text_font);
    }

    QDialog::move(pos);
    QDialog::resize(width, height);
}

template <Core::HID::NpadButton... T>
void OverlayDialog::HandleButtonPressedOnce() {
    const auto f = [this](Core::HID::NpadButton button) {
        if (input_interpreter->IsButtonPressedOnce(button)) {
            TranslateButtonPress(button);
        }
    };

    (f(T), ...);
}

void OverlayDialog::TranslateButtonPress(Core::HID::NpadButton button) {
    QPushButton* left_button = use_rich_text ? ui->button_cancel_rich : ui->button_cancel;
    QPushButton* right_button = use_rich_text ? ui->button_ok_rich : ui->button_ok_label;

    // TODO (Morph): Handle QTextBrowser text scrolling
    // TODO (Morph): focusPrevious/NextChild() doesn't work well with the rich text dialog, fix it

    switch (button) {
    case Core::HID::NpadButton::A:
    case Core::HID::NpadButton::B:
        if (left_button->hasFocus()) {
            left_button->click();
        } else if (right_button->hasFocus()) {
            right_button->click();
        }
        break;
    case Core::HID::NpadButton::Left:
    case Core::HID::NpadButton::StickLLeft:
        focusPreviousChild();
        break;
    case Core::HID::NpadButton::Right:
    case Core::HID::NpadButton::StickLRight:
        focusNextChild();
        break;
    default:
        break;
    }
}

void OverlayDialog::StartInputThread() {
    if (input_thread_running) {
        return;
    }

    input_thread_running = true;

    input_thread = std::thread(&OverlayDialog::InputThread, this);
}

void OverlayDialog::StopInputThread() {
    input_thread_running = false;

    if (input_thread.joinable()) {
        input_thread.join();
    }
}

void OverlayDialog::InputThread() {
    while (input_thread_running) {
        input_interpreter->PollInput();

        HandleButtonPressedOnce<Core::HID::NpadButton::A, Core::HID::NpadButton::B,
                                Core::HID::NpadButton::Left, Core::HID::NpadButton::Right,
                                Core::HID::NpadButton::StickLLeft,
                                Core::HID::NpadButton::StickLRight>();

        std::this_thread::sleep_for(std::chrono::milliseconds(50));
    }
}

void OverlayDialog::keyPressEvent(QKeyEvent* e) {
    if (!ui->buttonsDialog->isHidden() || e->key() != Qt::Key_Escape) {
        QDialog::keyPressEvent(e);
    }
}


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <atomic>
#include <memory>
#include <thread>

#include <QDialog>

#include "common/common_types.h"

class InputInterpreter;

namespace Core {
class System;
}

namespace Core::HID {
enum class NpadButton : u64;
}

namespace Ui {
class OverlayDialog;
}

/**
 * An OverlayDialog is an interactive dialog that accepts controller input (while a game is running)
 * This dialog attempts to replicate the look and feel of the Nintendo Switch's overlay dialogs and
 * provide some extra features such as embedding HTML/Rich Text content in a QTextBrowser.
 * The OverlayDialog provides 2 modes: one to embed regular text into a QLabel and another to embed
 * HTML/Rich Text content into a QTextBrowser.
 */
class OverlayDialog final : public QDialog {
    Q_OBJECT

public:
    explicit OverlayDialog(QWidget* parent, Core::System& system, const QString& title_text,
                           const QString& body_text, const QString& left_button_text,
                           const QString& right_button_text,
                           Qt::Alignment alignment = Qt::AlignCenter, bool use_rich_text_ = false);
    ~OverlayDialog() override;

private:
    /**
     * Initializes a text dialog with a QLabel storing text.
     * Only use this for short text as the dialog buttons would be squashed with longer text.
     *
     * @param title_text Title text to be displayed
     * @param body_text Main text to be displayed
     * @param left_button_text Left button text. If empty, the button is hidden and disabled
     * @param right_button_text Right button text. If empty, the button is hidden and disabled
     * @param alignment Main text alignment
     */
    void InitializeRegularTextDialog(const QString& title_text, const QString& body_text,
                                     const QString& left_button_text,
                                     const QString& right_button_text, Qt::Alignment alignment);

    /**
     * Initializes a text dialog with a QTextBrowser storing text.
     * This is ideal for longer text or rich text content. A scrollbar is shown for longer text.
     *
     * @param title_text Title text to be displayed
     * @param body_text Main text to be displayed
     * @param left_button_text Left button text. If empty, the button is hidden and disabled
     * @param right_button_text Right button text. If empty, the button is hidden and disabled
     * @param alignment Main text alignment
     */
    void InitializeRichTextDialog(const QString& title_text, const QString& body_text,
                                  const QString& left_button_text, const QString& right_button_text,
                                  Qt::Alignment alignment);

    /// Moves and resizes the dialog to be fully overlaid on top of the parent window.
    void MoveAndResizeWindow();

    /**
     * Handles button presses and converts them into keyboard input.
     *
     * @tparam HIDButton The list of buttons that can be converted into keyboard input.
     */
    template <Core::HID::NpadButton... T>
    void HandleButtonPressedOnce();

    /**
     * Translates a button press to focus or click either the left or right buttons.
     *
     * @param button The button press to process.
     */
    void TranslateButtonPress(Core::HID::NpadButton button);

    void StartInputThread();
    void StopInputThread();

    /// The thread where input is being polled and processed.
    void InputThread();
    void keyPressEvent(QKeyEvent* e) override;

    std::unique_ptr<Ui::OverlayDialog> ui;

    bool use_rich_text;

    std::unique_ptr<InputInterpreter> input_interpreter;

    std::thread input_thread;

    std::atomic<bool> input_thread_running{};
};


code:
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>OverlayDialog</class>
 <widget class="QDialog" name="OverlayDialog">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>1280</width>
    <height>720</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Dialog</string>
  </property>
  <property name="styleSheet">
   <string notr="true"/>
  </property>
  <layout class="QVBoxLayout" name="verticalLayout">
   <property name="spacing">
    <number>0</number>
   </property>
   <property name="leftMargin">
    <number>0</number>
   </property>
   <property name="topMargin">
    <number>0</number>
   </property>
   <property name="rightMargin">
    <number>0</number>
   </property>
   <property name="bottomMargin">
    <number>0</number>
   </property>
   <item>
    <widget class="QStackedWidget" name="stackedDialog">
     <property name="currentIndex">
      <number>0</number>
     </property>
     <widget class="QWidget" name="lineDialog">
      <layout class="QGridLayout" name="lineDialogGridLayout" rowstretch="210,300,210" columnstretch="250,780,250">
       <property name="leftMargin">
        <number>0</number>
       </property>
       <property name="topMargin">
        <number>0</number>
       </property>
       <property name="rightMargin">
        <number>0</number>
       </property>
       <property name="bottomMargin">
        <number>0</number>
       </property>
       <property name="spacing">
        <number>0</number>
       </property>
       <item row="1" column="1">
        <widget class="QWidget" name="contentDialog" native="true">
         <layout class="QVBoxLayout" name="verticalLayout_2" stretch="70,149,82">
          <property name="spacing">
           <number>0</number>
          </property>
          <property name="leftMargin">
           <number>0</number>
          </property>
          <property name="topMargin">
           <number>0</number>
          </property>
          <property name="rightMargin">
           <number>0</number>
          </property>
          <property name="bottomMargin">
           <number>0</number>
          </property>
          <item>
           <widget class="QLabel" name="label_title">
            <property name="font">

              <pointsize>14</pointsize>

            </property>
            <property name="alignment">
             <set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
            </property>
           </widget>
          </item>
          <item>
           <widget class="QLabel" name="label_dialog">
            <property name="font">

              <pointsize>18</pointsize>

            </property>
            <property name="alignment">
             <set>Qt::AlignCenter</set>
            </property>
            <property name="wordWrap">
             <bool>true</bool>
            </property>
           </widget>
          </item>
          <item>
           <widget class="QWidget" name="buttonsDialog" native="true">
            <layout class="QHBoxLayout" name="horizontalLayout">
             <property name="spacing">
              <number>0</number>
             </property>
             <property name="leftMargin">
              <number>0</number>
             </property>
             <property name="topMargin">
              <number>0</number>
             </property>
             <property name="rightMargin">
              <number>0</number>
             </property>
             <property name="bottomMargin">
              <number>0</number>
             </property>
             <item>
              <widget class="QPushButton" name="button_cancel">
               <property name="sizePolicy">
                <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
                 <horstretch>0</horstretch>
                 <verstretch>0</verstretch>
                </sizepolicy>
               </property>
               <property name="font">

                 <pointsize>18</pointsize>

               </property>
               <property name="text">
                <string>Cancel</string>
               </property>
              </widget>
             </item>
             <item>
              <widget class="QPushButton" name="button_ok_label">
               <property name="sizePolicy">
                <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
                 <horstretch>0</horstretch>
                 <verstretch>0</verstretch>
                </sizepolicy>
               </property>
               <property name="font">

                 <pointsize>18</pointsize>

               </property>
               <property name="text">
                <string>OK</string>
               </property>
              </widget>
             </item>
            </layout>
           </widget>
          </item>
         </layout>
        </widget>
       </item>
       <item row="0" column="1">
        <spacer name="verticalSpacer">
         <property name="orientation">
          <enum>Qt::Vertical</enum>
         </property>
         <property name="sizeHint" stdset="0">
          <size>
           <width>20</width>
           <height>40</height>
          </size>
         </property>
        </spacer>
       </item>
       <item row="1" column="0">
        <spacer name="horizontalSpacer">
         <property name="orientation">
          <enum>Qt::Horizontal</enum>
         </property>
         <property name="sizeHint" stdset="0">
          <size>
           <width>40</width>
           <height>20</height>
          </size>
         </property>
        </spacer>
       </item>
       <item row="2" column="1">
        <spacer name="verticalSpacer_2">
         <property name="orientation">
          <enum>Qt::Vertical</enum>
         </property>
         <property name="sizeHint" stdset="0">
          <size>
           <width>20</width>
           <height>40</height>
          </size>
         </property>
        </spacer>
       </item>
       <item row="1" column="2">
        <spacer name="horizontalSpacer_2">
         <property name="orientation">
          <enum>Qt::Horizontal</enum>
         </property>
         <property name="sizeHint" stdset="0">
          <size>
           <width>40</width>
           <height>20</height>
          </size>
         </property>
        </spacer>
       </item>
      </layout>
     </widget>
     <widget class="QWidget" name="richDialog">
      <layout class="QGridLayout" name="richDialogGridLayout" rowstretch="100,520,100" columnstretch="165,950,165">
       <property name="leftMargin">
        <number>0</number>
       </property>
       <property name="topMargin">
        <number>0</number>
       </property>
       <property name="rightMargin">
        <number>0</number>
       </property>
       <property name="bottomMargin">
        <number>0</number>
       </property>
       <property name="spacing">
        <number>0</number>
       </property>
       <item row="1" column="0">
        <spacer name="horizontalSpacer_3">
         <property name="orientation">
          <enum>Qt::Horizontal</enum>
         </property>
         <property name="sizeHint" stdset="0">
          <size>
           <width>40</width>
           <height>20</height>
          </size>
         </property>
        </spacer>
       </item>
       <item row="2" column="1">
        <spacer name="verticalSpacer_4">
         <property name="orientation">
          <enum>Qt::Vertical</enum>
         </property>
         <property name="sizeHint" stdset="0">
          <size>
           <width>20</width>
           <height>40</height>
          </size>
         </property>
        </spacer>
       </item>
       <item row="0" column="1">
        <spacer name="verticalSpacer_3">
         <property name="orientation">
          <enum>Qt::Vertical</enum>
         </property>
         <property name="sizeHint" stdset="0">
          <size>
           <width>20</width>
           <height>40</height>
          </size>
         </property>
        </spacer>
       </item>
       <item row="1" column="1">
        <widget class="QWidget" name="contentRichDialog" native="true">
         <layout class="QVBoxLayout" name="verticalLayout_3" stretch="70,368,82">
          <property name="spacing">
           <number>0</number>
          </property>
          <property name="leftMargin">
           <number>0</number>
          </property>
          <property name="topMargin">
           <number>0</number>
          </property>
          <property name="rightMargin">
           <number>0</number>
          </property>
          <property name="bottomMargin">
           <number>0</number>
          </property>
          <item>
           <widget class="QLabel" name="label_title_rich">
            <property name="font">

              <pointsize>14</pointsize>

            </property>
            <property name="text">
             <string/>
            </property>
            <property name="alignment">
             <set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
            </property>
           </widget>
          </item>
          <item>
           <widget class="QTextBrowser" name="text_browser_dialog">
            <property name="font">

              <pointsize>18</pointsize>

            </property>
            <property name="html">
             <string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
p, li { white-space: pre-wrap; }
</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:18pt; font-weight:400; font-style:normal;">
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html></string>
            </property>
           </widget>
          </item>
          <item>
           <widget class="QWidget" name="buttonsRichDialog" native="true">
            <layout class="QHBoxLayout" name="horizontalLayout_2">
             <property name="spacing">
              <number>0</number>
             </property>
             <property name="leftMargin">
              <number>0</number>
             </property>
             <property name="topMargin">
              <number>0</number>
             </property>
             <property name="rightMargin">
              <number>0</number>
             </property>
             <property name="bottomMargin">
              <number>0</number>
             </property>
             <item>
              <widget class="QPushButton" name="button_cancel_rich">
               <property name="sizePolicy">
                <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
                 <horstretch>0</horstretch>
                 <verstretch>0</verstretch>
                </sizepolicy>
               </property>
               <property name="font">

                 <pointsize>18</pointsize>

               </property>
               <property name="text">
                <string>Cancel</string>
               </property>
              </widget>
             </item>
             <item>
              <widget class="QPushButton" name="button_ok_rich">
               <property name="sizePolicy">
                <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
                 <horstretch>0</horstretch>
                 <verstretch>0</verstretch>
                </sizepolicy>
               </property>
               <property name="font">

                 <pointsize>18</pointsize>

               </property>
               <property name="text">
                <string>OK</string>
               </property>
              </widget>
             </item>
            </layout>
           </widget>
          </item>
         </layout>
        </widget>
       </item>
       <item row="1" column="2">
        <spacer name="horizontalSpacer_4">
         <property name="orientation">
          <enum>Qt::Horizontal</enum>
         </property>
         <property name="sizeHint" stdset="0">
          <size>
           <width>40</width>
           <height>20</height>
          </size>
         </property>
        </spacer>
       </item>
      </layout>
     </widget>
    </widget>
   </item>
  </layout>
 </widget>
 <resources>
  <include location="../../../dist/icons/overlay/overlay.qrc"/>
 </resources>
 <connections/>
</ui>


code:
// SPDX-FileCopyrightText: 2018 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include <QDialogButtonBox>
#include <QKeySequenceEdit>
#include <QVBoxLayout>
#include "yuzu/util/sequence_dialog/sequence_dialog.h"

SequenceDialog::SequenceDialog(QWidget* parent) : QDialog(parent) {
    setWindowTitle(tr("Enter a hotkey"));

    key_sequence = new QKeySequenceEdit;

    auto* const buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
    buttons->setCenterButtons(true);

    auto* const layout = new QVBoxLayout(this);
    layout->addWidget(key_sequence);
    layout->addWidget(buttons);

    connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
    connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
}

SequenceDialog::~SequenceDialog() = default;

QKeySequence SequenceDialog::GetSequence() const {
    // Only the first key is returned. The other 3, if present, are ignored.
    return QKeySequence(key_sequence->keySequence()[0]);
}

bool SequenceDialog::focusNextPrevChild(bool next) {
    return false;
}

void SequenceDialog::closeEvent(QCloseEvent*) {
    reject();
}


code:
// SPDX-FileCopyrightText: 2018 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <QDialog>

class QKeySequenceEdit;

class SequenceDialog : public QDialog {
    Q_OBJECT

public:
    explicit SequenceDialog(QWidget* parent = nullptr);
    ~SequenceDialog() override;

    QKeySequence GetSequence() const;
    void closeEvent(QCloseEvent*) override;

private:
    QKeySequenceEdit* key_sequence;
    bool focusNextPrevChild(bool next) override;
};


code:
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#ifdef YUZU_USE_QT_WEB_ENGINE

#include "yuzu/util/url_request_interceptor.h"

UrlRequestInterceptor::UrlRequestInterceptor(QObject* p) : QWebEngineUrlRequestInterceptor(p) {}

UrlRequestInterceptor::~UrlRequestInterceptor() = default;

void UrlRequestInterceptor::interceptRequest(QWebEngineUrlRequestInfo& info) {
    const auto resource_type = info.resourceType();

    switch (resource_type) {
    case QWebEngineUrlRequestInfo::ResourceTypeMainFrame:
        requested_url = info.requestUrl();
        emit FrameChanged();
        break;
    case QWebEngineUrlRequestInfo::ResourceTypeSubFrame:
    case QWebEngineUrlRequestInfo::ResourceTypeXhr:
        emit FrameChanged();
        break;
    default:
        break;
    }
}

QUrl UrlRequestInterceptor::GetRequestedURL() const {
    return requested_url;
}

#endif


code:
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#ifdef YUZU_USE_QT_WEB_ENGINE

#include <QObject>
#include <QWebEngineUrlRequestInterceptor>

class UrlRequestInterceptor : public QWebEngineUrlRequestInterceptor {
    Q_OBJECT

public:
    explicit UrlRequestInterceptor(QObject* p = nullptr);
    ~UrlRequestInterceptor() override;

    void interceptRequest(QWebEngineUrlRequestInfo& info) override;

    QUrl GetRequestedURL() const;

signals:
    void FrameChanged();

private:
    QUrl requested_url;
};

#endif


code:
// SPDX-FileCopyrightText: 2015 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include <array>
#include <cmath>
#include <QPainter>

#include "common/logging/log.h"
#include "yuzu/util/util.h"

#ifdef _WIN32
#include <windows.h>
#include "common/fs/file.h"
#endif

QFont GetMonospaceFont() {
    QFont font(QStringLiteral("monospace"));
    // Automatic fallback to a monospace font on on platforms without a font called "monospace"
    font.setStyleHint(QFont::Monospace);
    font.setFixedPitch(true);
    return font;
}

QString ReadableByteSize(qulonglong size) {
    static constexpr std::array units{"B", "KiB", "MiB", "GiB", "TiB", "PiB"};
    if (size == 0) {
        return QStringLiteral("0");
    }

    const int digit_groups = std::min(static_cast<int>(std::log10(size) / std::log10(1024)),
                                      static_cast<int>(units.size()));
    return QStringLiteral("%L1 %2")
        .arg(size / std::pow(1024, digit_groups), 0, 'f', 1)
        .arg(QString::fromUtf8(units[digit_groups]));
}

QPixmap CreateCirclePixmapFromColor(const QColor& color) {
    QPixmap circle_pixmap(16, 16);
    circle_pixmap.fill(Qt::transparent);
    QPainter painter(&circle_pixmap);
    painter.setRenderHint(QPainter::Antialiasing);
    painter.setPen(color);
    painter.setBrush(color);
    painter.drawEllipse({circle_pixmap.width() / 2.0, circle_pixmap.height() / 2.0}, 7.0, 7.0);
    return circle_pixmap;
}

bool SaveIconToFile(const std::filesystem::path& icon_path, const QImage& image) {
#if defined(WIN32)
#pragma pack(push, 2)
    struct IconDir {
        WORD id_reserved;
        WORD id_type;
        WORD id_count;
    };

    struct IconDirEntry {
        BYTE width;
        BYTE height;
        BYTE color_count;
        BYTE reserved;
        WORD planes;
        WORD bit_count;
        DWORD bytes_in_res;
        DWORD image_offset;
    };
#pragma pack(pop)

    const QImage source_image = image.convertToFormat(QImage::Format_RGB32);
    constexpr std::array<int, 7> scale_sizes{256, 128, 64, 48, 32, 24, 16};
    constexpr int bytes_per_pixel = 4;

    const IconDir icon_dir{
        .id_reserved = 0,
        .id_type = 1,
        .id_count = static_cast<WORD>(scale_sizes.size()),
    };

    Common::FS::IOFile icon_file(icon_path.string(), Common::FS::FileAccessMode::Write,
                                 Common::FS::FileType::BinaryFile);
    if (!icon_file.IsOpen()) {
        return false;
    }

    if (!icon_file.Write(icon_dir)) {
        return false;
    }

    std::size_t image_offset = sizeof(IconDir) + (sizeof(IconDirEntry) * scale_sizes.size());
    for (std::size_t i = 0; i < scale_sizes.size(); i++) {
        const int image_size = scale_sizes[i] * scale_sizes[i] * bytes_per_pixel;
        const IconDirEntry icon_entry{
            .width = static_cast<BYTE>(scale_sizes[i]),
            .height = static_cast<BYTE>(scale_sizes[i]),
            .color_count = 0,
            .reserved = 0,
            .planes = 1,
            .bit_count = bytes_per_pixel * 8,
            .bytes_in_res = static_cast<DWORD>(sizeof(BITMAPINFOHEADER) + image_size),
            .image_offset = static_cast<DWORD>(image_offset),
        };
        image_offset += icon_entry.bytes_in_res;
        if (!icon_file.Write(icon_entry)) {
            return false;
        }
    }

    for (std::size_t i = 0; i < scale_sizes.size(); i++) {
        const QImage scaled_image = source_image.scaled(
            scale_sizes[i], scale_sizes[i], Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
        const BITMAPINFOHEADER info_header{
            .biSize = sizeof(BITMAPINFOHEADER),
            .biWidth = scaled_image.width(),
            .biHeight = scaled_image.height() * 2,
            .biPlanes = 1,
            .biBitCount = bytes_per_pixel * 8,
            .biCompression = BI_RGB,
            .biSizeImage{},
            .biXPelsPerMeter{},
            .biYPelsPerMeter{},
            .biClrUsed{},
            .biClrImportant{},
        };

        if (!icon_file.Write(info_header)) {
            return false;
        }

        for (int y = 0; y < scaled_image.height(); y++) {
            const auto* line = scaled_image.scanLine(scaled_image.height() - 1 - y);
            std::vector<u8> line_data(scaled_image.width() * bytes_per_pixel);
            std::memcpy(line_data.data(), line, line_data.size());
            if (!icon_file.Write(line_data)) {
                return false;
            }
        }
    }
    icon_file.Close();

    return true;
#elif defined(__linux__) || defined(__FreeBSD__)
    // Convert and write the icon as a PNG
    if (!image.save(QString::fromStdString(icon_path.string()))) {
        LOG_ERROR(Frontend, "Could not write icon as PNG to file");
    } else {
        LOG_INFO(Frontend, "Wrote an icon to {}", icon_path.string());
    }
    return true;
#else
    return false;
#endif
}


code:
// SPDX-FileCopyrightText: 2015 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <filesystem>
#include <QFont>
#include <QString>

/// Returns a QFont object appropriate to use as a monospace font for debugging widgets, etc.
[[nodiscard]] QFont GetMonospaceFont();

/// Convert a size in bytes into a readable format (KiB, MiB, etc.)
[[nodiscard]] QString ReadableByteSize(qulonglong size);

/**
 * Creates a circle pixmap from a specified color
 * @param color The color the pixmap shall have
 * @return QPixmap circle pixmap
 */
[[nodiscard]] QPixmap CreateCirclePixmapFromColor(const QColor& color);

/**
 * Saves a windows icon to a file
 * @param path The icons path
 * @param image The image to save
 * @return bool If the operation succeeded
 */
[[nodiscard]] bool SaveIconToFile(const std::filesystem::path& icon_path, const QImage& image);


code:
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include <utility>
#include <vector>

#include "yuzu/qt_common.h"

#include "common/dynamic_library.h"
#include "common/logging/log.h"
#include "video_core/vulkan_common/vulkan_device.h"
#include "video_core/vulkan_common/vulkan_instance.h"
#include "video_core/vulkan_common/vulkan_library.h"
#include "video_core/vulkan_common/vulkan_surface.h"
#include "video_core/vulkan_common/vulkan_wrapper.h"
#include "vulkan/vulkan_core.h"
#include "yuzu/vk_device_info.h"

class QWindow;

namespace VkDeviceInfo {
Record::Record(std::string_view name_, const std::vector<VkPresentModeKHR>& vsync_modes_,
               bool has_broken_compute_)
    : name{name_}, vsync_support{vsync_modes_}, has_broken_compute{has_broken_compute_} {}

Record::~Record() = default;

void PopulateRecords(std::vector<Record>& records, QWindow* window) try {
    using namespace Vulkan;

    // Create a test window with a Vulkan surface type for checking present modes.
    QWindow test_window(window);
    test_window.setSurfaceType(QWindow::VulkanSurface);
    test_window.create();
    auto wsi = QtCommon::GetWindowSystemInfo(&test_window);

    vk::InstanceDispatch dld;
    const auto library = OpenLibrary();
    const vk::Instance instance = CreateInstance(*library, dld, VK_API_VERSION_1_1, wsi.type);
    const std::vector<VkPhysicalDevice> physical_devices = instance.EnumeratePhysicalDevices();
    vk::SurfaceKHR surface = CreateSurface(instance, wsi);

    records.clear();
    records.reserve(physical_devices.size());
    for (const VkPhysicalDevice device : physical_devices) {
        const auto physical_device = vk::PhysicalDevice(device, dld);
        const std::string name = physical_device.GetProperties().deviceName;
        const std::vector<VkPresentModeKHR> present_modes =
            physical_device.GetSurfacePresentModesKHR(*surface);

        VkPhysicalDeviceDriverProperties driver_properties{};
        driver_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES;
        driver_properties.pNext = nullptr;
        VkPhysicalDeviceProperties2 properties{};
        properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR;
        properties.pNext = &driver_properties;
        dld.vkGetPhysicalDeviceProperties2(physical_device, &properties);

        bool has_broken_compute{Vulkan::Device::CheckBrokenCompute(
            driver_properties.driverID, properties.properties.driverVersion)};

        records.push_back(VkDeviceInfo::Record(name, present_modes, has_broken_compute));
    }
} catch (const Vulkan::vk::Exception& exception) {
    LOG_ERROR(Frontend, "Failed to enumerate devices with error: {}", exception.what());
}
} // namespace VkDeviceInfo


code:
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <algorithm>
#include <iterator>
#include <memory>
#include <string>
#include <string_view>
#include <vector>
#include "common/common_types.h"
#include "vulkan/vulkan_core.h"

class QWindow;

namespace Settings {
enum class VSyncMode : u32;
}
// #include "common/settings.h"

namespace VkDeviceInfo {
// Short class to record Vulkan driver information for configuration purposes
class Record {
public:
    explicit Record(std::string_view name, const std::vector<VkPresentModeKHR>& vsync_modes,
                    bool has_broken_compute);
    ~Record();

    const std::string name;
    const std::vector<VkPresentModeKHR> vsync_support;
    const bool has_broken_compute;
};

void PopulateRecords(std::vector<Record>& records, QWindow* window);
} // namespace VkDeviceInfo


code:
<!--
SPDX-FileCopyrightText: 2021 yuzu Emulator Project
SPDX-License-Identifier: GPL-2.0-or-later
-->

<RCC>
    <qresource prefix="/img">
        <file alias="yuzu.ico">../../dist/yuzu.ico</file>
    </qresource>
</RCC>


code:
// SPDX-FileCopyrightText: 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "winresrc.h"
/////////////////////////////////////////////////////////////////////////////
//
// Icon
//

// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
// QT requires that the default application icon is named IDI_ICON1

IDI_ICON1               ICON                    "../../dist/yuzu.ico"

/////////////////////////////////////////////////////////////////////////////
//
// RT_MANIFEST
//

0                       RT_MANIFEST             "../../dist/yuzu.manifest"


code:
# SPDX-FileCopyrightText: 2018 yuzu Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later

# Credits to Samantas5855 and others for this function.
function(create_resource file output filename)
    # Read hex data from file
    file(READ ${file} filedata HEX)
    # Convert hex data for C compatibility
    string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\1," filedata ${filedata})
    # Write data to output file
    set(RESOURCES_DIR "${PROJECT_BINARY_DIR}/dist" PARENT_SCOPE)
    file(WRITE "${PROJECT_BINARY_DIR}/dist/${output}" "const unsigned char ${filename}[] = {${filedata}};\nconst unsigned ${filename}_size = sizeof(${filename});\n")
endfunction()

add_executable(yuzu-cmd
    emu_window/emu_window_sdl2.cpp
    emu_window/emu_window_sdl2.h
    emu_window/emu_window_sdl2_gl.cpp
    emu_window/emu_window_sdl2_gl.h
    emu_window/emu_window_sdl2_null.cpp
    emu_window/emu_window_sdl2_null.h
    emu_window/emu_window_sdl2_vk.cpp
    emu_window/emu_window_sdl2_vk.h
    precompiled_headers.h
    sdl_config.cpp
    sdl_config.h
    yuzu.cpp
    yuzu.rc
)

target_link_libraries(yuzu-cmd PRIVATE common core input_common frontend_common)
target_link_libraries(yuzu-cmd PRIVATE glad)
if (MSVC)
    target_link_libraries(yuzu-cmd PRIVATE getopt)
endif()
target_link_libraries(yuzu-cmd PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)

create_resource("../../dist/yuzu.bmp" "yuzu_cmd/yuzu_icon.h" "yuzu_icon")
target_include_directories(yuzu-cmd PRIVATE ${RESOURCES_DIR})

target_link_libraries(yuzu-cmd PRIVATE SDL2::SDL2 Vulkan::Headers)

if(UNIX AND NOT APPLE)
    install(TARGETS yuzu-cmd)
endif()

if(WIN32)
    # compile as a win32 gui application instead of a console application
    if(MSVC)
        set_target_properties(yuzu-cmd PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup")
    elseif(MINGW)
        set_target_properties(yuzu-cmd PROPERTIES LINK_FLAGS_RELEASE "-Wl,--subsystem,windows")
    endif()
endif()

if (MSVC)
    include(CopyYuzuSDLDeps)
    copy_yuzu_SDL_deps(yuzu-cmd)
endif()

if (YUZU_USE_PRECOMPILED_HEADERS)
    target_precompile_headers(yuzu-cmd PRIVATE precompiled_headers.h)
endif()

create_target_directory_groups(yuzu-cmd)


code:
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include <SDL.h>

#include "common/logging/log.h"
#include "common/scm_rev.h"
#include "common/settings.h"
#include "core/core.h"
#include "core/perf_stats.h"
#include "hid_core/hid_core.h"
#include "input_common/drivers/keyboard.h"
#include "input_common/drivers/mouse.h"
#include "input_common/drivers/touch_screen.h"
#include "input_common/main.h"
#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
#include "yuzu_cmd/yuzu_icon.h"

EmuWindow_SDL2::EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem_, Core::System& system_)
    : input_subsystem{input_subsystem_}, system{system_} {
    input_subsystem->Initialize();
    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) < 0) {
        LOG_CRITICAL(Frontend, "Failed to initialize SDL2: {}, Exiting...", SDL_GetError());
        exit(1);
    }
    SDL_SetMainReady();
}

EmuWindow_SDL2::~EmuWindow_SDL2() {
    system.HIDCore().UnloadInputDevices();
    input_subsystem->Shutdown();
    SDL_Quit();
}

InputCommon::MouseButton EmuWindow_SDL2::SDLButtonToMouseButton(u32 button) const {
    switch (button) {
    case SDL_BUTTON_LEFT:
        return InputCommon::MouseButton::Left;
    case SDL_BUTTON_RIGHT:
        return InputCommon::MouseButton::Right;
    case SDL_BUTTON_MIDDLE:
        return InputCommon::MouseButton::Wheel;
    case SDL_BUTTON_X1:
        return InputCommon::MouseButton::Backward;
    case SDL_BUTTON_X2:
        return InputCommon::MouseButton::Forward;
    default:
        return InputCommon::MouseButton::Undefined;
    }
}

std::pair<float, float> EmuWindow_SDL2::MouseToTouchPos(s32 touch_x, s32 touch_y) const {
    int w, h;
    SDL_GetWindowSize(render_window, &w, &h);
    const float fx = static_cast<float>(touch_x) / w;
    const float fy = static_cast<float>(touch_y) / h;

    return {std::clamp<float>(fx, 0.0f, 1.0f), std::clamp<float>(fy, 0.0f, 1.0f)};
}

void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) {
    const auto mouse_button = SDLButtonToMouseButton(button);
    if (state == SDL_PRESSED) {
        const auto [touch_x, touch_y] = MouseToTouchPos(x, y);
        input_subsystem->GetMouse()->PressButton(x, y, mouse_button);
        input_subsystem->GetMouse()->PressMouseButton(mouse_button);
        input_subsystem->GetMouse()->PressTouchButton(touch_x, touch_y, mouse_button);
    } else {
        input_subsystem->GetMouse()->ReleaseButton(mouse_button);
    }
}

void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) {
    const auto [touch_x, touch_y] = MouseToTouchPos(x, y);
    input_subsystem->GetMouse()->Move(x, y, 0, 0);
    input_subsystem->GetMouse()->MouseMove(touch_x, touch_y);
    input_subsystem->GetMouse()->TouchMove(touch_x, touch_y);
}

void EmuWindow_SDL2::OnFingerDown(float x, float y, std::size_t id) {
    input_subsystem->GetTouchScreen()->TouchPressed(x, y, id);
}

void EmuWindow_SDL2::OnFingerMotion(float x, float y, std::size_t id) {
    input_subsystem->GetTouchScreen()->TouchMoved(x, y, id);
}

void EmuWindow_SDL2::OnFingerUp() {
    input_subsystem->GetTouchScreen()->ReleaseAllTouch();
}

void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) {
    if (state == SDL_PRESSED) {
        input_subsystem->GetKeyboard()->PressKey(static_cast<std::size_t>(key));
    } else if (state == SDL_RELEASED) {
        input_subsystem->GetKeyboard()->ReleaseKey(static_cast<std::size_t>(key));
    }
}

bool EmuWindow_SDL2::IsOpen() const {
    return is_open;
}

bool EmuWindow_SDL2::IsShown() const {
    return is_shown;
}

void EmuWindow_SDL2::OnResize() {
    int width, height;
    SDL_GL_GetDrawableSize(render_window, &width, &height);
    UpdateCurrentFramebufferLayout(width, height);
}

void EmuWindow_SDL2::ShowCursor(bool show_cursor) {
    SDL_ShowCursor(show_cursor ? SDL_ENABLE : SDL_DISABLE);
}

void EmuWindow_SDL2::Fullscreen() {
    SDL_DisplayMode display_mode;
    switch (Settings::values.fullscreen_mode.GetValue()) {
    case Settings::FullscreenMode::Exclusive:
        // Set window size to render size before entering fullscreen -- SDL2 does not resize window
        // to display dimensions automatically in this mode.
        if (SDL_GetDesktopDisplayMode(0, &display_mode) == 0) {
            SDL_SetWindowSize(render_window, display_mode.w, display_mode.h);
        } else {
            LOG_ERROR(Frontend, "SDL_GetDesktopDisplayMode failed: {}", SDL_GetError());
        }

        if (SDL_SetWindowFullscreen(render_window, SDL_WINDOW_FULLSCREEN) == 0) {
            return;
        }

        LOG_ERROR(Frontend, "Fullscreening failed: {}", SDL_GetError());
        LOG_INFO(Frontend, "Attempting to use borderless fullscreen...");
        [[fallthrough]];
    case Settings::FullscreenMode::Borderless:
        if (SDL_SetWindowFullscreen(render_window, SDL_WINDOW_FULLSCREEN_DESKTOP) == 0) {
            return;
        }

        LOG_ERROR(Frontend, "Borderless fullscreening failed: {}", SDL_GetError());
        [[fallthrough]];
    default:
        // Fallback algorithm: Maximise window.
        // Works on all systems (unless something is seriously wrong), so no fallback for this one.
        LOG_INFO(Frontend, "Falling back on a maximised window...");
        SDL_MaximizeWindow(render_window);
        break;
    }
}

void EmuWindow_SDL2::WaitEvent() {
    // Called on main thread
    SDL_Event event;

    if (!SDL_WaitEvent(&event)) {
        const char* error = SDL_GetError();
        if (!error || strcmp(error, "") == 0) {
            // https://github.com/libsdl-org/SDL/issues/5780
            // Sometimes SDL will return without actually having hit an error condition;
            // just ignore it in this case.
            return;
        }

        LOG_CRITICAL(Frontend, "SDL_WaitEvent failed: {}", error);
        exit(1);
    }

    switch (event.type) {
    case SDL_WINDOWEVENT:
        switch (event.window.event) {
        case SDL_WINDOWEVENT_SIZE_CHANGED:
        case SDL_WINDOWEVENT_RESIZED:
        case SDL_WINDOWEVENT_MAXIMIZED:
        case SDL_WINDOWEVENT_RESTORED:
            OnResize();
            break;
        case SDL_WINDOWEVENT_MINIMIZED:
        case SDL_WINDOWEVENT_EXPOSED:
            is_shown = event.window.event == SDL_WINDOWEVENT_EXPOSED;
            OnResize();
            break;
        case SDL_WINDOWEVENT_CLOSE:
            is_open = false;
            break;
        }
        break;
    case SDL_KEYDOWN:
    case SDL_KEYUP:
        OnKeyEvent(static_cast<int>(event.key.keysym.scancode), event.key.state);
        break;
    case SDL_MOUSEMOTION:
        // ignore if it came from touch
        if (event.button.which != SDL_TOUCH_MOUSEID)
            OnMouseMotion(event.motion.x, event.motion.y);
        break;
    case SDL_MOUSEBUTTONDOWN:
    case SDL_MOUSEBUTTONUP:
        // ignore if it came from touch
        if (event.button.which != SDL_TOUCH_MOUSEID) {
            OnMouseButton(event.button.button, event.button.state, event.button.x, event.button.y);
        }
        break;
    case SDL_FINGERDOWN:
        OnFingerDown(event.tfinger.x, event.tfinger.y,
                     static_cast<std::size_t>(event.tfinger.touchId));
        break;
    case SDL_FINGERMOTION:
        OnFingerMotion(event.tfinger.x, event.tfinger.y,
                       static_cast<std::size_t>(event.tfinger.touchId));
        break;
    case SDL_FINGERUP:
        OnFingerUp();
        break;
    case SDL_QUIT:
        is_open = false;
        break;
    default:
        break;
    }

    const u32 current_time = SDL_GetTicks();
    if (current_time > last_time + 2000) {
        const auto results = system.GetAndResetPerfStats();
        const auto title =
            fmt::format("yuzu {} | {}-{} | FPS: {:.0f} ({:.0f}%)", Common::g_build_fullname,
                        Common::g_scm_branch, Common::g_scm_desc, results.average_game_fps,
                        results.emulation_speed * 100.0);
        SDL_SetWindowTitle(render_window, title.c_str());
        last_time = current_time;
    }
}

// Credits to Samantas5855 and others for this function.
void EmuWindow_SDL2::SetWindowIcon() {
    SDL_RWops* const yuzu_icon_stream = SDL_RWFromConstMem((void*)yuzu_icon, yuzu_icon_size);
    if (yuzu_icon_stream == nullptr) {
        LOG_WARNING(Frontend, "Failed to create yuzu icon stream.");
        return;
    }
    SDL_Surface* const window_icon = SDL_LoadBMP_RW(yuzu_icon_stream, 1);
    if (window_icon == nullptr) {
        LOG_WARNING(Frontend, "Failed to read BMP from stream.");
        return;
    }
    // The icon is attached to the window pointer
    SDL_SetWindowIcon(render_window, window_icon);
    SDL_FreeSurface(window_icon);
}

void EmuWindow_SDL2::OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) {
    SDL_SetWindowMinimumSize(render_window, minimal_size.first, minimal_size.second);
}


code:
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <utility>

#include "core/frontend/emu_window.h"
#include "core/frontend/graphics_context.h"

struct SDL_Window;

namespace Core {
class System;
}

namespace InputCommon {
class InputSubsystem;
enum class MouseButton;
} // namespace InputCommon

class EmuWindow_SDL2 : public Core::Frontend::EmuWindow {
public:
    explicit EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem_, Core::System& system_);
    ~EmuWindow_SDL2();

    /// Whether the window is still open, and a close request hasn't yet been sent
    bool IsOpen() const;

    /// Returns if window is shown (not minimized)
    bool IsShown() const override;

    /// Wait for the next event on the main thread.
    void WaitEvent();

    // Sets the window icon from yuzu.bmp
    void SetWindowIcon();

protected:
    /// Called by WaitEvent when a key is pressed or released.
    void OnKeyEvent(int key, u8 state);

    /// Converts a SDL mouse button into MouseInput mouse button
    InputCommon::MouseButton SDLButtonToMouseButton(u32 button) const;

    /// Translates pixel position to float position
    std::pair<float, float> MouseToTouchPos(s32 touch_x, s32 touch_y) const;

    /// Called by WaitEvent when a mouse button is pressed or released
    void OnMouseButton(u32 button, u8 state, s32 x, s32 y);

    /// Called by WaitEvent when the mouse moves.
    void OnMouseMotion(s32 x, s32 y);

    /// Called by WaitEvent when a finger starts touching the touchscreen
    void OnFingerDown(float x, float y, std::size_t id);

    /// Called by WaitEvent when a finger moves while touching the touchscreen
    void OnFingerMotion(float x, float y, std::size_t id);

    /// Called by WaitEvent when a finger stops touching the touchscreen
    void OnFingerUp();

    /// Called by WaitEvent when any event that may cause the window to be resized occurs
    void OnResize();

    /// Called when users want to hide the mouse cursor
    void ShowCursor(bool show_cursor);

    /// Called when user passes the fullscreen parameter flag
    void Fullscreen();

    /// Called when a configuration change affects the minimal size of the window
    void OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) override;

    /// Is the window still open?
    bool is_open = true;

    /// Is the window being shown?
    bool is_shown = true;

    /// Internal SDL2 render window
    SDL_Window* render_window{};

    /// Keeps track of how often to update the title bar during gameplay
    u32 last_time = 0;

    /// Input subsystem to use with this window.
    InputCommon::InputSubsystem* input_subsystem;

    /// yuzu core instance
    Core::System& system;
};

class DummyContext : public Core::Frontend::GraphicsContext {};


code:
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include <algorithm>
#include <cstdlib>
#include <string>

#define SDL_MAIN_HANDLED
#include <SDL.h>

#include <fmt/format.h>
#include <glad/glad.h>
#include "common/logging/log.h"
#include "common/scm_rev.h"
#include "common/settings.h"
#include "common/string_util.h"
#include "core/core.h"
#include "input_common/main.h"
#include "video_core/renderer_base.h"
#include "yuzu_cmd/emu_window/emu_window_sdl2_gl.h"

class SDLGLContext : public Core::Frontend::GraphicsContext {
public:
    explicit SDLGLContext(SDL_Window* window_) : window{window_} {
        context = SDL_GL_CreateContext(window);
    }

    ~SDLGLContext() {
        DoneCurrent();
        SDL_GL_DeleteContext(context);
    }

    void SwapBuffers() override {
        SDL_GL_SwapWindow(window);
    }

    void MakeCurrent() override {
        if (is_current) {
            return;
        }
        is_current = SDL_GL_MakeCurrent(window, context) == 0;
    }

    void DoneCurrent() override {
        if (!is_current) {
            return;
        }
        SDL_GL_MakeCurrent(window, nullptr);
        is_current = false;
    }

private:
    SDL_Window* window;
    SDL_GLContext context;
    bool is_current = false;
};

bool EmuWindow_SDL2_GL::SupportsRequiredGLExtensions() {
    std::vector<std::string_view> unsupported_ext;

    // Extensions required to support some texture formats.
    if (!GLAD_GL_EXT_texture_compression_s3tc) {
        unsupported_ext.push_back("EXT_texture_compression_s3tc");
    }
    if (!GLAD_GL_ARB_texture_compression_rgtc) {
        unsupported_ext.push_back("ARB_texture_compression_rgtc");
    }

    for (const auto& extension : unsupported_ext) {
        LOG_CRITICAL(Frontend, "Unsupported GL extension: {}", extension);
    }

    return unsupported_ext.empty();
}

EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsystem_,
                                     Core::System& system_, bool fullscreen)
    : EmuWindow_SDL2{input_subsystem_, system_} {
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 6);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);
    SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
    SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
    SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
    SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
    SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0);
    SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1);
    if (Settings::values.renderer_debug) {
        SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG);
    }
    SDL_GL_SetSwapInterval(0);

    std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_fullname,
                                           Common::g_scm_branch, Common::g_scm_desc);
    render_window =
        SDL_CreateWindow(window_title.c_str(),
                         SDL_WINDOWPOS_UNDEFINED, // x position
                         SDL_WINDOWPOS_UNDEFINED, // y position
                         Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height,
                         SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);

    if (render_window == nullptr) {
        LOG_CRITICAL(Frontend, "Failed to create SDL2 window! {}", SDL_GetError());
        exit(1);
    }

    strict_context_required = strcmp(SDL_GetCurrentVideoDriver(), "wayland") == 0;

    SetWindowIcon();

    if (fullscreen) {
        Fullscreen();
        ShowCursor(false);
    }

    window_context = SDL_GL_CreateContext(render_window);
    core_context = CreateSharedContext();

    if (window_context == nullptr) {
        LOG_CRITICAL(Frontend, "Failed to create SDL2 GL context: {}", SDL_GetError());
        exit(1);
    }
    if (core_context == nullptr) {
        LOG_CRITICAL(Frontend, "Failed to create shared SDL2 GL context: {}", SDL_GetError());
        exit(1);
    }

    if (!gladLoadGLLoader(static_cast<GLADloadproc>(SDL_GL_GetProcAddress))) {
        LOG_CRITICAL(Frontend, "Failed to initialize GL functions! {}", SDL_GetError());
        exit(1);
    }

    if (!SupportsRequiredGLExtensions()) {
        LOG_CRITICAL(Frontend, "GPU does not support all required OpenGL extensions! Exiting...");
        exit(1);
    }

    OnResize();
    OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
    SDL_PumpEvents();
    LOG_INFO(Frontend, "yuzu Version: {} | {}-{}", Common::g_build_fullname, Common::g_scm_branch,
             Common::g_scm_desc);
    Settings::LogSettings();
}

EmuWindow_SDL2_GL::~EmuWindow_SDL2_GL() {
    core_context.reset();
    SDL_GL_DeleteContext(window_context);
}

std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_GL::CreateSharedContext() const {
    return std::make_unique<SDLGLContext>(render_window);
}


code:
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <memory>
#include "core/frontend/emu_window.h"
#include "yuzu_cmd/emu_window/emu_window_sdl2.h"

namespace Core {
class System;
}

namespace InputCommon {
class InputSubsystem;
}

class EmuWindow_SDL2_GL final : public EmuWindow_SDL2 {
public:
    explicit EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsystem_, Core::System& system_,
                               bool fullscreen);
    ~EmuWindow_SDL2_GL();

    std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;

private:
    /// Whether the GPU and driver supports the OpenGL extension required
    bool SupportsRequiredGLExtensions();

    using SDL_GLContext = void*;

    /// The OpenGL context associated with the window
    SDL_GLContext window_context;

    /// The OpenGL context associated with the core
    std::unique_ptr<Core::Frontend::GraphicsContext> core_context;
};


code:
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include <cstdlib>
#include <memory>
#include <string>

#include <fmt/format.h>

#include "common/logging/log.h"
#include "common/scm_rev.h"
#include "video_core/renderer_null/renderer_null.h"
#include "yuzu_cmd/emu_window/emu_window_sdl2_null.h"

#ifdef YUZU_USE_EXTERNAL_SDL2
// Include this before SDL.h to prevent the external from including a dummy
#define USING_GENERATED_CONFIG_H
#include <SDL_config.h>
#endif

#include <SDL.h>

EmuWindow_SDL2_Null::EmuWindow_SDL2_Null(InputCommon::InputSubsystem* input_subsystem_,
                                         Core::System& system_, bool fullscreen)
    : EmuWindow_SDL2{input_subsystem_, system_} {
    const std::string window_title = fmt::format("yuzu {} | {}-{} (Vulkan)", Common::g_build_name,
                                                 Common::g_scm_branch, Common::g_scm_desc);
    render_window =
        SDL_CreateWindow(window_title.c_str(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
                         Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height,
                         SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);

    SetWindowIcon();

    if (fullscreen) {
        Fullscreen();
        ShowCursor(false);
    }

    OnResize();
    OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
    SDL_PumpEvents();
    LOG_INFO(Frontend, "yuzu Version: {} | {}-{} (Null)", Common::g_build_name,
             Common::g_scm_branch, Common::g_scm_desc);
}

EmuWindow_SDL2_Null::~EmuWindow_SDL2_Null() = default;

std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_Null::CreateSharedContext() const {
    return std::make_unique<DummyContext>();
}


code:
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <memory>

#include "core/frontend/emu_window.h"
#include "yuzu_cmd/emu_window/emu_window_sdl2.h"

namespace Core {
class System;
}

namespace InputCommon {
class InputSubsystem;
}

class EmuWindow_SDL2_Null final : public EmuWindow_SDL2 {
public:
    explicit EmuWindow_SDL2_Null(InputCommon::InputSubsystem* input_subsystem_,
                                 Core::System& system, bool fullscreen);
    ~EmuWindow_SDL2_Null() override;

    std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
};


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include <cstdlib>
#include <memory>
#include <string>

#include <fmt/format.h>

#include "common/logging/log.h"
#include "common/scm_rev.h"
#include "video_core/renderer_vulkan/renderer_vulkan.h"
#include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h"

#include <SDL.h>
#include <SDL_syswm.h>

EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem_,
                                     Core::System& system_, bool fullscreen)
    : EmuWindow_SDL2{input_subsystem_, system_} {
    const std::string window_title = fmt::format("yuzu {} | {}-{} (Vulkan)", Common::g_build_name,
                                                 Common::g_scm_branch, Common::g_scm_desc);
    render_window =
        SDL_CreateWindow(window_title.c_str(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
                         Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height,
                         SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);

    SDL_SysWMinfo wm;
    SDL_VERSION(&wm.version);
    if (SDL_GetWindowWMInfo(render_window, &wm) == SDL_FALSE) {
        LOG_CRITICAL(Frontend, "Failed to get information from the window manager: {}",
                     SDL_GetError());
        std::exit(EXIT_FAILURE);
    }

    SetWindowIcon();

    if (fullscreen) {
        Fullscreen();
        ShowCursor(false);
    }

    switch (wm.subsystem) {
#ifdef SDL_VIDEO_DRIVER_WINDOWS
    case SDL_SYSWM_TYPE::SDL_SYSWM_WINDOWS:
        window_info.type = Core::Frontend::WindowSystemType::Windows;
        window_info.render_surface = reinterpret_cast<void*>(wm.info.win.window);
        break;
#endif
#ifdef SDL_VIDEO_DRIVER_X11
    case SDL_SYSWM_TYPE::SDL_SYSWM_X11:
        window_info.type = Core::Frontend::WindowSystemType::X11;
        window_info.display_connection = wm.info.x11.display;
        window_info.render_surface = reinterpret_cast<void*>(wm.info.x11.window);
        break;
#endif
#ifdef SDL_VIDEO_DRIVER_WAYLAND
    case SDL_SYSWM_TYPE::SDL_SYSWM_WAYLAND:
        window_info.type = Core::Frontend::WindowSystemType::Wayland;
        window_info.display_connection = wm.info.wl.display;
        window_info.render_surface = wm.info.wl.surface;
        break;
#endif
#ifdef SDL_VIDEO_DRIVER_COCOA
    case SDL_SYSWM_TYPE::SDL_SYSWM_COCOA:
        window_info.type = Core::Frontend::WindowSystemType::Cocoa;
        window_info.render_surface = SDL_Metal_CreateView(render_window);
        break;
#endif
#ifdef SDL_VIDEO_DRIVER_ANDROID
    case SDL_SYSWM_TYPE::SDL_SYSWM_ANDROID:
        window_info.type = Core::Frontend::WindowSystemType::Android;
        window_info.render_surface = reinterpret_cast<void*>(wm.info.android.window);
        break;
#endif
    default:
        LOG_CRITICAL(Frontend, "Window manager subsystem {} not implemented", wm.subsystem);
        std::exit(EXIT_FAILURE);
        break;
    }

    OnResize();
    OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
    SDL_PumpEvents();
    LOG_INFO(Frontend, "yuzu Version: {} | {}-{} (Vulkan)", Common::g_build_name,
             Common::g_scm_branch, Common::g_scm_desc);
}

EmuWindow_SDL2_VK::~EmuWindow_SDL2_VK() = default;

std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_VK::CreateSharedContext() const {
    return std::make_unique<DummyContext>();
}


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <memory>

#include "core/frontend/emu_window.h"
#include "yuzu_cmd/emu_window/emu_window_sdl2.h"

namespace Core {
class System;
}

namespace InputCommon {
class InputSubsystem;
}

class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 {
public:
    explicit EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem_, Core::System& system,
                               bool fullscreen);
    ~EmuWindow_SDL2_VK() override;

    std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
};


code:
// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "common/common_precompiled_headers.h"