Qt/input: add shortcut for toggling emulated mouse and keyboard

This commit is contained in:
Megamouse 2024-08-12 20:31:06 +02:00
parent ff84e7c6e2
commit ff6a4bb72d
21 changed files with 192 additions and 29 deletions

View File

@ -312,6 +312,8 @@ void KeyboardHandlerBase::ReleaseAllKeys()
{
consumer.ReleaseAllKeys();
}
m_keys_released = true;
}
void keyboard_consumer::ReleaseAllKeys()

View File

@ -169,5 +169,6 @@ public:
protected:
void ReleaseAllKeys();
bool m_keys_released = false;
std::unordered_map<keyboard_consumer::identifier, keyboard_consumer> m_consumers;
};

View File

@ -4,18 +4,36 @@
#include "MouseHandler.h"
#include "Input/pad_thread.h"
#include "Emu/IdManager.h"
#include "Emu/RSX/Overlays/overlay_message.h"
#include "Emu/system_config.h"
LOG_CHANNEL(input_log, "Input");
template <>
void fmt_class_string<input::active_mouse_and_keyboard>::format(std::string& out, u64 arg)
{
format_enum(out, arg, [](input::active_mouse_and_keyboard value)
{
switch (value)
{
case input::active_mouse_and_keyboard::emulated: return "emulated";
case input::active_mouse_and_keyboard::pad: return "pad";
}
return unknown;
});
}
namespace input
{
atomic_t<active_mouse_and_keyboard> g_active_mouse_and_keyboard{active_mouse_and_keyboard::emulated};
atomic_t<bool> g_pads_intercepted{false};
atomic_t<bool> g_keyboards_intercepted{false};
atomic_t<bool> g_mice_intercepted{false};
void SetIntercepted(bool pads_intercepted, bool keyboards_intercepted, bool mice_intercepted, const char* func)
{
input_log.warning("SetIntercepted: pads=%d, keyboards=%d, mice=%d, src=%s)", pads_intercepted, keyboards_intercepted, mice_intercepted, func);
input_log.notice("SetIntercepted: pads=%d, keyboards=%d, mice=%d, src=%s)", pads_intercepted, keyboards_intercepted, mice_intercepted, func);
g_pads_intercepted = pads_intercepted;
g_keyboards_intercepted = keyboards_intercepted;
@ -38,4 +56,34 @@ namespace input
{
SetIntercepted(all_intercepted, all_intercepted, all_intercepted, func);
}
static void show_mouse_and_keyboard_overlay()
{
if (!g_cfg.misc.show_mouse_and_keyboard_toggle_hint)
return;
const localized_string_id id = g_active_mouse_and_keyboard == active_mouse_and_keyboard::emulated
? localized_string_id::RSX_OVERLAYS_MOUSE_AND_KEYBOARD_EMULATED
: localized_string_id::RSX_OVERLAYS_MOUSE_AND_KEYBOARD_PAD;
rsx::overlays::queue_message(get_localized_string(id), 3'000'000);
}
void set_mouse_and_keyboard(active_mouse_and_keyboard device)
{
// Always log
input_log.notice("set_mouse_and_keyboard: device=%s", device);
if (g_active_mouse_and_keyboard != device)
{
g_active_mouse_and_keyboard = device;
show_mouse_and_keyboard_overlay();
}
}
void toggle_mouse_and_keyboard()
{
g_active_mouse_and_keyboard = g_active_mouse_and_keyboard == active_mouse_and_keyboard::emulated ? active_mouse_and_keyboard::pad : active_mouse_and_keyboard::emulated;
input_log.notice("toggle_mouse_and_keyboard: device=%s", g_active_mouse_and_keyboard.load());
show_mouse_and_keyboard_overlay();
}
}

View File

@ -4,10 +4,20 @@
namespace input
{
enum class active_mouse_and_keyboard : u32
{
pad,
emulated
};
extern atomic_t<active_mouse_and_keyboard> g_active_mouse_and_keyboard;
extern atomic_t<bool> g_pads_intercepted;
extern atomic_t<bool> g_keyboards_intercepted;
extern atomic_t<bool> g_mice_intercepted;
void SetIntercepted(bool pads_intercepted, bool keyboards_intercepted, bool mice_intercepted, const char* func = __builtin_FUNCTION());
void SetIntercepted(bool all_intercepted, const char* func = __builtin_FUNCTION());
void set_mouse_and_keyboard(active_mouse_and_keyboard device);
void toggle_mouse_and_keyboard();
}

View File

@ -35,6 +35,8 @@ enum class localized_string_id
RSX_OVERLAYS_PRESSURE_INTENSITY_TOGGLED_ON,
RSX_OVERLAYS_ANALOG_LIMITER_TOGGLED_OFF,
RSX_OVERLAYS_ANALOG_LIMITER_TOGGLED_ON,
RSX_OVERLAYS_MOUSE_AND_KEYBOARD_EMULATED,
RSX_OVERLAYS_MOUSE_AND_KEYBOARD_PAD,
CELL_GAME_ERROR_BROKEN_GAMEDATA,
CELL_GAME_ERROR_BROKEN_HDDGAME,

View File

@ -342,6 +342,7 @@ struct cfg_root : cfg::node
cfg::_bool show_ppu_compilation_hint{ this, "Show PPU compilation hint", true, true };
cfg::_bool show_pressure_intensity_toggle_hint{ this, "Show pressure intensity toggle hint", true, true };
cfg::_bool show_analog_limiter_toggle_hint{ this, "Show analog limiter toggle hint", true, true };
cfg::_bool show_mouse_and_keyboard_toggle_hint{ this, "Show mouse and keyboard toggle hint", true, true };
cfg::_bool use_native_interface{ this, "Use native user interface", true };
cfg::string gdb_server{ this, "GDB Server", "127.0.0.1:2345" };
cfg::_bool silence_all_logs{ this, "Silence All Logs", false, true };

View File

@ -60,11 +60,22 @@ void basic_keyboard_handler::SetTargetWindow(QWindow* target)
bool basic_keyboard_handler::eventFilter(QObject* watched, QEvent* event)
{
if (!event)
if (!event) [[unlikely]]
{
return false;
}
if (input::g_active_mouse_and_keyboard != input::active_mouse_and_keyboard::emulated)
{
if (!m_keys_released)
{
ReleaseAllKeys();
}
return false;
}
m_keys_released = false;
// !m_target is for future proofing when gsrender isn't automatically initialized on load.
// !m_target->isVisible() is a hack since currently a guiless application will STILL inititialize a gsrender (providing a valid target)
if (!m_target || !m_target->isVisible() || watched == m_target)
@ -97,7 +108,7 @@ bool basic_keyboard_handler::eventFilter(QObject* watched, QEvent* event)
void basic_keyboard_handler::keyPressEvent(QKeyEvent* keyEvent)
{
if (!keyEvent)
if (!keyEvent) [[unlikely]]
{
return;
}
@ -118,7 +129,7 @@ void basic_keyboard_handler::keyPressEvent(QKeyEvent* keyEvent)
void basic_keyboard_handler::keyReleaseEvent(QKeyEvent* keyEvent)
{
if (!keyEvent)
if (!keyEvent) [[unlikely]]
{
return;
}
@ -141,7 +152,7 @@ void basic_keyboard_handler::keyReleaseEvent(QKeyEvent* keyEvent)
// key() only shows the modifiers and the modified key (e.g. no easy way of knowing that - was pressed in 'SHIFT+-' in order to get _)
s32 basic_keyboard_handler::getUnmodifiedKey(QKeyEvent* keyEvent)
{
if (!keyEvent)
if (!keyEvent) [[unlikely]]
{
return -1;
}

View File

@ -86,7 +86,12 @@ bool basic_mouse_handler::eventFilter(QObject* target, QEvent* ev)
return false;
}
if (!ev)
if (!ev) [[unlikely]]
{
return false;
}
if (input::g_active_mouse_and_keyboard != input::active_mouse_and_keyboard::emulated)
{
return false;
}
@ -123,7 +128,10 @@ bool basic_mouse_handler::eventFilter(QObject* target, QEvent* ev)
void basic_mouse_handler::MouseButtonDown(QMouseEvent* event)
{
if (!event) return;
if (!event) [[unlikely]]
{
return;
}
const int button = event->button();
if (const auto it = std::find_if(m_buttons.cbegin(), m_buttons.cend(), [button](const auto& entry){ return entry.second == button; });
@ -135,7 +143,10 @@ void basic_mouse_handler::MouseButtonDown(QMouseEvent* event)
void basic_mouse_handler::MouseButtonUp(QMouseEvent* event)
{
if (!event) return;
if (!event) [[unlikely]]
{
return;
}
const int button = event->button();
if (const auto it = std::find_if(m_buttons.cbegin(), m_buttons.cend(), [button](const auto& entry){ return entry.second == button; });
@ -147,7 +158,10 @@ void basic_mouse_handler::MouseButtonUp(QMouseEvent* event)
void basic_mouse_handler::MouseScroll(QWheelEvent* event)
{
if (!event) return;
if (!event) [[unlikely]]
{
return;
}
const QPoint delta = event->angleDelta();
const s8 x = std::clamp(delta.x() / 120, -128, 127);
@ -177,7 +191,10 @@ int basic_mouse_handler::get_mouse_button(const cfg::string& button)
void basic_mouse_handler::MouseMove(QMouseEvent* event)
{
if (!event) return;
if (!event) [[unlikely]]
{
return;
}
if (is_time_for_update())
{

View File

@ -2,6 +2,7 @@
#include "pad_thread.h"
#include "Emu/Io/pad_config.h"
#include "Emu/Io/KeyboardHandler.h"
#include "Emu/Io/interception.h"
#include "Input/product_info.h"
#include "rpcs3qt/gs_frame.h"
@ -324,10 +325,28 @@ void keyboard_pad_handler::release_all_keys()
pad.m_sticks[i].m_value = 128;
}
}
m_keys_released = true;
}
bool keyboard_pad_handler::eventFilter(QObject* target, QEvent* ev)
{
if (!ev) [[unlikely]]
{
return false;
}
if (input::g_active_mouse_and_keyboard != input::active_mouse_and_keyboard::pad)
{
if (!m_keys_released)
{
release_all_keys();
}
return false;
}
m_keys_released = false;
// !m_target is for future proofing when gsrender isn't automatically initialized on load.
// !m_target->isVisible() is a hack since currently a guiless application will STILL inititialize a gsrender (providing a valid target)
if (!m_target || !m_target->isVisible()|| target == m_target)
@ -381,6 +400,11 @@ void keyboard_pad_handler::SetTargetWindow(QWindow* target)
void keyboard_pad_handler::processKeyEvent(QKeyEvent* event, bool pressed)
{
if (!event) [[unlikely]]
{
return;
}
if (event->isAutoRepeat())
{
event->ignore();
@ -444,6 +468,11 @@ void keyboard_pad_handler::processKeyEvent(QKeyEvent* event, bool pressed)
void keyboard_pad_handler::keyPressEvent(QKeyEvent* event)
{
if (!event) [[unlikely]]
{
return;
}
if (event->modifiers() & Qt::AltModifier)
{
switch (event->key())
@ -502,12 +531,22 @@ void keyboard_pad_handler::keyReleaseEvent(QKeyEvent* event)
void keyboard_pad_handler::mousePressEvent(QMouseEvent* event)
{
if (!event) [[unlikely]]
{
return;
}
Key(event->button(), true);
event->ignore();
}
void keyboard_pad_handler::mouseReleaseEvent(QMouseEvent* event)
{
if (!event) [[unlikely]]
{
return;
}
Key(event->button(), false, 0);
event->ignore();
}
@ -521,7 +560,7 @@ bool keyboard_pad_handler::get_mouse_lock_state() const
void keyboard_pad_handler::mouseMoveEvent(QMouseEvent* event)
{
if (!m_mouse_move_used)
if (!m_mouse_move_used || !event)
{
event->ignore();
return;
@ -659,7 +698,7 @@ void keyboard_pad_handler::mouseMoveEvent(QMouseEvent* event)
void keyboard_pad_handler::mouseWheelEvent(QWheelEvent* event)
{
if (!m_mouse_wheel_used)
if (!m_mouse_wheel_used || !event)
{
return;
}

View File

@ -105,6 +105,7 @@ protected:
private:
QWindow* m_target = nullptr;
mouse_movement_mode m_mouse_movement_mode = mouse_movement_mode::relative;
bool m_keys_released = false;
bool m_mouse_move_used = false;
bool m_mouse_wheel_used = false;
bool get_mouse_lock_state() const;

View File

@ -16,6 +16,7 @@
#endif
#include "keyboard_pad_handler.h"
#include "Emu/Io/Null/NullPadHandler.h"
#include "Emu/Io/interception.h"
#include "Emu/Io/PadHandler.h"
#include "Emu/Io/pad_config.h"
#include "Emu/System.h"
@ -200,6 +201,9 @@ void pad_thread::Init()
|| (pad->m_class_type >= CELL_PAD_FAKE_TYPE_FIRST && pad->m_class_type < CELL_PAD_FAKE_TYPE_LAST);
connect_usb_controller(i, input::get_product_by_vid_pid(pad->m_vendor_id, pad->m_product_id));
}
// Initialize active mouse and keyboard. Activate pad handler if one exists.
input::set_mouse_and_keyboard(m_handlers.contains(pad_handler::keyboard) ? input::active_mouse_and_keyboard::pad : input::active_mouse_and_keyboard::emulated);
}
void pad_thread::SetRumble(const u32 pad, u8 large_motor, bool small_motor)

View File

@ -432,7 +432,7 @@ void raw_mouse_handler::register_raw_input_devices()
m_registered_raw_input_devices = true;
}
void raw_mouse_handler::unregister_raw_input_devices()
void raw_mouse_handler::unregister_raw_input_devices() const
{
if (!m_registered_raw_input_devices)
{

View File

@ -108,7 +108,7 @@ private:
#ifdef _WIN32
void register_raw_input_devices();
void unregister_raw_input_devices();
void unregister_raw_input_devices() const;
bool m_registered_raw_input_devices = false;
#endif

View File

@ -175,6 +175,7 @@ enum class emu_settings_type
ShowPPUCompilationHint,
ShowPressureIntensityToggleHint,
ShowAnalogLimiterToggleHint,
ShowMouseAndKeyboardToggleHint,
WindowTitleFormat,
PauseDuringHomeMenu,
@ -366,6 +367,7 @@ inline static const QMap<emu_settings_type, cfg_location> settings_location =
{ emu_settings_type::ShowPPUCompilationHint, { "Miscellaneous", "Show PPU compilation hint"}},
{ emu_settings_type::ShowPressureIntensityToggleHint, { "Miscellaneous", "Show pressure intensity toggle hint"}},
{ emu_settings_type::ShowAnalogLimiterToggleHint, { "Miscellaneous", "Show analog limiter toggle hint"}},
{ emu_settings_type::ShowMouseAndKeyboardToggleHint, { "Miscellaneous", "Show mouse and keyboard toggle hint"}},
{ emu_settings_type::SilenceAllLogs, { "Miscellaneous", "Silence All Logs" }},
{ emu_settings_type::WindowTitleFormat, { "Miscellaneous", "Window Title Format" }},
{ emu_settings_type::PauseDuringHomeMenu, { "Miscellaneous", "Pause Emulation During Home Menu" }},

View File

@ -16,6 +16,7 @@
#include "Emu/Cell/lv2/sys_rsxaudio.h"
#include "Emu/RSX/rsx_utils.h"
#include "Emu/RSX/Overlays/overlay_message.h"
#include "Emu/Io/interception.h"
#include "Emu/Io/recording_config.h"
#include <QApplication>
@ -295,7 +296,10 @@ void gs_frame::keyPressEvent(QKeyEvent *keyEvent)
}
case Qt::Key_F11:
{
handle_shortcut(gui::shortcuts::shortcut::gw_toggle_recording, {});
if (keyEvent->modifiers() == Qt::ControlModifier)
handle_shortcut(gui::shortcuts::shortcut::gw_toggle_mouse_and_keyboard, {});
else
handle_shortcut(gui::shortcuts::shortcut::gw_toggle_recording, {});
break;
}
case Qt::Key_F12:
@ -421,6 +425,11 @@ void gs_frame::handle_shortcut(gui::shortcuts::shortcut shortcut_key, const QKey
gui_log.warning("%s boost mode", g_disable_frame_limit.load() ? "Enabled" : "Disabled");
break;
}
case gui::shortcuts::shortcut::gw_toggle_mouse_and_keyboard:
{
input::toggle_mouse_and_keyboard();
break;
}
default:
{
break;

View File

@ -69,6 +69,8 @@ private:
case localized_string_id::RSX_OVERLAYS_PRESSURE_INTENSITY_TOGGLED_ON: return tr("Pressure intensity mode of player %0 enabled", "Pressure intensity toggled on").arg(std::forward<Args>(args)...);
case localized_string_id::RSX_OVERLAYS_ANALOG_LIMITER_TOGGLED_OFF: return tr("Analog limiter of player %0 disabled", "Analog limiter toggled off").arg(std::forward<Args>(args)...);
case localized_string_id::RSX_OVERLAYS_ANALOG_LIMITER_TOGGLED_ON: return tr("Analog limiter of player %0 enabled", "Analog limiter toggled on").arg(std::forward<Args>(args)...);
case localized_string_id::RSX_OVERLAYS_MOUSE_AND_KEYBOARD_EMULATED: return tr("Mouse and keyboard are now used as emulated devices.", "Mouse and keyboard emulated");
case localized_string_id::RSX_OVERLAYS_MOUSE_AND_KEYBOARD_PAD: return tr("Mouse and keyboard are now used as pad.", "Mouse and keyboard pad");
case localized_string_id::CELL_GAME_ERROR_BROKEN_GAMEDATA: return tr("ERROR: Game data is corrupted. The application will continue.", "Game Error");
case localized_string_id::CELL_GAME_ERROR_BROKEN_HDDGAME: return tr("ERROR: HDD boot game is corrupted. The application will continue.", "Game Error");
case localized_string_id::CELL_GAME_ERROR_BROKEN_EXIT_GAMEDATA: return tr("ERROR: Game data is corrupted. The application will be terminated.", "Game Error");

View File

@ -1817,6 +1817,9 @@ settings_dialog::settings_dialog(std::shared_ptr<gui_settings> gui_settings, std
m_emu_settings->EnhanceCheckBox(ui->showAnalogLimiterToggleHint, emu_settings_type::ShowAnalogLimiterToggleHint);
SubscribeTooltip(ui->showAnalogLimiterToggleHint, tooltips.settings.show_analog_limiter_toggle_hint);
m_emu_settings->EnhanceCheckBox(ui->showMouseAndKeyboardToggleHint, emu_settings_type::ShowMouseAndKeyboardToggleHint);
SubscribeTooltip(ui->showMouseAndKeyboardToggleHint, tooltips.settings.show_mouse_and_keyboard_toggle_hint);
m_emu_settings->EnhanceCheckBox(ui->pauseDuringHomeMenu, emu_settings_type::PauseDuringHomeMenu);
SubscribeTooltip(ui->pauseDuringHomeMenu, tooltips.settings.pause_during_home_menu);

View File

@ -2277,17 +2277,17 @@
</item>
<item>
<widget class="QGroupBox" name="gb_psnCountryBox">
<property name="title">
<string>Country</string>
</property>
<layout class="QVBoxLayout" name="gb_psnCountryBox_layout">
<item>
<widget class="QComboBox" name="psnCountryBox"/>
</item>
</layout>
</widget>
</item>
<item>
<property name="title">
<string>Country</string>
</property>
<layout class="QVBoxLayout" name="gb_psnCountryBox_layout">
<item>
<widget class="QComboBox" name="psnCountryBox"/>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="networkTabSpacerRight">
<property name="orientation">
<enum>Qt::Vertical</enum>
@ -2993,6 +2993,13 @@
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="showMouseAndKeyboardToggleHint">
<property name="text">
<string>Show mouse and keyboard toggle hint</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="startGameFullscreen">
<property name="text">

View File

@ -27,8 +27,9 @@ void fmt_class_string<gui::shortcuts::shortcut>::format(std::string& out, u64 ar
case shortcut::gw_restart: return "gw_restart";
case shortcut::gw_rsx_capture: return "gw_rsx_capture";
case shortcut::gw_frame_limit: return "gw_frame_limit";
case shortcut::gw_toggle_mouse_and_keyboard: return "gw_toggle_mouse_and_keyboard";
case shortcut::count: return "count";
};
}
return unknown;
});
@ -53,7 +54,8 @@ shortcut_settings::shortcut_settings()
{ shortcut::gw_savestate, shortcut_info{ "game_window_savestate", tr("Savestate"), "Ctrl+S", shortcut_handler_id::game_window } },
{ shortcut::gw_restart, shortcut_info{ "game_window_restart", tr("Restart"), "Ctrl+R", shortcut_handler_id::game_window } },
{ shortcut::gw_rsx_capture, shortcut_info{ "game_window_rsx_capture", tr("RSX Capture"), "Alt+C", shortcut_handler_id::game_window } },
{ shortcut::gw_frame_limit, shortcut_info{ "game_window_gw_frame_limit", tr("Toggle Framelimit"), "Ctrl+F10", shortcut_handler_id::game_window } },
{ shortcut::gw_frame_limit, shortcut_info{ "game_window_frame_limit", tr("Toggle Framelimit"), "Ctrl+F10", shortcut_handler_id::game_window } },
{ shortcut::gw_toggle_mouse_and_keyboard, shortcut_info{ "game_window_toggle_mouse_and_keyboard", tr("Toggle Keyboard"), "Ctrl+F11", shortcut_handler_id::game_window } },
})
{
}

View File

@ -35,6 +35,7 @@ namespace gui
gw_restart,
gw_rsx_capture,
gw_frame_limit,
gw_toggle_mouse_and_keyboard,
count
};

View File

@ -142,6 +142,7 @@ public:
const QString show_ppu_compilation_hint = tr("Shows 'Compiling PPU modules' hint using the native overlay.");
const QString show_pressure_intensity_toggle_hint = tr("Shows pressure intensity toggle hint using the native overlay.");
const QString show_analog_limiter_toggle_hint = tr("Shows analog limiter toggle hint using the native overlay.");
const QString show_mouse_and_keyboard_toggle_hint = tr("Shows mouse and keyboard toggle hint using the native overlay.");
const QString use_native_interface = tr("Enables use of native HUD within the game window that can interact with game controllers.\nWhen disabled, regular Qt dialogs are used instead.\nCurrently, the on-screen keyboard only supports the English key layout.");
const QString pause_during_home_menu = tr("When enabled, opening the home menu will also pause emulation.\nWhile most games pause themselves while the home menu is shown, some do not.\nIn that case it can be helpful to pause the emulation whenever the home menu is open.");
@ -247,7 +248,7 @@ public:
const QString dns_swap = tr("DNS Swap List.\nOnly available in custom configurations.");
const QString bind = tr("Interface IP Address to bind to.\nOnly available in custom configurations.");
const QString enable_upnp = tr("Enable UPNP.\nThis will automatically forward ports bound on 0.0.0.0 if your router has UPNP enabled.");
const QString psn_country = tr("Changes the RPCN country.");
const QString psn_country = tr("Changes the RPCN country.");
// system