From 4823d4c32a4da006dd1e489ebccefb03617dcd20 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Tue, 5 Jul 2022 21:47:05 +0200 Subject: [PATCH] input: add background input option Adds an option to disable background input to the IO tab in the settings dialog. This will disable pad input as well as ps move and overlays input when the window is unfocused. --- rpcs3/Emu/Cell/Modules/cellGem.cpp | 51 ++++++++-- rpcs3/Emu/Cell/Modules/cellKb.cpp | 4 +- rpcs3/Emu/Cell/Modules/cellMouse.cpp | 14 +-- rpcs3/Emu/Cell/Modules/cellPad.cpp | 6 +- rpcs3/Emu/Io/Buzz.cpp | 7 ++ rpcs3/Emu/Io/GHLtar.cpp | 9 ++ rpcs3/Emu/Io/pad_config_types.h | 6 +- rpcs3/Emu/Io/usio.cpp | 7 ++ rpcs3/Emu/RSX/Overlays/overlays.cpp | 17 +++- rpcs3/Emu/system_config.h | 1 + rpcs3/Input/pad_thread.cpp | 6 +- rpcs3/rpcs3qt/emu_settings_type.h | 2 + rpcs3/rpcs3qt/gs_frame.cpp | 8 ++ rpcs3/rpcs3qt/settings_dialog.cpp | 3 + rpcs3/rpcs3qt/settings_dialog.ui | 146 +++++++++++++++------------ rpcs3/rpcs3qt/tooltips.h | 1 + 16 files changed, 196 insertions(+), 92 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellGem.cpp b/rpcs3/Emu/Cell/Modules/cellGem.cpp index ea86d8c286..82115b9de4 100644 --- a/rpcs3/Emu/Cell/Modules/cellGem.cpp +++ b/rpcs3/Emu/Cell/Modules/cellGem.cpp @@ -420,6 +420,8 @@ static bool check_gem_num(const u32 gem_num) return gem_num < CELL_GEM_MAX_NUM; } +extern bool is_input_allowed(); + /** * \brief Maps Move controller data (digital buttons, and analog Trigger data) to DS3 pad input. * Unavoidably buttons conflict with DS3 mappings, which is problematic for some games. @@ -430,15 +432,20 @@ static bool check_gem_num(const u32 gem_num) */ static bool ds3_input_to_pad(const u32 port_no, be_t& digital_buttons, be_t& analog_t) { + if (!is_input_allowed()) + { + return false; + } + std::lock_guard lock(pad::g_pad_mutex); const auto handler = pad::get_current_handler(); - auto& pad = handler->GetPads()[port_no]; + const auto& pad = handler->GetPads().at(port_no); if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) return false; - for (Button& button : pad->m_buttons) + for (const Button& button : pad->m_buttons) { // here we check btns, and set pad accordingly if (button.m_offset == CELL_PAD_BTN_OFFSET_DIGITAL2) @@ -514,10 +521,15 @@ static bool ds3_input_to_pad(const u32 port_no, be_t& digital_buttons, be_t */ static bool ds3_input_to_ext(const u32 port_no, const gem_config::gem_controller& controller, CellGemExtPortData& ext) { + if (!is_input_allowed()) + { + return false; + } + std::lock_guard lock(pad::g_pad_mutex); const auto handler = pad::get_current_handler(); - auto& pad = handler->GetPads()[port_no]; + const auto& pad = handler->GetPads().at(port_no); if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) return false; @@ -554,6 +566,11 @@ static bool ds3_input_to_ext(const u32 port_no, const gem_config::gem_controller */ static bool mouse_input_to_pad(const u32 mouse_no, be_t& digital_buttons, be_t& analog_t) { + if (!is_input_allowed()) + { + return false; + } + auto& handler = g_fxo->get(); std::scoped_lock lock(handler.mutex); @@ -563,7 +580,7 @@ static bool mouse_input_to_pad(const u32 mouse_no, be_t& digital_buttons, b return false; } - const auto& mouse_data = handler.GetMice().at(0); + const auto& mouse_data = handler.GetMice().at(mouse_no); const auto is_pressed = [&mouse_data](MouseButtonCodes button) -> bool { return !!(mouse_data.buttons & button); }; digital_buttons = 0; @@ -597,18 +614,23 @@ static bool mouse_input_to_pad(const u32 mouse_no, be_t& digital_buttons, b return true; } -static bool mouse_pos_to_gem_image_state(const u32 mouse_no, const gem_config::gem_controller& controller, vm::ptr& gem_image_state) +static void mouse_pos_to_gem_image_state(const u32 mouse_no, const gem_config::gem_controller& controller, vm::ptr& gem_image_state) { + if (!gem_image_state || !is_input_allowed()) + { + return; + } + auto& handler = g_fxo->get(); std::scoped_lock lock(handler.mutex); - if (!gem_image_state || mouse_no >= handler.GetMice().size()) + if (mouse_no >= handler.GetMice().size()) { - return false; + return; } - const auto& mouse = handler.GetMice().at(0); + const auto& mouse = handler.GetMice().at(mouse_no); const auto& shared_data = g_fxo->get(); s32 mouse_width = mouse.x_max; @@ -638,12 +660,15 @@ static bool mouse_pos_to_gem_image_state(const u32 mouse_no, const gem_config::g // Projected camera coordinates in mm gem_image_state->projectionx = camera_x / controller.distance; gem_image_state->projectiony = camera_y / controller.distance; - - return true; } static bool mouse_pos_to_gem_state(const u32 mouse_no, const gem_config::gem_controller& controller, vm::ptr& gem_state) { + if (!is_input_allowed()) + { + return false; + } + auto& handler = g_fxo->get(); std::scoped_lock lock(handler.mutex); @@ -653,7 +678,7 @@ static bool mouse_pos_to_gem_state(const u32 mouse_no, const gem_config::gem_con return false; } - const auto& mouse = handler.GetMice().at(0); + const auto& mouse = handler.GetMice().at(mouse_no); const auto& shared_data = g_fxo->get(); s32 mouse_width = mouse.x_max; @@ -1077,6 +1102,8 @@ error_code cellGemGetImageState(u32 gem_num, vm::ptr gem_imag return CELL_GEM_ERROR_INVALID_PARAMETER; } + *gem_image_state = {}; + if (g_cfg.io.move == move_handler::fake || g_cfg.io.move == move_handler::mouse) { auto& shared_data = g_fxo->get(); @@ -1126,6 +1153,8 @@ error_code cellGemGetInertialState(u32 gem_num, u32 state_flag, u64 timestamp, v return CELL_GEM_TIME_OUT_OF_RANGE; } + inertial_state = {}; + if (g_cfg.io.move == move_handler::fake || g_cfg.io.move == move_handler::mouse) { ds3_input_to_ext(gem_num, gem.controllers[gem_num], inertial_state->ext); diff --git a/rpcs3/Emu/Cell/Modules/cellKb.cpp b/rpcs3/Emu/Cell/Modules/cellKb.cpp index 5dcc5cb1cb..d065d9e55f 100644 --- a/rpcs3/Emu/Cell/Modules/cellKb.cpp +++ b/rpcs3/Emu/Cell/Modules/cellKb.cpp @@ -9,6 +9,8 @@ extern void libio_sys_config_init(); extern void libio_sys_config_end(); +extern bool is_input_allowed(); + LOG_CHANNEL(sys_io); template<> @@ -333,7 +335,7 @@ error_code cellKbRead(u32 port_no, vm::ptr data) KbData& current_data = handler.GetData(port_no); - if (current_info.is_null_handler || (current_info.info & CELL_KB_INFO_INTERCEPTED)) + if (current_info.is_null_handler || (current_info.info & CELL_KB_INFO_INTERCEPTED) || !is_input_allowed()) { data->led = 0; data->mkey = 0; diff --git a/rpcs3/Emu/Cell/Modules/cellMouse.cpp b/rpcs3/Emu/Cell/Modules/cellMouse.cpp index 8cc2d366c7..eefa986a34 100644 --- a/rpcs3/Emu/Cell/Modules/cellMouse.cpp +++ b/rpcs3/Emu/Cell/Modules/cellMouse.cpp @@ -10,6 +10,8 @@ extern void libio_sys_config_init(); extern void libio_sys_config_end(); +extern bool is_input_allowed(); + LOG_CHANNEL(sys_io); template<> @@ -236,7 +238,7 @@ error_code cellMouseGetData(u32 port_no, vm::ptr data) MouseDataList& data_list = handler.GetDataList(port_no); - if (data_list.empty() || current_info.is_null_handler || (current_info.info & CELL_MOUSE_INFO_INTERCEPTED)) + if (data_list.empty() || current_info.is_null_handler || (current_info.info & CELL_MOUSE_INFO_INTERCEPTED) || !is_input_allowed()) { data_list.clear(); return CELL_OK; @@ -284,9 +286,9 @@ error_code cellMouseGetDataList(u32 port_no, vm::ptr data) // TODO: check if (current_info.mode[port_no] != CELL_MOUSE_INFO_TABLET_MOUSE_MODE) has any impact - auto& list = handler.GetDataList(port_no); + MouseDataList& list = handler.GetDataList(port_no); - if (list.empty() || current_info.is_null_handler || (current_info.info & CELL_MOUSE_INFO_INTERCEPTED)) + if (list.empty() || current_info.is_null_handler || (current_info.info & CELL_MOUSE_INFO_INTERCEPTED) || !is_input_allowed()) { list.clear(); return CELL_OK; @@ -374,9 +376,9 @@ error_code cellMouseGetTabletDataList(u32 port_no, vm::ptr data) MouseRawData& current_data = handler.GetRawData(port_no); - if (current_info.is_null_handler || (current_info.info & CELL_MOUSE_INFO_INTERCEPTED)) + if (current_info.is_null_handler || (current_info.info & CELL_MOUSE_INFO_INTERCEPTED) || !is_input_allowed()) { current_data = {}; return CELL_OK; diff --git a/rpcs3/Emu/Cell/Modules/cellPad.cpp b/rpcs3/Emu/Cell/Modules/cellPad.cpp index 5174e29006..45eb2db195 100644 --- a/rpcs3/Emu/Cell/Modules/cellPad.cpp +++ b/rpcs3/Emu/Cell/Modules/cellPad.cpp @@ -1,8 +1,8 @@ #include "stdafx.h" #include "Emu/IdManager.h" +#include "Emu/system_config.h" #include "Emu/Cell/PPUModule.h" #include "Emu/Cell/lv2/sys_process.h" - #include "Emu/Io/pad_types.h" #include "Input/pad_thread.h" #include "Input/product_info.h" @@ -11,6 +11,8 @@ extern void libio_sys_config_init(); extern void libio_sys_config_end(); +extern bool is_input_allowed(); + LOG_CHANNEL(sys_io); template<> @@ -197,7 +199,7 @@ void pad_get_data(u32 port_no, CellPadData* data) const auto setting = config.port_setting[port_no]; bool btnChanged = false; - if (rinfo.ignore_input) + if (rinfo.ignore_input || !is_input_allowed()) { // Needed for Hotline Miami and Ninja Gaiden Sigma after dialogs were closed and buttons are still pressed. // Gran Turismo 6 would keep registering the Start button during OSK Dialogs if this wasn't cleared and if we'd return with len as CELL_PAD_LEN_NO_CHANGE. diff --git a/rpcs3/Emu/Io/Buzz.cpp b/rpcs3/Emu/Io/Buzz.cpp index 557c7d03c7..afb3736c78 100644 --- a/rpcs3/Emu/Io/Buzz.cpp +++ b/rpcs3/Emu/Io/Buzz.cpp @@ -43,6 +43,8 @@ void usb_device_buzz::control_transfer(u8 bmRequestType, u8 bRequest, u16 wValue } } +extern bool is_input_allowed(); + void usb_device_buzz::interrupt_transfer(u32 buf_size, u8* buf, u32 /*endpoint*/, UsbTransfer* transfer) { transfer->fake = true; @@ -60,6 +62,11 @@ void usb_device_buzz::interrupt_transfer(u32 buf_size, u8* buf, u32 /*endpoint*/ buf[3] = 0x00; buf[4] = 0xf0; + if (!is_input_allowed()) + { + return; + } + std::lock_guard lock(pad::g_pad_mutex); const auto handler = pad::get_current_handler(); const auto& pads = handler->GetPads(); diff --git a/rpcs3/Emu/Io/GHLtar.cpp b/rpcs3/Emu/Io/GHLtar.cpp index 129715d3b1..f4147d4d48 100644 --- a/rpcs3/Emu/Io/GHLtar.cpp +++ b/rpcs3/Emu/Io/GHLtar.cpp @@ -46,6 +46,8 @@ void usb_device_ghltar::control_transfer(u8 bmRequestType, u8 bRequest, u16 wVal } } +extern bool is_input_allowed(); + void usb_device_ghltar::interrupt_transfer(u32 buf_size, u8* buf, u32 /*endpoint*/, UsbTransfer* transfer) { transfer->fake = true; @@ -98,12 +100,19 @@ void usb_device_ghltar::interrupt_transfer(u32 buf_size, u8* buf, u32 /*endpoint // buf[7] through buf[18] are always 0x00 // buf[21]/[23]/[25] are also always 0x00 + if (!is_input_allowed()) + { + return; + } + std::lock_guard lock(pad::g_pad_mutex); const auto handler = pad::get_current_handler(); const auto& pad = handler->GetPads()[m_controller_index]; if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) + { return; + } for (Button& button : pad->m_buttons) { diff --git a/rpcs3/Emu/Io/pad_config_types.h b/rpcs3/Emu/Io/pad_config_types.h index 500bfc873a..85f3632087 100644 --- a/rpcs3/Emu/Io/pad_config_types.h +++ b/rpcs3/Emu/Io/pad_config_types.h @@ -26,7 +26,7 @@ enum class mouse_movement_mode : s32 struct PadInfo { - u32 now_connect; - u32 system_info; - bool ignore_input; + u32 now_connect = 0; + u32 system_info = 0; + bool ignore_input = false; }; diff --git a/rpcs3/Emu/Io/usio.cpp b/rpcs3/Emu/Io/usio.cpp index 7218dcc564..540278d36d 100644 --- a/rpcs3/Emu/Io/usio.cpp +++ b/rpcs3/Emu/Io/usio.cpp @@ -87,6 +87,8 @@ void usb_device_usio::control_transfer(u8 bmRequestType, u8 bRequest, u16 wValue } } +extern bool is_input_allowed(); + void usb_device_usio::translate_input() { std::lock_guard lock(pad::g_pad_mutex); @@ -98,6 +100,11 @@ void usb_device_usio::translate_input() auto translate_from_pad = [&](u8 pad_number, u8 player) { + if (!is_input_allowed()) + { + return; + } + const auto& pad = handler->GetPads()[pad_number]; if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) { diff --git a/rpcs3/Emu/RSX/Overlays/overlays.cpp b/rpcs3/Emu/RSX/Overlays/overlays.cpp index b39e4f8099..b4a25ae6b0 100644 --- a/rpcs3/Emu/RSX/Overlays/overlays.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlays.cpp @@ -9,6 +9,8 @@ LOG_CHANNEL(overlays); +extern bool is_input_allowed(); + namespace rsx { namespace overlays @@ -120,13 +122,24 @@ namespace rsx while (!exit) { - std::this_thread::sleep_for(1ms); - if (Emu.IsStopped()) + { return selection_code::canceled; + } if (Emu.IsPaused()) + { + thread_ctrl::wait_for(10000); continue; + } + + thread_ctrl::wait_for(1000); + + if (!is_input_allowed()) + { + refresh(); + continue; + } // Get keyboard input if supported by the overlay and activated by the game. // Ignored if a keyboard pad handler is active in order to prevent double input. diff --git a/rpcs3/Emu/system_config.h b/rpcs3/Emu/system_config.h index b0de3df0c7..bc54b606ab 100644 --- a/rpcs3/Emu/system_config.h +++ b/rpcs3/Emu/system_config.h @@ -279,6 +279,7 @@ struct cfg_root : cfg::node cfg::_enum ghltar{this, "GHLtar emulated controller", ghltar_handler::null}; cfg::_enum pad_mode{this, "Pad handler mode", pad_handler_mode::single_threaded, true}; cfg::uint<0, 100'000> pad_sleep{this, "Pad handler sleep (microseconds)", 1'000, true}; + cfg::_bool background_input_enabled{this, "Background input enabled", true, true}; } io{ this }; struct node_sys : cfg::node diff --git a/rpcs3/Input/pad_thread.cpp b/rpcs3/Input/pad_thread.cpp index 8c2c83a663..cccafad18a 100644 --- a/rpcs3/Input/pad_thread.cpp +++ b/rpcs3/Input/pad_thread.cpp @@ -21,6 +21,8 @@ LOG_CHANNEL(input_log, "Input"); +extern bool is_input_allowed(); + namespace pad { atomic_t g_current = nullptr; @@ -251,7 +253,7 @@ void pad_thread::operator()() { while (thread_ctrl::state() != thread_state::aborting) { - if (!pad::g_enabled || Emu.IsPaused()) + if (!pad::g_enabled || Emu.IsPaused() || !is_input_allowed()) { thread_ctrl::wait_for(10'000); continue; @@ -267,7 +269,7 @@ void pad_thread::operator()() while (thread_ctrl::state() != thread_state::aborting) { - if (!pad::g_enabled || Emu.IsPaused()) + if (!pad::g_enabled || Emu.IsPaused() || !is_input_allowed()) { thread_ctrl::wait_for(10000); continue; diff --git a/rpcs3/rpcs3qt/emu_settings_type.h b/rpcs3/rpcs3qt/emu_settings_type.h index 06be279ae0..5bc8dc400b 100644 --- a/rpcs3/rpcs3qt/emu_settings_type.h +++ b/rpcs3/rpcs3qt/emu_settings_type.h @@ -137,6 +137,7 @@ enum class emu_settings_type MusicHandler, // Input / Output + BackgroundInput, PadHandlerMode, KeyboardHandler, MouseHandler, @@ -310,6 +311,7 @@ inline static const QMap settings_location = { emu_settings_type::MusicHandler, { "Audio", "Music Handler"}}, // Input / Output + { emu_settings_type::BackgroundInput, { "Input/Output", "Background input enabled"}}, { emu_settings_type::PadHandlerMode, { "Input/Output", "Pad handler mode"}}, { emu_settings_type::KeyboardHandler, { "Input/Output", "Keyboard"}}, { emu_settings_type::MouseHandler, { "Input/Output", "Mouse"}}, diff --git a/rpcs3/rpcs3qt/gs_frame.cpp b/rpcs3/rpcs3qt/gs_frame.cpp index 1c0127e1b2..124cc2c056 100644 --- a/rpcs3/rpcs3qt/gs_frame.cpp +++ b/rpcs3/rpcs3qt/gs_frame.cpp @@ -55,6 +55,13 @@ LOG_CHANNEL(gui_log, "GUI"); extern atomic_t g_user_asked_for_frame_capture; extern atomic_t g_disable_frame_limit; +atomic_t g_game_window_focused = false; + +bool is_input_allowed() +{ + return g_game_window_focused || g_cfg.io.background_input_enabled; +} + constexpr auto qstr = QString::fromStdString; gs_frame::gs_frame(QScreen* screen, const QRect& geometry, const QIcon& appIcon, std::shared_ptr gui_settings) @@ -115,6 +122,7 @@ gs_frame::gs_frame(QScreen* screen, const QRect& geometry, const QIcon& appIcon, // Change cursor when this window gets or loses focus. connect(this, &QWindow::activeChanged, this, [this]() { + g_game_window_focused = isActive(); handle_cursor(visibility(), false, true); }); diff --git a/rpcs3/rpcs3qt/settings_dialog.cpp b/rpcs3/rpcs3qt/settings_dialog.cpp index f3e62a9175..93b1c54fce 100644 --- a/rpcs3/rpcs3qt/settings_dialog.cpp +++ b/rpcs3/rpcs3qt/settings_dialog.cpp @@ -1176,6 +1176,9 @@ settings_dialog::settings_dialog(std::shared_ptr gui_settings, std m_emu_settings->EnhanceComboBox(ui->ghltarBox, emu_settings_type::GHLtar); SubscribeTooltip(ui->gb_ghltar_emulated, tooltips.settings.ghltar); + m_emu_settings->EnhanceCheckBox(ui->backgroundInputBox, emu_settings_type::BackgroundInput); + SubscribeTooltip(ui->backgroundInputBox, tooltips.settings.background_input); + // _____ _ _______ _ // / ____| | | |__ __| | | // | (___ _ _ ___| |_ ___ _ __ ___ | | __ _| |__ diff --git a/rpcs3/rpcs3qt/settings_dialog.ui b/rpcs3/rpcs3qt/settings_dialog.ui index 835b0ce998..d8cd2b5126 100644 --- a/rpcs3/rpcs3qt/settings_dialog.ui +++ b/rpcs3/rpcs3qt/settings_dialog.ui @@ -1540,6 +1540,42 @@ + + + + Buzz! emulated controller + + + + + + + + + + + + Guitar Hero Live emulated guitar + + + + + + + + + + + + Camera Flip + + + + + + + + @@ -1552,6 +1588,30 @@ + + + + Keyboard Handler + + + + + + + + + + + + Pad Handler Mode + + + + + + + + @@ -1564,6 +1624,18 @@ + + + + Camera + + + + + + + + @@ -1588,42 +1660,6 @@ - - - - Keyboard Handler - - - - - - - - - - - - Buzz! emulated controller - - - - - - - - - - - - Camera Flip - - - - - - - - @@ -1636,38 +1672,18 @@ - - + + - Guitar Hero Live emulated guitar + Additional Settings - + - - - - - - - - - Camera - - - - - - - - - - - - Pad Handler Mode - - - - + + + Enable Background Input + + diff --git a/rpcs3/rpcs3qt/tooltips.h b/rpcs3/rpcs3qt/tooltips.h index 2b38d70367..070db1f21f 100644 --- a/rpcs3/rpcs3qt/tooltips.h +++ b/rpcs3/rpcs3qt/tooltips.h @@ -214,6 +214,7 @@ public: const QString buzz = tr("Buzz! support.\nSelect 1 or 2 controllers if the game requires Buzz! controllers and you don't have real controllers.\nSelect Null if the game has support for DualShock or if you have real Buzz! controllers."); const QString turntable = tr("DJ Hero Turntable controller support.\nSelect 1 or 2 controllers if the game requires DJ Hero Turntable controllers and you don't have real turntable controllers.\nSelect Null if the game has support for DualShock or if you have real turntable controllers.\nA real turntable controller can be used at the same time as an emulated turntable controller."); const QString ghltar = tr("Guitar Hero Live (GHL) Guitar controller support.\nSelect 1 or 2 controllers if the game requires GHL Guitar controllers and you don't have real guitar controllers.\nSelect Null if the game has support for DualShock or if you have real guitar controllers.\nA real guitar controller can be used at the same time as an emulated guitar controller."); + const QString background_input = tr("Allows pad and keyboard input while the game window is unfocused."); // network