diff options
| -rw-r--r-- | src/common/hex_util.h | 3 | ||||
| -rw-r--r-- | src/core/hle/service/filesystem/fsp/fsp_srv.cpp | 9 | ||||
| -rw-r--r-- | src/core/hle/service/glue/time/time_zone_binary.cpp | 1 | ||||
| -rw-r--r-- | src/hid_core/resource_manager.cpp | 88 | ||||
| -rw-r--r-- | src/hid_core/resource_manager.h | 9 | ||||
| -rw-r--r-- | src/hid_core/resources/digitizer/digitizer.cpp | 4 | ||||
| -rw-r--r-- | src/hid_core/resources/digitizer/digitizer.h | 3 | ||||
| -rw-r--r-- | src/hid_core/resources/npad/npad.cpp | 18 | ||||
| -rw-r--r-- | src/hid_core/resources/npad/npad.h | 1 | ||||
| -rw-r--r-- | src/hid_core/resources/unique_pad/unique_pad.cpp | 4 | ||||
| -rw-r--r-- | src/hid_core/resources/unique_pad/unique_pad.h | 3 | ||||
| -rw-r--r-- | src/yuzu/main.cpp | 7 | ||||
| -rw-r--r-- | src/yuzu/multiplayer/lobby.cpp | 13 | ||||
| -rw-r--r-- | src/yuzu/multiplayer/lobby_p.h | 25 | ||||
| -rw-r--r-- | src/yuzu/uisettings.h | 14 | 
15 files changed, 130 insertions, 72 deletions
diff --git a/src/common/hex_util.h b/src/common/hex_util.h index a00904939..618f53152 100644 --- a/src/common/hex_util.h +++ b/src/common/hex_util.h @@ -9,6 +9,7 @@  #include <string>  #include <vector>  #include <fmt/format.h> +#include "common/assert.h"  #include "common/common_types.h"  namespace Common { @@ -29,6 +30,8 @@ namespace Common {  template <std::size_t Size, bool le = false>  [[nodiscard]] constexpr std::array<u8, Size> HexStringToArray(std::string_view str) { +    ASSERT_MSG(Size * 2 <= str.size(), "Invalid string size"); +      std::array<u8, Size> out{};      if constexpr (le) {          for (std::size_t i = 2 * Size - 2; i <= 2 * Size; i -= 2) { diff --git a/src/core/hle/service/filesystem/fsp/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp/fsp_srv.cpp index 5fe534c73..63c2d3a58 100644 --- a/src/core/hle/service/filesystem/fsp/fsp_srv.cpp +++ b/src/core/hle/service/filesystem/fsp/fsp_srv.cpp @@ -115,6 +115,11 @@ private:              if (type->GetName() == "save") {                  for (const auto& save_id : type->GetSubdirectories()) {                      for (const auto& user_id : save_id->GetSubdirectories()) { +                        // Skip non user id subdirectories +                        if (user_id->GetName().size() != 0x20) { +                            continue; +                        } +                          const auto save_id_numeric = stoull_be(save_id->GetName());                          auto user_id_numeric = Common::HexStringToArray<0x10>(user_id->GetName());                          std::reverse(user_id_numeric.begin(), user_id_numeric.end()); @@ -160,6 +165,10 @@ private:              } else if (space == FileSys::SaveDataSpaceId::TemporaryStorage) {                  // Temporary Storage                  for (const auto& user_id : type->GetSubdirectories()) { +                    // Skip non user id subdirectories +                    if (user_id->GetName().size() != 0x20) { +                        continue; +                    }                      for (const auto& title_id : user_id->GetSubdirectories()) {                          if (!title_id->GetFiles().empty() ||                              !title_id->GetSubdirectories().empty()) { diff --git a/src/core/hle/service/glue/time/time_zone_binary.cpp b/src/core/hle/service/glue/time/time_zone_binary.cpp index d5f7ca3d2..18c6abd6b 100644 --- a/src/core/hle/service/glue/time/time_zone_binary.cpp +++ b/src/core/hle/service/glue/time/time_zone_binary.cpp @@ -65,6 +65,7 @@ Result MountTimeZoneBinary(Core::System& system) {          // Validate that the romfs is readable, using invalid firmware keys can cause this to get          // set but the files to be garbage. In that case, we want to hit the next path and          // synthesise them instead. +        g_time_zone_binary_mount_result = ResultSuccess;          Service::PSC::Time::LocationName name{"Etc/GMT"};          if (!IsTimeZoneBinaryValid(name)) {              ResetTimeZoneBinary(); diff --git a/src/hid_core/resource_manager.cpp b/src/hid_core/resource_manager.cpp index 68ce2c7ae..245da582e 100644 --- a/src/hid_core/resource_manager.cpp +++ b/src/hid_core/resource_manager.cpp @@ -52,9 +52,42 @@ ResourceManager::ResourceManager(Core::System& system_,                                   std::shared_ptr<HidFirmwareSettings> settings)      : firmware_settings{settings}, system{system_}, service_context{system_, "hid"} {      applet_resource = std::make_shared<AppletResource>(system); + +    // Register update callbacks +    npad_update_event = Core::Timing::CreateEvent("HID::UpdatePadCallback", +                                                  [this](s64 time, std::chrono::nanoseconds ns_late) +                                                      -> std::optional<std::chrono::nanoseconds> { +                                                      UpdateNpad(ns_late); +                                                      return std::nullopt; +                                                  }); +    default_update_event = Core::Timing::CreateEvent( +        "HID::UpdateDefaultCallback", +        [this](s64 time, +               std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { +            UpdateControllers(ns_late); +            return std::nullopt; +        }); +    mouse_keyboard_update_event = Core::Timing::CreateEvent( +        "HID::UpdateMouseKeyboardCallback", +        [this](s64 time, +               std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { +            UpdateMouseKeyboard(ns_late); +            return std::nullopt; +        }); +    motion_update_event = Core::Timing::CreateEvent( +        "HID::UpdateMotionCallback", +        [this](s64 time, +               std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { +            UpdateMotion(ns_late); +            return std::nullopt; +        });  }  ResourceManager::~ResourceManager() { +    system.CoreTiming().UnscheduleEvent(npad_update_event); +    system.CoreTiming().UnscheduleEvent(default_update_event); +    system.CoreTiming().UnscheduleEvent(mouse_keyboard_update_event); +    system.CoreTiming().UnscheduleEvent(motion_update_event);      system.CoreTiming().UnscheduleEvent(touch_update_event);      input_event->Finalize();  }; @@ -201,6 +234,7 @@ void ResourceManager::InitializeHidCommonSampler() {      debug_pad->SetAppletResource(applet_resource, &shared_mutex);      digitizer->SetAppletResource(applet_resource, &shared_mutex); +    unique_pad->SetAppletResource(applet_resource, &shared_mutex);      keyboard->SetAppletResource(applet_resource, &shared_mutex);      const auto settings = @@ -214,6 +248,14 @@ void ResourceManager::InitializeHidCommonSampler() {      home_button->SetAppletResource(applet_resource, &shared_mutex);      sleep_button->SetAppletResource(applet_resource, &shared_mutex);      capture_button->SetAppletResource(applet_resource, &shared_mutex); + +    system.CoreTiming().ScheduleLoopingEvent(npad_update_ns, npad_update_ns, npad_update_event); +    system.CoreTiming().ScheduleLoopingEvent(default_update_ns, default_update_ns, +                                             default_update_event); +    system.CoreTiming().ScheduleLoopingEvent(mouse_keyboard_update_ns, mouse_keyboard_update_ns, +                                             mouse_keyboard_update_event); +    system.CoreTiming().ScheduleLoopingEvent(motion_update_ns, motion_update_ns, +                                             motion_update_event);  }  void ResourceManager::InitializeTouchScreenSampler() { @@ -465,55 +507,9 @@ IAppletResource::IAppletResource(Core::System& system_, std::shared_ptr<Resource          {0, &IAppletResource::GetSharedMemoryHandle, "GetSharedMemoryHandle"},      };      RegisterHandlers(functions); - -    // Register update callbacks -    npad_update_event = Core::Timing::CreateEvent( -        "HID::UpdatePadCallback", -        [this, resource]( -            s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { -            const auto guard = LockService(); -            resource->UpdateNpad(ns_late); -            return std::nullopt; -        }); -    default_update_event = Core::Timing::CreateEvent( -        "HID::UpdateDefaultCallback", -        [this, resource]( -            s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { -            const auto guard = LockService(); -            resource->UpdateControllers(ns_late); -            return std::nullopt; -        }); -    mouse_keyboard_update_event = Core::Timing::CreateEvent( -        "HID::UpdateMouseKeyboardCallback", -        [this, resource]( -            s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { -            const auto guard = LockService(); -            resource->UpdateMouseKeyboard(ns_late); -            return std::nullopt; -        }); -    motion_update_event = Core::Timing::CreateEvent( -        "HID::UpdateMotionCallback", -        [this, resource]( -            s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { -            const auto guard = LockService(); -            resource->UpdateMotion(ns_late); -            return std::nullopt; -        }); - -    system.CoreTiming().ScheduleLoopingEvent(npad_update_ns, npad_update_ns, npad_update_event); -    system.CoreTiming().ScheduleLoopingEvent(default_update_ns, default_update_ns, -                                             default_update_event); -    system.CoreTiming().ScheduleLoopingEvent(mouse_keyboard_update_ns, mouse_keyboard_update_ns, -                                             mouse_keyboard_update_event); -    system.CoreTiming().ScheduleLoopingEvent(motion_update_ns, motion_update_ns, -                                             motion_update_event);  }  IAppletResource::~IAppletResource() { -    system.CoreTiming().UnscheduleEvent(npad_update_event); -    system.CoreTiming().UnscheduleEvent(default_update_event); -    system.CoreTiming().UnscheduleEvent(mouse_keyboard_update_event); -    system.CoreTiming().UnscheduleEvent(motion_update_event);      resource_manager->FreeAppletResourceId(aruid);  } diff --git a/src/hid_core/resource_manager.h b/src/hid_core/resource_manager.h index 0bfe09511..dc3ff01f8 100644 --- a/src/hid_core/resource_manager.h +++ b/src/hid_core/resource_manager.h @@ -147,6 +147,10 @@ private:      std::shared_ptr<SixAxis> six_axis{nullptr};      std::shared_ptr<SleepButton> sleep_button{nullptr};      std::shared_ptr<UniquePad> unique_pad{nullptr}; +    std::shared_ptr<Core::Timing::EventType> npad_update_event; +    std::shared_ptr<Core::Timing::EventType> default_update_event; +    std::shared_ptr<Core::Timing::EventType> mouse_keyboard_update_event; +    std::shared_ptr<Core::Timing::EventType> motion_update_event;      // TODO: Create these resources      // std::shared_ptr<AudioControl> audio_control{nullptr}; @@ -179,11 +183,6 @@ public:  private:      void GetSharedMemoryHandle(HLERequestContext& ctx); -    std::shared_ptr<Core::Timing::EventType> npad_update_event{nullptr}; -    std::shared_ptr<Core::Timing::EventType> default_update_event{nullptr}; -    std::shared_ptr<Core::Timing::EventType> mouse_keyboard_update_event{nullptr}; -    std::shared_ptr<Core::Timing::EventType> motion_update_event{nullptr}; -      u64 aruid{};      std::shared_ptr<ResourceManager> resource_manager;  }; diff --git a/src/hid_core/resources/digitizer/digitizer.cpp b/src/hid_core/resources/digitizer/digitizer.cpp index cd72fd6e5..5d7dcadfe 100644 --- a/src/hid_core/resources/digitizer/digitizer.cpp +++ b/src/hid_core/resources/digitizer/digitizer.cpp @@ -17,10 +17,6 @@ void Digitizer::OnInit() {}  void Digitizer::OnRelease() {}  void Digitizer::OnUpdate(const Core::Timing::CoreTiming& core_timing) { -    if (!smart_update) { -        return; -    } -      std::scoped_lock shared_lock{*shared_mutex};      const u64 aruid = applet_resource->GetActiveAruid();      auto* data = applet_resource->GetAruidData(aruid); diff --git a/src/hid_core/resources/digitizer/digitizer.h b/src/hid_core/resources/digitizer/digitizer.h index e031a16b0..68b03111c 100644 --- a/src/hid_core/resources/digitizer/digitizer.h +++ b/src/hid_core/resources/digitizer/digitizer.h @@ -20,8 +20,5 @@ public:      // When the controller is requesting an update for the shared memory      void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; - -private: -    bool smart_update{};  };  } // namespace Service::HID diff --git a/src/hid_core/resources/npad/npad.cpp b/src/hid_core/resources/npad/npad.cpp index 1a58eff4a..fe3fdc5cd 100644 --- a/src/hid_core/resources/npad/npad.cpp +++ b/src/hid_core/resources/npad/npad.cpp @@ -102,6 +102,8 @@ Result NPad::Activate(u64 aruid) {          for (std::size_t i = 0; i < 19; ++i) {              WriteEmptyEntry(npad);          } + +        controller.is_active = true;      }      return ResultSuccess; @@ -467,6 +469,13 @@ void NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {              continue;          } +        bool is_set{}; +        npad_resource.IsSupportedNpadStyleSet(is_set, aruid); +        // Wait until style is defined +        if (!is_set) { +            continue; +        } +          for (std::size_t i = 0; i < controller_data[aruid_index].size(); ++i) {              auto& controller = controller_data[aruid_index][i];              controller.shared_memory = @@ -484,6 +493,10 @@ void NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {                  continue;              } +            if (!controller.is_active) { +                continue; +            } +              RequestPadStateUpdate(aruid, controller.device->GetNpadIdType());              auto& pad_state = controller.npad_pad_state;              auto& libnx_state = controller.npad_libnx_state; @@ -592,7 +605,9 @@ void NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {              libnx_state.npad_buttons.raw = pad_state.npad_buttons.raw;              libnx_state.l_stick = pad_state.l_stick;              libnx_state.r_stick = pad_state.r_stick; -            npad->system_ext_lifo.WriteNextEntry(pad_state); +            libnx_state.sampling_number = +                npad->system_ext_lifo.ReadCurrentEntry().state.sampling_number + 1; +            npad->system_ext_lifo.WriteNextEntry(libnx_state);              press_state |= static_cast<u64>(pad_state.npad_buttons.raw);          } @@ -1060,6 +1075,7 @@ void NPad::UnregisterAppletResourceUserId(u64 aruid) {      // TODO: Remove this once abstract pad is emulated properly      const auto aruid_index = npad_resource.GetIndexFromAruid(aruid);      for (auto& controller : controller_data[aruid_index]) { +        controller.is_active = false;          controller.is_connected = false;          controller.shared_memory = nullptr;      } diff --git a/src/hid_core/resources/npad/npad.h b/src/hid_core/resources/npad/npad.h index 4e26ed2e8..c63488346 100644 --- a/src/hid_core/resources/npad/npad.h +++ b/src/hid_core/resources/npad/npad.h @@ -164,6 +164,7 @@ private:          NpadInternalState* shared_memory = nullptr;          Core::HID::EmulatedController* device = nullptr; +        bool is_active{};          bool is_connected{};          // Dual joycons can have only one side connected diff --git a/src/hid_core/resources/unique_pad/unique_pad.cpp b/src/hid_core/resources/unique_pad/unique_pad.cpp index 89fc57269..b2db55c5a 100644 --- a/src/hid_core/resources/unique_pad/unique_pad.cpp +++ b/src/hid_core/resources/unique_pad/unique_pad.cpp @@ -17,10 +17,6 @@ void UniquePad::OnInit() {}  void UniquePad::OnRelease() {}  void UniquePad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { -    if (!smart_update) { -        return; -    } -      const u64 aruid = applet_resource->GetActiveAruid();      auto* data = applet_resource->GetAruidData(aruid); diff --git a/src/hid_core/resources/unique_pad/unique_pad.h b/src/hid_core/resources/unique_pad/unique_pad.h index 674ad1691..4873b7f7e 100644 --- a/src/hid_core/resources/unique_pad/unique_pad.h +++ b/src/hid_core/resources/unique_pad/unique_pad.h @@ -20,8 +20,5 @@ public:      // When the controller is requesting an update for the shared memory      void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; - -private: -    bool smart_update{};  };  } // namespace Service::HID diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 303d84a1f..13381fea8 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -1353,6 +1353,13 @@ void GMainWindow::InitializeHotkeys() {      LinkActionShortcut(ui->action_TAS_Start, QStringLiteral("TAS Start/Stop"), true);      LinkActionShortcut(ui->action_TAS_Record, QStringLiteral("TAS Record"), true);      LinkActionShortcut(ui->action_TAS_Reset, QStringLiteral("TAS Reset"), true); +    LinkActionShortcut(ui->action_View_Lobby, +                       QStringLiteral("Multiplayer Browse Public Game Lobby")); +    LinkActionShortcut(ui->action_Start_Room, QStringLiteral("Multiplayer Create Room")); +    LinkActionShortcut(ui->action_Connect_To_Room, +                       QStringLiteral("Multiplayer Direct Connect to Room")); +    LinkActionShortcut(ui->action_Show_Room, QStringLiteral("Multiplayer Show Current Room")); +    LinkActionShortcut(ui->action_Leave_Room, QStringLiteral("Multiplayer Leave Room"));      static const QString main_window = QStringLiteral("Main Window");      const auto connect_shortcut = [&]<typename Fn>(const QString& action_name, const Fn& function) { diff --git a/src/yuzu/multiplayer/lobby.cpp b/src/yuzu/multiplayer/lobby.cpp index 41692c05b..77ac84295 100644 --- a/src/yuzu/multiplayer/lobby.cpp +++ b/src/yuzu/multiplayer/lobby.cpp @@ -77,16 +77,23 @@ Lobby::Lobby(QWidget* parent, QStandardItemModel* list,      // UI Buttons      connect(ui->refresh_list, &QPushButton::clicked, this, &Lobby::RefreshLobby); +    connect(ui->search, &QLineEdit::textChanged, proxy, &LobbyFilterProxyModel::SetFilterSearch);      connect(ui->games_owned, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterOwned);      connect(ui->hide_empty, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterEmpty);      connect(ui->hide_full, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterFull); -    connect(ui->search, &QLineEdit::textChanged, proxy, &LobbyFilterProxyModel::SetFilterSearch);      connect(ui->room_list, &QTreeView::doubleClicked, this, &Lobby::OnJoinRoom);      connect(ui->room_list, &QTreeView::clicked, this, &Lobby::OnExpandRoom);      // Actions      connect(&room_list_watcher, &QFutureWatcher<AnnounceMultiplayerRoom::RoomList>::finished, this,              &Lobby::OnRefreshLobby); + +    // Load persistent filters after events are connected to make sure they apply +    ui->search->setText( +        QString::fromStdString(UISettings::values.multiplayer_filter_text.GetValue())); +    ui->games_owned->setChecked(UISettings::values.multiplayer_filter_games_owned.GetValue()); +    ui->hide_empty->setChecked(UISettings::values.multiplayer_filter_hide_empty.GetValue()); +    ui->hide_full->setChecked(UISettings::values.multiplayer_filter_hide_full.GetValue());  }  Lobby::~Lobby() = default; @@ -204,6 +211,10 @@ void Lobby::OnJoinRoom(const QModelIndex& source) {      // Save settings      UISettings::values.multiplayer_nickname = ui->nickname->text().toStdString(); +    UISettings::values.multiplayer_filter_text = ui->search->text().toStdString(); +    UISettings::values.multiplayer_filter_games_owned = ui->games_owned->isChecked(); +    UISettings::values.multiplayer_filter_hide_empty = ui->hide_empty->isChecked(); +    UISettings::values.multiplayer_filter_hide_full = ui->hide_full->isChecked();      UISettings::values.multiplayer_ip =          proxy->data(connection_index, LobbyItemHost::HostIPRole).value<QString>().toStdString();      UISettings::values.multiplayer_port = diff --git a/src/yuzu/multiplayer/lobby_p.h b/src/yuzu/multiplayer/lobby_p.h index 068c95aca..398833e7a 100644 --- a/src/yuzu/multiplayer/lobby_p.h +++ b/src/yuzu/multiplayer/lobby_p.h @@ -193,12 +193,29 @@ public:      }      QVariant data(int role) const override { -        if (role != Qt::DisplayRole) { +        switch (role) { +        case Qt::DisplayRole: { +            auto members = data(MemberListRole).toList(); +            return QStringLiteral("%1 / %2 ") +                .arg(QString::number(members.size()), data(MaxPlayerRole).toString()); +        } +        case Qt::ForegroundRole: { +            auto members = data(MemberListRole).toList(); +            auto max_players = data(MaxPlayerRole).toInt(); +            if (members.size() >= max_players) { +                return QBrush(QColor(255, 48, 32)); +            } else if (members.size() == (max_players - 1)) { +                return QBrush(QColor(255, 140, 32)); +            } else if (members.size() == 0) { +                return QBrush(QColor(128, 128, 128)); +            } +            // FIXME: How to return a value that tells Qt not to modify the +            // text color from the default (as if Qt::ForegroundRole wasn't overridden)? +            return QBrush(nullptr); +        } +        default:              return LobbyItem::data(role);          } -        auto members = data(MemberListRole).toList(); -        return QStringLiteral("%1 / %2 ") -            .arg(QString::number(members.size()), data(MaxPlayerRole).toString());      }      bool operator<(const QStandardItem& other) const override { diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h index f9906be33..03e42b930 100644 --- a/src/yuzu/uisettings.h +++ b/src/yuzu/uisettings.h @@ -169,6 +169,13 @@ struct Values {      // 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}; @@ -222,7 +229,7 @@ void RestoreWindowState(std::unique_ptr<QtConfig>& qtConfig);  // 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, 23> default_hotkeys{{ +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}}, @@ -236,6 +243,11 @@ const std::array<Shortcut, 23> default_hotkeys{{      {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}},  | 
