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.
This commit is contained in:
Megamouse 2022-07-05 21:47:05 +02:00
parent c8c035eeef
commit 4823d4c32a
16 changed files with 196 additions and 92 deletions

View File

@ -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<u16>& digital_buttons, be_t<u16>& 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<u16>& 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<u16>& digital_buttons, be_t<u16>& analog_t)
{
if (!is_input_allowed())
{
return false;
}
auto& handler = g_fxo->get<MouseHandlerBase>();
std::scoped_lock lock(handler.mutex);
@ -563,7 +580,7 @@ static bool mouse_input_to_pad(const u32 mouse_no, be_t<u16>& 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<u16>& 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<CellGemImageState>& gem_image_state)
static void mouse_pos_to_gem_image_state(const u32 mouse_no, const gem_config::gem_controller& controller, vm::ptr<CellGemImageState>& gem_image_state)
{
if (!gem_image_state || !is_input_allowed())
{
return;
}
auto& handler = g_fxo->get<MouseHandlerBase>();
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<gem_camera_shared>();
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<CellGemState>& gem_state)
{
if (!is_input_allowed())
{
return false;
}
auto& handler = g_fxo->get<MouseHandlerBase>();
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<gem_camera_shared>();
s32 mouse_width = mouse.x_max;
@ -1077,6 +1102,8 @@ error_code cellGemGetImageState(u32 gem_num, vm::ptr<CellGemImageState> 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<gem_camera_shared>();
@ -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);

View File

@ -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<CellKbData> 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;

View File

@ -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<CellMouseData> 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<CellMouseDataList> 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<CellMouseTabletDataLi
// TODO: decr tests show that CELL_MOUSE_ERROR_DATA_READ_FAILED is returned when a mouse is connected
// TODO: check if (current_info.mode[port_no] != CELL_MOUSE_INFO_TABLET_TABLET_MODE) has any impact
auto& list = handler.GetTabletDataList(port_no);
MouseTabletDataList& list = handler.GetTabletDataList(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;
@ -432,7 +434,7 @@ error_code cellMouseGetRawData(u32 port_no, vm::ptr<CellMouseRawData> 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;

View File

@ -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.

View File

@ -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();

View File

@ -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)
{

View File

@ -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;
};

View File

@ -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))
{

View File

@ -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.

View File

@ -279,6 +279,7 @@ struct cfg_root : cfg::node
cfg::_enum<ghltar_handler> ghltar{this, "GHLtar emulated controller", ghltar_handler::null};
cfg::_enum<pad_handler_mode> 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

View File

@ -21,6 +21,8 @@
LOG_CHANNEL(input_log, "Input");
extern bool is_input_allowed();
namespace pad
{
atomic_t<pad_thread*> 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;

View File

@ -137,6 +137,7 @@ enum class emu_settings_type
MusicHandler,
// Input / Output
BackgroundInput,
PadHandlerMode,
KeyboardHandler,
MouseHandler,
@ -310,6 +311,7 @@ inline static const QMap<emu_settings_type, cfg_location> 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"}},

View File

@ -55,6 +55,13 @@ LOG_CHANNEL(gui_log, "GUI");
extern atomic_t<bool> g_user_asked_for_frame_capture;
extern atomic_t<bool> g_disable_frame_limit;
atomic_t<bool> 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> 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);
});

View File

@ -1176,6 +1176,9 @@ settings_dialog::settings_dialog(std::shared_ptr<gui_settings> 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);
// _____ _ _______ _
// / ____| | | |__ __| | |
// | (___ _ _ ___| |_ ___ _ __ ___ | | __ _| |__

View File

@ -1540,6 +1540,42 @@
<layout class="QVBoxLayout" name="inputTab_layout">
<item>
<layout class="QGridLayout" name="ioGridLayout">
<item row="0" column="2">
<widget class="QGroupBox" name="gb_buzz_emulated">
<property name="title">
<string>Buzz! emulated controller</string>
</property>
<layout class="QVBoxLayout" name="gb_buzz_emulated_layout">
<item>
<widget class="QComboBox" name="buzzBox"/>
</item>
</layout>
</widget>
</item>
<item row="2" column="2">
<widget class="QGroupBox" name="gb_ghltar_emulated">
<property name="title">
<string>Guitar Hero Live emulated guitar</string>
</property>
<layout class="QVBoxLayout" name="gb_ghltar_emulated_layout">
<item>
<widget class="QComboBox" name="ghltarBox"/>
</item>
</layout>
</widget>
</item>
<item row="3" column="1">
<widget class="QGroupBox" name="gb_camera_flip">
<property name="title">
<string>Camera Flip</string>
</property>
<layout class="QVBoxLayout" name="gb_camera_flip_layout">
<item>
<widget class="QComboBox" name="cameraFlipBox"/>
</item>
</layout>
</widget>
</item>
<item row="1" column="2">
<widget class="QGroupBox" name="gb_turntable_emulated">
<property name="title">
@ -1552,6 +1588,30 @@
</layout>
</widget>
</item>
<item row="0" column="0">
<widget class="QGroupBox" name="gb_keyboard_handler">
<property name="title">
<string>Keyboard Handler</string>
</property>
<layout class="QVBoxLayout" name="gb_keyboard_handler_layout">
<item>
<widget class="QComboBox" name="keyboardHandlerBox"/>
</item>
</layout>
</widget>
</item>
<item row="3" column="0">
<widget class="QGroupBox" name="gb_pad_mode">
<property name="title">
<string>Pad Handler Mode</string>
</property>
<layout class="QVBoxLayout" name="gb_pad_mode_layout">
<item>
<widget class="QComboBox" name="padModeBox"/>
</item>
</layout>
</widget>
</item>
<item row="1" column="0">
<widget class="QGroupBox" name="gb_mouse_handler">
<property name="title">
@ -1564,6 +1624,18 @@
</layout>
</widget>
</item>
<item row="2" column="1">
<widget class="QGroupBox" name="gb_camera_id">
<property name="title">
<string>Camera</string>
</property>
<layout class="QVBoxLayout" name="camera_id_layout">
<item>
<widget class="QComboBox" name="cameraIdBox"/>
</item>
</layout>
</widget>
</item>
<item row="0" column="1">
<widget class="QGroupBox" name="gb_camera_type">
<property name="title">
@ -1588,42 +1660,6 @@
</layout>
</widget>
</item>
<item row="0" column="0">
<widget class="QGroupBox" name="gb_keyboard_handler">
<property name="title">
<string>Keyboard Handler</string>
</property>
<layout class="QVBoxLayout" name="gb_keyboard_handler_layout">
<item>
<widget class="QComboBox" name="keyboardHandlerBox"/>
</item>
</layout>
</widget>
</item>
<item row="0" column="2">
<widget class="QGroupBox" name="gb_buzz_emulated">
<property name="title">
<string>Buzz! emulated controller</string>
</property>
<layout class="QVBoxLayout" name="gb_buzz_emulated_layout">
<item>
<widget class="QComboBox" name="buzzBox"/>
</item>
</layout>
</widget>
</item>
<item row="3" column="1">
<widget class="QGroupBox" name="gb_camera_flip">
<property name="title">
<string>Camera Flip</string>
</property>
<layout class="QVBoxLayout" name="gb_camera_flip_layout">
<item>
<widget class="QComboBox" name="cameraFlipBox"/>
</item>
</layout>
</widget>
</item>
<item row="1" column="1">
<widget class="QGroupBox" name="gb_camera_setting">
<property name="title">
@ -1636,38 +1672,18 @@
</layout>
</widget>
</item>
<item row="2" column="2">
<widget class="QGroupBox" name="gb_ghltar_emulated">
<item row="4" column="0">
<widget class="QGroupBox" name="gb_additional_io_settings">
<property name="title">
<string>Guitar Hero Live emulated guitar</string>
<string>Additional Settings</string>
</property>
<layout class="QVBoxLayout" name="gb_ghltar_emulated_layout">
<layout class="QVBoxLayout" name="layout_gb_additional_io_settings">
<item>
<widget class="QComboBox" name="ghltarBox"/>
</item>
</layout>
</widget>
</item>
<item row="2" column="1">
<widget class="QGroupBox" name="gb_camera_id">
<property name="title">
<string>Camera</string>
</property>
<layout class="QVBoxLayout" name="camera_id_layout">
<item>
<widget class="QComboBox" name="cameraIdBox"/>
</item>
</layout>
</widget>
</item>
<item row="3" column="0">
<widget class="QGroupBox" name="gb_pad_mode">
<property name="title">
<string>Pad Handler Mode</string>
</property>
<layout class="QVBoxLayout" name="gb_pad_mode_layout">
<item>
<widget class="QComboBox" name="padModeBox"/>
<widget class="QCheckBox" name="backgroundInputBox">
<property name="text">
<string>Enable Background Input</string>
</property>
</widget>
</item>
</layout>
</widget>

View File

@ -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