From d6575455eedf8fc4dbe4bd1b8101361f4257d0c4 Mon Sep 17 00:00:00 2001 From: goeiecool9999 <7033575+goeiecool9999@users.noreply.github.com> Date: Thu, 17 Oct 2024 22:24:01 +0200 Subject: [PATCH 1/4] Linux: Fix crash on invalid command-line arguments use std::cout instead of wxMessageBox which does not work when wxWidgets has not been initialised yet --- src/config/LaunchSettings.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/config/LaunchSettings.cpp b/src/config/LaunchSettings.cpp index bf38b9cf..32a069c6 100644 --- a/src/config/LaunchSettings.cpp +++ b/src/config/LaunchSettings.cpp @@ -199,7 +199,11 @@ bool LaunchSettings::HandleCommandline(const std::vector& args) std::string errorMsg; errorMsg.append("Error while trying to parse command line parameter:\n"); errorMsg.append(ex.what()); +#if BOOST_OS_WINDOWS wxMessageBox(errorMsg, "Parameter error", wxICON_ERROR); +#else + std::cout << errorMsg << std::endl; +#endif return false; } From f9a4b2dbb1ae8de07223a3d988b1605b941f35a8 Mon Sep 17 00:00:00 2001 From: goeiecool9999 <7033575+goeiecool9999@users.noreply.github.com> Date: Sat, 19 Oct 2024 01:56:56 +0200 Subject: [PATCH 2/4] input: Add option to make `show screen` button a toggle (#1383) --- src/Cafe/CafeSystem.cpp | 2 +- src/Cafe/HW/Latte/Core/Latte.h | 2 +- src/Cafe/HW/Latte/Core/LatteRenderTarget.cpp | 47 ++++++++------------ src/gui/input/panels/VPADInputPanel.cpp | 24 +++++++++- src/gui/input/panels/VPADInputPanel.h | 3 ++ src/input/emulated/VPADController.cpp | 11 +++++ src/input/emulated/VPADController.h | 6 +++ 7 files changed, 64 insertions(+), 31 deletions(-) diff --git a/src/Cafe/CafeSystem.cpp b/src/Cafe/CafeSystem.cpp index 958a5a57..51de3550 100644 --- a/src/Cafe/CafeSystem.cpp +++ b/src/Cafe/CafeSystem.cpp @@ -396,7 +396,7 @@ void cemu_initForGame() // replace any known function signatures with our HLE implementations and patch bugs in the games GamePatch_scan(); } - LatteGPUState.alwaysDisplayDRC = ActiveSettings::DisplayDRCEnabled(); + LatteGPUState.isDRCPrimary = ActiveSettings::DisplayDRCEnabled(); InfoLog_PrintActiveSettings(); Latte_Start(); // check for debugger entrypoint bp diff --git a/src/Cafe/HW/Latte/Core/Latte.h b/src/Cafe/HW/Latte/Core/Latte.h index e8cb2be4..e5e9dd5c 100644 --- a/src/Cafe/HW/Latte/Core/Latte.h +++ b/src/Cafe/HW/Latte/Core/Latte.h @@ -52,7 +52,7 @@ struct LatteGPUState_t uint32 gx2InitCalled; // incremented every time GX2Init() is called // OpenGL control uint32 glVendor; // GLVENDOR_* - bool alwaysDisplayDRC = false; + bool isDRCPrimary = false; // temporary (replace with proper solution later) bool tvBufferUsesSRGB; bool drcBufferUsesSRGB; diff --git a/src/Cafe/HW/Latte/Core/LatteRenderTarget.cpp b/src/Cafe/HW/Latte/Core/LatteRenderTarget.cpp index 60124c02..ca6a2a4d 100644 --- a/src/Cafe/HW/Latte/Core/LatteRenderTarget.cpp +++ b/src/Cafe/HW/Latte/Core/LatteRenderTarget.cpp @@ -989,8 +989,6 @@ void LatteRenderTarget_copyToBackbuffer(LatteTextureView* textureView, bool isPa g_renderer->ImguiEnd(); } -bool ctrlTabHotkeyPressed = false; - void LatteRenderTarget_itHLECopyColorBufferToScanBuffer(MPTR colorBufferPtr, uint32 colorBufferWidth, uint32 colorBufferHeight, uint32 colorBufferSliceIndex, uint32 colorBufferFormat, uint32 colorBufferPitch, Latte::E_HWTILEMODE colorBufferTilemode, uint32 colorBufferSwizzle, uint32 renderTarget) { cemu_assert_debug(colorBufferSliceIndex == 0); // todo - support for non-zero slice @@ -1000,38 +998,31 @@ void LatteRenderTarget_itHLECopyColorBufferToScanBuffer(MPTR colorBufferPtr, uin return; } + auto getVPADScreenActive = [](size_t n) -> std::pair { + auto controller = InputManager::instance().get_vpad_controller(n); + if (!controller) + return {false,false}; + auto pressed = controller->is_screen_active(); + auto toggle = controller->is_screen_active_toggle(); + return {pressed && !toggle, pressed && toggle}; + }; + const bool tabPressed = gui_isKeyDown(PlatformKeyCodes::TAB); const bool ctrlPressed = gui_isKeyDown(PlatformKeyCodes::LCONTROL); + const auto [vpad0Active, vpad0Toggle] = getVPADScreenActive(0); + const auto [vpad1Active, vpad1Toggle] = getVPADScreenActive(1); - bool showDRC = swkbd_hasKeyboardInputHook() == false && tabPressed; - bool& alwaysDisplayDRC = LatteGPUState.alwaysDisplayDRC; + const bool altScreenRequested = (!ctrlPressed && tabPressed) || vpad0Active || vpad1Active; + const bool togglePressed = (ctrlPressed && tabPressed) || vpad0Toggle || vpad1Toggle; + static bool togglePressedLast = false; - if (ctrlPressed && tabPressed) - { - if (ctrlTabHotkeyPressed == false) - { - alwaysDisplayDRC = !alwaysDisplayDRC; - ctrlTabHotkeyPressed = true; - } - } - else - ctrlTabHotkeyPressed = false; + bool& isDRCPrimary = LatteGPUState.isDRCPrimary; - if (alwaysDisplayDRC) - showDRC = !tabPressed; + if(togglePressed && !togglePressedLast) + isDRCPrimary = !isDRCPrimary; + togglePressedLast = togglePressed; - if (!showDRC) - { - auto controller = InputManager::instance().get_vpad_controller(0); - if (controller && controller->is_screen_active()) - showDRC = true; - if (!showDRC) - { - controller = InputManager::instance().get_vpad_controller(1); - if (controller && controller->is_screen_active()) - showDRC = true; - } - } + bool showDRC = swkbd_hasKeyboardInputHook() == false && (isDRCPrimary ^ altScreenRequested); if ((renderTarget & RENDER_TARGET_DRC) && g_renderer->IsPadWindowActive()) LatteRenderTarget_copyToBackbuffer(texView, true); diff --git a/src/gui/input/panels/VPADInputPanel.cpp b/src/gui/input/panels/VPADInputPanel.cpp index fbcdfde4..9e6d75d6 100644 --- a/src/gui/input/panels/VPADInputPanel.cpp +++ b/src/gui/input/panels/VPADInputPanel.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include "gui/helpers/wxControlObject.h" @@ -131,11 +132,23 @@ VPADInputPanel::VPADInputPanel(wxWindow* parent) } // Blow Mic - row = 9; + row = 8; add_button_row(main_sizer, row, column, VPADController::kButtonId_Mic, _("blow mic")); row++; add_button_row(main_sizer, row, column, VPADController::kButtonId_Screen, _("show screen")); + row++; + + auto toggleScreenText = new wxStaticText(this, wxID_ANY, _("toggle screen")); + main_sizer->Add(toggleScreenText, + wxGBPosition(row, column), + wxDefaultSpan, + wxALL | wxALIGN_CENTER_VERTICAL, 5); + m_togglePadViewCheckBox = new wxCheckBox(this, wxID_ANY, {}, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); + wxString toggleScreenTT = _("Makes the \"show screen\" button toggle between the TV and gamepad screens"); + m_togglePadViewCheckBox->SetToolTip(toggleScreenTT); + toggleScreenText->SetToolTip(toggleScreenTT); + main_sizer->Add(m_togglePadViewCheckBox, wxGBPosition(row,column+1), wxDefaultSpan, wxALL | wxEXPAND, 5); ////////////////////////////////////////////////////////////////// @@ -168,6 +181,8 @@ void VPADInputPanel::on_timer(const EmulatedControllerPtr& emulated_controller, { InputPanel::on_timer(emulated_controller, controller_base); + static_cast(emulated_controller.get())->set_screen_toggle(m_togglePadViewCheckBox->GetValue()); + if(emulated_controller) { const auto axis = emulated_controller->get_axis(); @@ -182,3 +197,10 @@ void VPADInputPanel::OnVolumeChange(wxCommandEvent& event) { } +void VPADInputPanel::load_controller(const EmulatedControllerPtr& controller) +{ + InputPanel::load_controller(controller); + + const bool isToggle = static_cast(controller.get())->is_screen_active_toggle(); + m_togglePadViewCheckBox->SetValue(isToggle); +} diff --git a/src/gui/input/panels/VPADInputPanel.h b/src/gui/input/panels/VPADInputPanel.h index 3513bbf7..477385f4 100644 --- a/src/gui/input/panels/VPADInputPanel.h +++ b/src/gui/input/panels/VPADInputPanel.h @@ -4,6 +4,7 @@ #include "gui/input/panels/InputPanel.h" class wxInputDraw; +class wxCheckBox; class VPADInputPanel : public InputPanel { @@ -11,11 +12,13 @@ public: VPADInputPanel(wxWindow* parent); void on_timer(const EmulatedControllerPtr& emulated_controller, const ControllerPtr& controller) override; + virtual void load_controller(const EmulatedControllerPtr& controller) override; private: void OnVolumeChange(wxCommandEvent& event); wxInputDraw* m_left_draw, * m_right_draw; + wxCheckBox* m_togglePadViewCheckBox; void add_button_row(wxGridBagSizer *sizer, sint32 row, sint32 column, const VPADController::ButtonId &button_id); void add_button_row(wxGridBagSizer *sizer, sint32 row, sint32 column, const VPADController::ButtonId &button_id, const wxString &label); diff --git a/src/input/emulated/VPADController.cpp b/src/input/emulated/VPADController.cpp index aeb56395..f1ab1bc4 100644 --- a/src/input/emulated/VPADController.cpp +++ b/src/input/emulated/VPADController.cpp @@ -686,3 +686,14 @@ bool VPADController::set_default_mapping(const std::shared_ptr& return mapping_updated; } + +void VPADController::load(const pugi::xml_node& node) +{ + if (const auto value = node.child("toggle_display")) + m_screen_active_toggle = ConvertString(value.child_value()); +} + +void VPADController::save(pugi::xml_node& node) +{ + node.append_child("toggle_display").append_child(pugi::node_pcdata).set_value(fmt::format("{}", (int)m_screen_active_toggle).c_str()); +} diff --git a/src/input/emulated/VPADController.h b/src/input/emulated/VPADController.h index 6aef16ae..937062da 100644 --- a/src/input/emulated/VPADController.h +++ b/src/input/emulated/VPADController.h @@ -66,6 +66,8 @@ public: bool is_mic_active() { return m_mic_active; } bool is_screen_active() { return m_screen_active; } + bool is_screen_active_toggle() { return m_screen_active_toggle; } + void set_screen_toggle(bool toggle) {m_screen_active_toggle = toggle;} static std::string_view get_button_name(ButtonId id); @@ -86,9 +88,13 @@ public: bool set_default_mapping(const std::shared_ptr& controller) override; + void load(const pugi::xml_node& node) override; + void save(pugi::xml_node& node) override; + private: bool m_mic_active = false; bool m_screen_active = false; + bool m_screen_active_toggle = false; uint32be m_last_holdvalue = 0; std::chrono::high_resolution_clock::time_point m_last_hold_change{}, m_last_pulse{}; From 63e1289bb518562ae62033389610a1e772e63c8b Mon Sep 17 00:00:00 2001 From: capitalistspz Date: Fri, 25 Oct 2024 17:48:21 +0100 Subject: [PATCH 3/4] Windows: Save icons to Cemu user data directory (#1390) --- src/gui/components/wxGameList.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/gui/components/wxGameList.cpp b/src/gui/components/wxGameList.cpp index eedfde5d..6cbb5859 100644 --- a/src/gui/components/wxGameList.cpp +++ b/src/gui/components/wxGameList.cpp @@ -1392,7 +1392,6 @@ void wxGameList::CreateShortcut(GameInfo2& gameInfo) const auto outputPath = shortcutDialog.GetPath(); std::optional icon_path = std::nullopt; - [&]() { int iconIdx; int smallIconIdx; @@ -1402,15 +1401,13 @@ void wxGameList::CreateShortcut(GameInfo2& gameInfo) return; } const auto icon = m_image_list->GetIcon(iconIdx); - PWSTR localAppData; - const auto hres = SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, NULL, &localAppData); - wxBitmap bitmap{}; - auto folder = fs::path(localAppData) / "Cemu" / "icons"; - if (!SUCCEEDED(hres) || (!fs::exists(folder) && !fs::create_directories(folder))) + const auto folder = ActiveSettings::GetUserDataPath("icons"); + if (!fs::exists(folder) && !fs::create_directories(folder)) { cemuLog_log(LogType::Force, "Failed to create icon directory"); return; } + wxBitmap bitmap{}; if (!bitmap.CopyFromIcon(icon)) { cemuLog_log(LogType::Force, "Failed to copy icon"); @@ -1426,7 +1423,7 @@ void wxGameList::CreateShortcut(GameInfo2& gameInfo) icon_path = std::nullopt; cemuLog_log(LogType::Force, "Icon failed to save"); } - }(); + } IShellLinkW* shellLink; HRESULT hres = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLink, reinterpret_cast(&shellLink)); From 459fd5d9bb4897381c4d7d326302ca707c7b818c Mon Sep 17 00:00:00 2001 From: goeiecool9999 <7033575+goeiecool9999@users.noreply.github.com> Date: Mon, 28 Oct 2024 09:37:30 +0100 Subject: [PATCH 4/4] input: Fix crash when closing add controller dialog before search completes (#1386) --- src/gui/input/InputAPIAddWindow.cpp | 43 ++++++++++++++++++++++------- src/gui/input/InputAPIAddWindow.h | 9 ++++++ 2 files changed, 42 insertions(+), 10 deletions(-) diff --git a/src/gui/input/InputAPIAddWindow.cpp b/src/gui/input/InputAPIAddWindow.cpp index a6d1f1a9..688ee14e 100644 --- a/src/gui/input/InputAPIAddWindow.cpp +++ b/src/gui/input/InputAPIAddWindow.cpp @@ -114,6 +114,11 @@ InputAPIAddWindow::InputAPIAddWindow(wxWindow* parent, const wxPoint& position, this->Bind(wxControllersRefreshed, &InputAPIAddWindow::on_controllers_refreshed, this); } +InputAPIAddWindow::~InputAPIAddWindow() +{ + discard_thread_result(); +} + void InputAPIAddWindow::on_add_button(wxCommandEvent& event) { const auto selection = m_input_api->GetSelection(); @@ -159,6 +164,8 @@ std::unique_ptr InputAPIAddWindow::get_settings() co void InputAPIAddWindow::on_api_selected(wxCommandEvent& event) { + discard_thread_result(); + if (m_input_api->GetSelection() == wxNOT_FOUND) return; @@ -239,19 +246,25 @@ void InputAPIAddWindow::on_controller_dropdown(wxCommandEvent& event) m_controller_list->Append(_("Searching for controllers..."), (wxClientData*)nullptr); m_controller_list->SetSelection(wxNOT_FOUND); - std::thread([this, provider, selected_uuid]() + m_search_thread_data = std::make_unique(); + std::thread([this, provider, selected_uuid](std::shared_ptr data) { auto available_controllers = provider->get_controllers(); - wxCommandEvent event(wxControllersRefreshed); - event.SetEventObject(m_controller_list); - event.SetClientObject(new wxCustomData(std::move(available_controllers))); - event.SetInt(provider->api()); - event.SetString(selected_uuid); - wxPostEvent(this, event); - - m_search_running = false; - }).detach(); + { + std::lock_guard lock{data->mutex}; + if(!data->discardResult) + { + wxCommandEvent event(wxControllersRefreshed); + event.SetEventObject(m_controller_list); + event.SetClientObject(new wxCustomData(std::move(available_controllers))); + event.SetInt(provider->api()); + event.SetString(selected_uuid); + wxPostEvent(this, event); + m_search_running = false; + } + } + }, m_search_thread_data).detach(); } void InputAPIAddWindow::on_controller_selected(wxCommandEvent& event) @@ -301,3 +314,13 @@ void InputAPIAddWindow::on_controllers_refreshed(wxCommandEvent& event) } } } + +void InputAPIAddWindow::discard_thread_result() +{ + m_search_running = false; + if(m_search_thread_data) + { + std::lock_guard lock{m_search_thread_data->mutex}; + m_search_thread_data->discardResult = true; + } +} diff --git a/src/gui/input/InputAPIAddWindow.h b/src/gui/input/InputAPIAddWindow.h index ebee0592..085dd623 100644 --- a/src/gui/input/InputAPIAddWindow.h +++ b/src/gui/input/InputAPIAddWindow.h @@ -19,6 +19,7 @@ class InputAPIAddWindow : public wxDialog { public: InputAPIAddWindow(wxWindow* parent, const wxPoint& position, const std::vector& controllers); + ~InputAPIAddWindow(); bool is_valid() const { return m_type.has_value() && m_controller != nullptr; } InputAPI::Type get_type() const { return m_type.value(); } @@ -38,6 +39,8 @@ private: void on_controller_selected(wxCommandEvent& event); void on_controllers_refreshed(wxCommandEvent& event); + void discard_thread_result(); + wxChoice* m_input_api; wxComboBox* m_controller_list; wxButton* m_ok_button; @@ -50,4 +53,10 @@ private: std::vector m_controllers; std::atomic_bool m_search_running = false; + struct AsyncThreadData + { + std::atomic_bool discardResult = false; + std::mutex mutex; + }; + std::shared_ptr m_search_thread_data; };