diff --git a/README.md b/README.md
index 8ccf4f12ce..351688be64 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@ The world's first free and open-source PlayStation 3 emulator/debugger, written
You can find some basic information on our [**website**](https://rpcs3.net/). Game info is being populated on the [**Wiki**](https://wiki.rpcs3.net/).
For discussion about this emulator, PS3 emulation, and game compatibility reports, please visit our [**forums**](https://forums.rpcs3.net) and our [**Discord server**](https://discord.gg/RPCS3).
-[**Support Lead Developers Nekotekina and kd-11 on Patreon**](https://www.patreon.com/Nekotekina)
+[**Support the Lead Developers on Patreon**](https://rpcs3.net/patreon)
## Contributing
diff --git a/rpcs3/CMakeLists.txt b/rpcs3/CMakeLists.txt
index fee8fa4327..373017197a 100644
--- a/rpcs3/CMakeLists.txt
+++ b/rpcs3/CMakeLists.txt
@@ -81,7 +81,7 @@ if (NOT ANDROID)
# Unix display manager
if(X11_FOUND)
- target_link_libraries(rpcs3_lib PRIVATE X11::X11)
+ target_link_libraries(rpcs3_lib PRIVATE X11::X11 xkbcommon-x11)
elseif(USE_VULKAN AND UNIX AND NOT WAYLAND_FOUND AND NOT APPLE AND NOT ANDROID)
# Wayland has been checked in 3rdparty/CMakeLists.txt already.
message(FATAL_ERROR "RPCS3 requires either X11 or Wayland (or both) for Vulkan.")
diff --git a/rpcs3/Emu/Cell/Modules/cellKb.cpp b/rpcs3/Emu/Cell/Modules/cellKb.cpp
index 0682d32fc8..adfa849bfb 100644
--- a/rpcs3/Emu/Cell/Modules/cellKb.cpp
+++ b/rpcs3/Emu/Cell/Modules/cellKb.cpp
@@ -381,7 +381,7 @@ error_code cellKbRead(u32 port_no, vm::ptr data)
data->keycode[i] = current_data.buttons[i].m_keyCode;
}
- KbConfig& current_config = consumer.GetConfig(port_no);
+ const KbConfig& current_config = consumer.GetConfig(port_no);
// For single character mode to work properly we need to "flush" the buffer after reading or else we'll constantly get the same key presses with each call.
// Actual key repeats are handled by adding a new key code to the buffer periodically. Key releases are handled in a similar fashion.
diff --git a/rpcs3/Emu/Cell/lv2/sys_usbd.cpp b/rpcs3/Emu/Cell/lv2/sys_usbd.cpp
index b84e3457cf..1362d106b5 100644
--- a/rpcs3/Emu/Cell/lv2/sys_usbd.cpp
+++ b/rpcs3/Emu/Cell/lv2/sys_usbd.cpp
@@ -391,6 +391,17 @@ void usb_handler_thread::perform_scan()
&& desc.idProduct >= entry.id_product_min
&& desc.idProduct <= entry.id_product_max)
{
+#ifdef __APPLE__
+ // On macOS, libusb cannot claim HID interfaces, so passing through a real
+ // controller that also has an emulated implementation yields a non-functional
+ // device and silently overrides the user's emulated-device setting (the emulated
+ // setup below only fills slots not already passed through). Prefer the emulated
+ // implementation whenever the user has enabled it. (e.g. DJ Hero Turntable)
+ if (entry.make_instance && entry.max_device_count && entry.max_device_count() > 0)
+ {
+ continue;
+ }
+#endif
sys_usbd.success("Found device: %s", std::basic_string(entry.device_name));
libusb_ref_device(dev);
std::shared_ptr usb_dev = std::make_shared(dev, desc, get_new_location());
diff --git a/rpcs3/Emu/Io/Dimensions.cpp b/rpcs3/Emu/Io/Dimensions.cpp
index fb3327779d..179be8ef3c 100644
--- a/rpcs3/Emu/Io/Dimensions.cpp
+++ b/rpcs3/Emu/Io/Dimensions.cpp
@@ -530,11 +530,10 @@ bool dimensions_toypad::create_blank_character(std::array& buf,
std::array dimensions_toypad::pwd_generate(const std::array& uid)
{
- std::vector pwd_calc = {PWD_CONSTANT.begin(), PWD_CONSTANT.end() - 1};
- for (u8 i = 0; i < uid.size(); i++)
- {
- pwd_calc.insert(pwd_calc.begin() + i, uid[i]);
- }
+ std::vector pwd_calc;
+ pwd_calc.reserve(uid.size() + PWD_CONSTANT.size());
+ pwd_calc.insert(pwd_calc.end(), uid.begin(), uid.end());
+ pwd_calc.insert(pwd_calc.end(), PWD_CONSTANT.begin(), PWD_CONSTANT.end());
return dimensions_randomize(pwd_calc, 8);
}
diff --git a/rpcs3/Emu/Io/KeyboardHandler.h b/rpcs3/Emu/Io/KeyboardHandler.h
index 594f7be3f8..2a5261ff8b 100644
--- a/rpcs3/Emu/Io/KeyboardHandler.h
+++ b/rpcs3/Emu/Io/KeyboardHandler.h
@@ -128,9 +128,9 @@ public:
KbInfo& GetInfo() { return m_info; }
std::vector& GetKeyboards() { return m_keyboards; }
- KbData& GetData(const u32 keyboard) { return m_keyboards[keyboard].m_data; }
- KbExtraData& GetExtraData(const u32 keyboard) { return m_keyboards[keyboard].m_extra_data; }
- KbConfig& GetConfig(const u32 keyboard) { return m_keyboards[keyboard].m_config; }
+ KbData& GetData(const u32 keyboard) { return ::at32(m_keyboards, keyboard).m_data; }
+ KbExtraData& GetExtraData(const u32 keyboard) { return ::at32(m_keyboards, keyboard).m_extra_data; }
+ KbConfig& GetConfig(const u32 keyboard) { return ::at32(m_keyboards, keyboard).m_config; }
identifier id() const { return m_id; }
void ReleaseAllKeys();
diff --git a/rpcs3/Emu/system_config.h b/rpcs3/Emu/system_config.h
index ea7de326f8..c431ee2f5a 100644
--- a/rpcs3/Emu/system_config.h
+++ b/rpcs3/Emu/system_config.h
@@ -126,7 +126,7 @@ struct cfg_root : cfg::node
cfg::_enum frame_limit{ this, "Frame limit", frame_limit_type::_auto, true };
cfg::_float<0, 1000> second_frame_limit{ this, "Second Frame Limit", 0, true }; // 0 disables its effect
cfg::_enum antialiasing_level{ this, "MSAA", msaa_level::_auto };
- cfg::_enum shadermode{ this, "Shader Mode", shader_mode::async_recompiler };
+ cfg::_enum shadermode{ this, "Shader Mode", shader_mode::async_with_interpreter };
cfg::_enum shader_precision{ this, "Shader Precision", gpu_preset_level::high };
cfg::_enum vsync{ this, "VSync Mode", vsync_mode::off, true };
@@ -366,7 +366,7 @@ struct cfg_root : cfg::node
cfg::_bool prevent_display_sleep{ this, "Prevent display sleep while running games", true, true };
cfg::_bool show_trophy_popups{ this, "Show trophy popups", true, true };
cfg::_bool show_rpcn_popups{ this, "Show RPCN popups", true, true };
- cfg::_bool show_shader_compilation_hint{ this, "Show shader compilation hint", true, true };
+ cfg::_bool show_shader_compilation_hint{ this, "Show shader compilation hint", false, true };
cfg::_bool show_ppu_compilation_hint{ this, "Show PPU compilation hint", true, true };
cfg::_bool show_autosave_autoload_hint{ this, "Show autosave/autoload hint", false, true };
cfg::_bool show_pressure_intensity_toggle_hint{ this, "Show pressure intensity toggle hint", true, true };
diff --git a/rpcs3/Input/basic_keyboard_handler.cpp b/rpcs3/Input/basic_keyboard_handler.cpp
index 3fa7acf876..a40190f3e8 100644
--- a/rpcs3/Input/basic_keyboard_handler.cpp
+++ b/rpcs3/Input/basic_keyboard_handler.cpp
@@ -7,6 +7,11 @@
#ifdef _WIN32
#include "windows.h"
+#elif __linux__
+#include
+#include
+#include
+#include
#endif
LOG_CHANNEL(input_log, "Input");
@@ -44,7 +49,7 @@ void basic_keyboard_handler::Init(keyboard_consumer& consumer, const u32 max_con
/* Sets the target window for the event handler, and also installs an event filter on the target. */
void basic_keyboard_handler::SetTargetWindow(QWindow* target)
{
- if (target != nullptr)
+ if (target)
{
m_target = target;
target->installEventFilter(this);
@@ -119,7 +124,7 @@ void basic_keyboard_handler::keyPressEvent(QKeyEvent* keyEvent)
return;
}
- const int key = getUnmodifiedKey(keyEvent);
+ const int key = get_unmodified_key(keyEvent);
if (key < 0 || !HandleKey(static_cast(key), keyEvent->nativeScanCode(), true, keyEvent->isAutoRepeat(), keyEvent->text().toStdU32String()))
{
@@ -140,7 +145,7 @@ void basic_keyboard_handler::keyReleaseEvent(QKeyEvent* keyEvent)
return;
}
- const int key = getUnmodifiedKey(keyEvent);
+ const int key = get_unmodified_key(keyEvent);
if (key < 0 || !HandleKey(static_cast(key), keyEvent->nativeScanCode(), false, keyEvent->isAutoRepeat(), keyEvent->text().toStdU32String()))
{
@@ -149,8 +154,10 @@ void basic_keyboard_handler::keyReleaseEvent(QKeyEvent* keyEvent)
}
// This should get the actual unmodified key without getting too crazy.
-// 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)
+// key() only shows the modifiers and the modified key.
+// e.g. 'Shift+1' may result in Qt::Key_Exclam, so we lose the information that Qt::Key_1 was pressed.
+// We want to find the actual physical key that was pressed (and return Qt::Key_1 in this example).
+s32 basic_keyboard_handler::get_unmodified_key(QKeyEvent* keyEvent)
{
if (!keyEvent) [[unlikely]]
{
@@ -166,9 +173,9 @@ s32 basic_keyboard_handler::getUnmodifiedKey(QKeyEvent* keyEvent)
u32 raw_key = static_cast(key);
-#ifdef _WIN32
if (keyEvent->modifiers() != Qt::NoModifier && !keyEvent->text().isEmpty())
{
+#ifdef _WIN32
u32 mapped_key = static_cast(MapVirtualKeyA(static_cast(keyEvent->nativeVirtualKey()), MAPVK_VK_TO_CHAR));
if (raw_key != mapped_key)
@@ -179,8 +186,72 @@ s32 basic_keyboard_handler::getUnmodifiedKey(QKeyEvent* keyEvent)
}
raw_key = mapped_key;
}
- }
+#elif __linux__
+ class kb_mapper
+ {
+ public:
+ kb_mapper()
+ {
+ m_ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
+ }
+
+ ~kb_mapper()
+ {
+ if (m_ctx) xkb_context_unref(m_ctx);
+ }
+
+ s32 get_unmodified_key(u32 qt_native_scan_code)
+ {
+ if (!m_ctx) return -1;
+
+ auto* connection = get_connection();
+ if (!connection) return -1;
+
+ const int device_id = xkb_x11_get_core_keyboard_device_id(connection);
+
+ xkb_keymap* keymap = xkb_x11_keymap_new_from_device(m_ctx, connection, device_id, XKB_KEYMAP_COMPILE_NO_FLAGS);
+ if (!keymap) return -1;
+
+ xkb_state* state = xkb_x11_state_new_from_device(keymap, connection, device_id);
+ if (!state)
+ {
+ xkb_keymap_unref(keymap);
+ return -1;
+ }
+
+ const xkb_keycode_t code = static_cast(qt_native_scan_code);
+ const xkb_layout_index_t layout = xkb_state_serialize_layout(state, XKB_STATE_LAYOUT_EFFECTIVE);
+ const xkb_keysym_t* syms = nullptr;
+ const int count = xkb_keymap_key_get_syms_by_level(keymap, code, layout, 0, &syms);
+
+ const auto new_key = (syms && count > 0) ? QXkbCommon::keysymToQtKey(syms[0], Qt::NoModifier, nullptr, code) : -1;
+
+ xkb_state_unref(state);
+ xkb_keymap_unref(keymap);
+
+ return new_key;
+ }
+
+ private:
+ xcb_connection_t* get_connection()
+ {
+ if (!qGuiApp) return nullptr;
+ auto* native_interface = qGuiApp->nativeInterface();
+ return native_interface ? native_interface->connection() : nullptr;
+ }
+
+ xkb_context* m_ctx = nullptr;
+ };
+
+ static kb_mapper mapper = kb_mapper();
+ if (const int res = mapper.get_unmodified_key(keyEvent->nativeScanCode()); res > 0)
+ {
+ raw_key = res;
+ }
+#elif __APPLE__
+ // TODO
#endif
+ }
return static_cast(raw_key);
}
diff --git a/rpcs3/Input/basic_keyboard_handler.h b/rpcs3/Input/basic_keyboard_handler.h
index 284890025f..353d054834 100644
--- a/rpcs3/Input/basic_keyboard_handler.h
+++ b/rpcs3/Input/basic_keyboard_handler.h
@@ -17,7 +17,7 @@ public:
bool eventFilter(QObject* watched, QEvent* event) override;
void keyPressEvent(QKeyEvent* event);
void keyReleaseEvent(QKeyEvent* event);
- static s32 getUnmodifiedKey(QKeyEvent* event);
+ static s32 get_unmodified_key(QKeyEvent* event);
private:
void LoadSettings(Keyboard& keyboard);
diff --git a/rpcs3/Input/sdl_pad_handler.cpp b/rpcs3/Input/sdl_pad_handler.cpp
index 099de9000e..76c1c3a9db 100644
--- a/rpcs3/Input/sdl_pad_handler.cpp
+++ b/rpcs3/Input/sdl_pad_handler.cpp
@@ -341,7 +341,13 @@ SDLDevice::sdl_info sdl_pad_handler::get_sdl_info(SDL_JoystickID id)
const int num_axes = SDL_GetNumJoystickAxes(joystick);
const int num_buttons = SDL_GetNumJoystickButtons(joystick);
- info.is_ds3_with_pressure_buttons = num_axes == 16 && num_buttons == 11;
+ // The DJ Hero Turntable (VID 0x12BA, PID 0x0140) coincidentally matches the
+ // DS3 axis/button counts (16 axes, 11 buttons) but is NOT a pressure-sensitive
+ // DS3. Routing its face buttons through the pressure axes drops the green (Cross)
+ // and blue (Square) deck buttons, so exclude it and read its buttons digitally.
+ const bool is_dj_hero_turntable = info.vid == 0x12BA && info.pid == 0x0140;
+
+ info.is_ds3_with_pressure_buttons = num_axes == 16 && num_buttons == 11 && !is_dj_hero_turntable;
sdl_log.notice("DS3 device %d has %d axis and %d buttons (has_pressure_buttons=%d)", id, num_axes, num_buttons, info.is_ds3_with_pressure_buttons);
diff --git a/rpcs3/rpcs3.metainfo.xml b/rpcs3/rpcs3.metainfo.xml
index 327c90c0f2..3449f39c7f 100644
--- a/rpcs3/rpcs3.metainfo.xml
+++ b/rpcs3/rpcs3.metainfo.xml
@@ -29,7 +29,7 @@ Most of this information can be found on the PlayStation 3 Developer Wiki.
https://github.com/RPCS3/rpcs3/issues
https://rpcs3.net/faq
https://rpcs3.net/quickstart
- https://www.patreon.com/Nekotekina
+ https://rpcs3.net/patreon
https://github.com/RPCS3/rpcs3
https://github.com/RPCS3/rpcs3#contributing