From ffd36ea662af02897e9d86291e9765afe3bfa192 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Mon, 4 Apr 2022 22:49:59 +0200 Subject: [PATCH] OSK: handle keyboard input --- rpcs3/Emu/Cell/Modules/cellKb.h | 2 + rpcs3/Emu/RSX/Overlays/overlay_osk.cpp | 119 +++++++++++++++++++++++-- rpcs3/Emu/RSX/Overlays/overlay_osk.h | 1 + rpcs3/Emu/RSX/Overlays/overlays.cpp | 59 ++++++++++-- rpcs3/Emu/RSX/Overlays/overlays.h | 8 +- 5 files changed, 173 insertions(+), 16 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellKb.h b/rpcs3/Emu/Cell/Modules/cellKb.h index b2911edfdc..4f525b86db 100644 --- a/rpcs3/Emu/Cell/Modules/cellKb.h +++ b/rpcs3/Emu/Cell/Modules/cellKb.h @@ -56,3 +56,5 @@ struct CellKbConfig be_t read_mode; be_t code_type; }; + +u16 cellKbCnvRawCode(u32 arrange, u32 mkey, u32 led, u16 rawcode); diff --git a/rpcs3/Emu/RSX/Overlays/overlay_osk.cpp b/rpcs3/Emu/RSX/Overlays/overlay_osk.cpp index a38e8c2101..fb4d248acb 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_osk.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlay_osk.cpp @@ -3,6 +3,7 @@ #include "Emu/RSX/RSXThread.h" #include "Emu/Cell/Modules/cellSysutil.h" #include "Emu/Cell/Modules/cellMsgDialog.h" +#include "Emu/Cell/Modules/cellKb.h" LOG_CHANNEL(osk, "OSK"); @@ -12,11 +13,13 @@ namespace rsx { osk_dialog::osk_dialog() { - auto_repeat_buttons.insert(pad_button::L1); - auto_repeat_buttons.insert(pad_button::R1); - auto_repeat_buttons.insert(pad_button::cross); - auto_repeat_buttons.insert(pad_button::triangle); - auto_repeat_buttons.insert(pad_button::square); + m_auto_repeat_buttons.insert(pad_button::L1); + m_auto_repeat_buttons.insert(pad_button::R1); + m_auto_repeat_buttons.insert(pad_button::cross); + m_auto_repeat_buttons.insert(pad_button::triangle); + m_auto_repeat_buttons.insert(pad_button::square); + + m_keyboard_input_enabled = true; } void osk_dialog::Close(s32 status) @@ -583,6 +586,112 @@ namespace rsx } } + void osk_dialog::on_key_pressed(u32 led, u32 mkey, u32 key_code, u32 out_key_code, bool pressed) + { + if (!pressed) + return; + + osk.notice("osk_dialog::on_key_pressed(led=%d, mkey=%d, key_code=%d, out_key_code=%d, pressed=%d)", led, mkey, key_code, out_key_code, pressed); + + // Get keyboard layout + u32 kb_mapping = CELL_KB_MAPPING_101; + u32 osk_panel_mode = CELL_OSKDIALOG_PANELMODE_DEFAULT; + for (usz i = 0; i < m_panels.size(); ++i) + { + if (m_panel_index == i) + { + osk_panel_mode = m_panels[i].osk_panel_mode; + break; + } + } + + switch (osk_panel_mode) + { + case CELL_OSKDIALOG_PANELMODE_DEFAULT : kb_mapping = CELL_KB_MAPPING_101; break; + case CELL_OSKDIALOG_PANELMODE_GERMAN : kb_mapping = CELL_KB_MAPPING_GERMAN_GERMANY; break; + case CELL_OSKDIALOG_PANELMODE_ENGLISH : kb_mapping = CELL_KB_MAPPING_ENGLISH_UK; break; + case CELL_OSKDIALOG_PANELMODE_SPANISH : kb_mapping = CELL_KB_MAPPING_SPANISH_SPAIN; break; + case CELL_OSKDIALOG_PANELMODE_FRENCH : kb_mapping = CELL_KB_MAPPING_FRENCH_FRANCE; break; + case CELL_OSKDIALOG_PANELMODE_ITALIAN : kb_mapping = CELL_KB_MAPPING_ITALIAN_ITALY; break; + case CELL_OSKDIALOG_PANELMODE_DUTCH : kb_mapping = CELL_KB_MAPPING_DUTCH_NETHERLANDS; break; + case CELL_OSKDIALOG_PANELMODE_PORTUGUESE : kb_mapping = CELL_KB_MAPPING_PORTUGUESE_PORTUGAL; break; + case CELL_OSKDIALOG_PANELMODE_RUSSIAN : kb_mapping = CELL_KB_MAPPING_RUSSIAN_RUSSIA; break; + case CELL_OSKDIALOG_PANELMODE_JAPANESE : kb_mapping = CELL_KB_MAPPING_106; break; + case CELL_OSKDIALOG_PANELMODE_DEFAULT_NO_JAPANESE: kb_mapping = CELL_KB_MAPPING_106; break; + case CELL_OSKDIALOG_PANELMODE_POLISH : kb_mapping = CELL_KB_MAPPING_POLISH_POLAND; break; + case CELL_OSKDIALOG_PANELMODE_KOREAN : kb_mapping = CELL_KB_MAPPING_KOREAN_KOREA; break; + case CELL_OSKDIALOG_PANELMODE_TURKEY : kb_mapping = CELL_KB_MAPPING_TURKISH_TURKEY; break; + case CELL_OSKDIALOG_PANELMODE_TRADITIONAL_CHINESE: kb_mapping = CELL_KB_MAPPING_CHINESE_TRADITIONAL; break; + case CELL_OSKDIALOG_PANELMODE_SIMPLIFIED_CHINESE : kb_mapping = CELL_KB_MAPPING_CHINESE_SIMPLIFIED; break; + case CELL_OSKDIALOG_PANELMODE_PORTUGUESE_BRAZIL : kb_mapping = CELL_KB_MAPPING_PORTUGUESE_BRAZIL; break; + case CELL_OSKDIALOG_PANELMODE_DANISH : kb_mapping = CELL_KB_MAPPING_DANISH_DENMARK; break; + case CELL_OSKDIALOG_PANELMODE_SWEDISH : kb_mapping = CELL_KB_MAPPING_SWEDISH_SWEDEN; break; + case CELL_OSKDIALOG_PANELMODE_NORWEGIAN : kb_mapping = CELL_KB_MAPPING_NORWEGIAN_NORWAY; break; + case CELL_OSKDIALOG_PANELMODE_FINNISH : kb_mapping = CELL_KB_MAPPING_FINNISH_FINLAND; break; + case CELL_OSKDIALOG_PANELMODE_JAPANESE_HIRAGANA : kb_mapping = CELL_KB_MAPPING_106; break; + case CELL_OSKDIALOG_PANELMODE_JAPANESE_KATAKANA : kb_mapping = CELL_KB_MAPPING_106_KANA; break; + case CELL_OSKDIALOG_PANELMODE_ALPHABET_FULL_WIDTH: kb_mapping = CELL_KB_MAPPING_106; break; + case CELL_OSKDIALOG_PANELMODE_ALPHABET : kb_mapping = CELL_KB_MAPPING_101; break; + case CELL_OSKDIALOG_PANELMODE_LATIN : kb_mapping = CELL_KB_MAPPING_101; break; + case CELL_OSKDIALOG_PANELMODE_NUMERAL_FULL_WIDTH : kb_mapping = CELL_KB_MAPPING_101; break; + case CELL_OSKDIALOG_PANELMODE_NUMERAL : kb_mapping = CELL_KB_MAPPING_101; break; + case CELL_OSKDIALOG_PANELMODE_URL : kb_mapping = CELL_KB_MAPPING_101; break; + case CELL_OSKDIALOG_PANELMODE_PASSWORD : kb_mapping = CELL_KB_MAPPING_101; break; + default : kb_mapping = CELL_KB_MAPPING_101; break; + } + + // Convert key to its u32string presentation + const u16 converted_out_key = cellKbCnvRawCode(kb_mapping, mkey, led, out_key_code); + std::u16string utf16_string; + utf16_string.push_back(converted_out_key); + const std::u32string u32_string = utf16_to_u32string(utf16_string); + const std::string out_key_string = utf16_to_ascii8(utf16_string); + + // Find matching key in the OSK + for (const cell& current_cell : m_grid) + { + // TODO: maybe just ignore the current charset and check all outputs + if (m_selected_charset < current_cell.outputs.size()) + { + for (const auto& str : current_cell.outputs[m_selected_charset]) + { + if (str == u32_string) + { + // Apply key press + if (current_cell.callback) + { + current_cell.callback(str); + } + else + { + on_default_callback(str); + } + return; + } + } + } + } + + // Handle special input + if (!out_key_string.empty()) + { + switch (out_key_string[0]) + { + case ' ': + on_space(u32_string); + return; + case '\b': + on_backspace(u32_string); + return; + case '\n': + on_enter(u32_string); + return; + default: + break; + } + } + } + void osk_dialog::on_text_changed() { const auto ws = u32string_to_utf16(m_preview.value); diff --git a/rpcs3/Emu/RSX/Overlays/overlay_osk.h b/rpcs3/Emu/RSX/Overlays/overlay_osk.h index 0fb2e442b7..ea6cc1bb7e 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_osk.h +++ b/rpcs3/Emu/RSX/Overlays/overlay_osk.h @@ -96,6 +96,7 @@ namespace rsx void update_selection_by_index(u32 index); void on_button_pressed(pad_button button_press) override; + void on_key_pressed(u32 led, u32 mkey, u32 key_code, u32 out_key_code, bool pressed) override; void on_text_changed(); void on_default_callback(const std::u32string& str); diff --git a/rpcs3/Emu/RSX/Overlays/overlays.cpp b/rpcs3/Emu/RSX/Overlays/overlays.cpp index e35eb38eac..bda3095b67 100644 --- a/rpcs3/Emu/RSX/Overlays/overlays.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlays.cpp @@ -3,6 +3,7 @@ #include "overlay_message_dialog.h" #include "Input/pad_thread.h" #include "Emu/Io/interception.h" +#include "Emu/Io/KeyboardHandler.h" #include "Emu/RSX/RSXThread.h" #include "Emu/RSX/Common/time.hpp" @@ -64,7 +65,7 @@ namespace rsx state.fill(true); } - input_timer.Start(); + m_input_timer.Start(); input::SetIntercepted(true); @@ -77,7 +78,7 @@ namespace rsx if (pressed) { - const bool is_auto_repeat_button = auto_repeat_buttons.contains(button_id); + const bool is_auto_repeat_button = m_auto_repeat_buttons.contains(button_id); if (!last_button_state[pad_index][button_id]) { @@ -90,8 +91,8 @@ namespace rsx else if (is_auto_repeat_button) { if (last_auto_repeat_button[pad_index] == button_id - && input_timer.GetMsSince(initial_timestamp[pad_index]) > ms_threshold - && input_timer.GetMsSince(timestamp[pad_index]) > ms_interval) + && m_input_timer.GetMsSince(initial_timestamp[pad_index]) > ms_threshold + && m_input_timer.GetMsSince(timestamp[pad_index]) > ms_interval) { // The auto-repeat button was pressed for at least the given threshold in ms and will trigger at an interval. timestamp[pad_index] = steady_clock::now(); @@ -115,18 +116,60 @@ namespace rsx while (!exit) { + std::this_thread::sleep_for(1ms); + if (Emu.IsStopped()) return selection_code::canceled; - std::this_thread::sleep_for(1ms); + if (Emu.IsPaused()) + continue; + // Get keyboard input + if (m_keyboard_input_enabled) + { + auto& handler = g_fxo->get(); + std::lock_guard lock(handler.m_mutex); + + const KbInfo& current_info = handler.GetInfo(); + + if (!handler.GetKeyboards().empty() && current_info.status[0] == CELL_KB_STATUS_CONNECTED) + { + KbData& current_data = handler.GetData(0); + + if (current_data.len > 0) + { + for (s32 i = 0; i < current_data.len; i++) + { + const KbButton& key = current_data.buttons[i]; + on_key_pressed(current_data.led, current_data.mkey, key.m_keyCode, key.m_outKeyCode, key.m_pressed); + } + + // TODO: is the following step necessary in the overlays? + KbConfig& current_config = handler.GetConfig(0); + + // 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. + // Warning: Don't do this in packet mode, which is basically the mouse and keyboard gaming mode. Otherwise games like Unreal Tournament will be unplayable. + if (current_config.read_mode == CELL_KB_RMODE_INPUTCHAR) + { + current_data.len = 0; + } + } + } + else + { + // TODO: Init handler only if the game requests keyboard input. + // This probably needs to happen completely seperate from cellKb. + handler.Init(1); + } + } + + // Get gamepad input std::lock_guard lock(pad::g_pad_mutex); - const auto handler = pad::get_current_handler(); - const PadInfo& rinfo = handler->GetInfo(); - if (Emu.IsPaused() || !rinfo.now_connect) + if (!rinfo.now_connect) { continue; } diff --git a/rpcs3/Emu/RSX/Overlays/overlays.h b/rpcs3/Emu/RSX/Overlays/overlays.h index 75c897ef82..4f457a480a 100644 --- a/rpcs3/Emu/RSX/Overlays/overlays.h +++ b/rpcs3/Emu/RSX/Overlays/overlays.h @@ -81,8 +81,8 @@ namespace rsx }; protected: - Timer input_timer; - std::set auto_repeat_buttons = { + Timer m_input_timer; + std::set m_auto_repeat_buttons = { pad_button::dpad_up, pad_button::dpad_down, pad_button::dpad_left, @@ -96,12 +96,13 @@ namespace rsx atomic_t m_interactive = false; atomic_t m_stop_pad_interception = false; atomic_t thread_bits = 0; + bool m_keyboard_input_enabled = false; static thread_local u64 g_thread_bit; u64 alloc_thread_bit(); - std::function on_close; + std::function on_close = nullptr; public: s32 return_code = 0; // CELL_OK @@ -111,6 +112,7 @@ namespace rsx compiled_resource get_compiled() override = 0; virtual void on_button_pressed(pad_button /*button_press*/) {} + virtual void on_key_pressed(u32 led, u32 mkey, u32 key_code, u32 out_key_code, bool pressed) {} virtual void close(bool use_callback, bool stop_pad_interception);