From 71f8280c5ea83c9bab8d14e87f0677156d8e3268 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Tue, 19 Apr 2022 23:36:18 +0200 Subject: [PATCH] cellOskDialog: implement KeyboardEventHookCallback --- rpcs3/Emu/Cell/Modules/cellOskDialog.cpp | 183 ++++++++++++++++++++--- rpcs3/Emu/Cell/Modules/cellOskDialog.h | 2 +- rpcs3/Emu/RSX/Overlays/overlay_osk.cpp | 32 ++-- rpcs3/rpcs3qt/osk_dialog_frame.cpp | 4 +- 4 files changed, 184 insertions(+), 37 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellOskDialog.cpp b/rpcs3/Emu/Cell/Modules/cellOskDialog.cpp index e2d78b0438..bea0fb4de2 100644 --- a/rpcs3/Emu/Cell/Modules/cellOskDialog.cpp +++ b/rpcs3/Emu/Cell/Modules/cellOskDialog.cpp @@ -3,6 +3,7 @@ #include "Emu/system_config.h" #include "Emu/Cell/PPUModule.h" #include "Emu/Io/interception.h" +#include "Emu/Io/Keyboard.h" #include "Emu/RSX/Overlays/overlay_osk.h" #include "Emu/IdManager.h" @@ -91,6 +92,7 @@ struct osk_info atomic_t> osk_confirm_callback{}; atomic_t> osk_force_finish_callback{}; atomic_t> osk_hardware_keyboard_event_hook_callback{}; + atomic_t hook_event_mode{0}; stx::init_mutex init; @@ -123,6 +125,7 @@ struct osk_info osk_confirm_callback.store({}); osk_force_finish_callback.store({}); osk_hardware_keyboard_event_hook_callback.store({}); + hook_event_mode.store(0); } }; @@ -340,12 +343,155 @@ error_code cellOskDialogLoadAsync(u32 container, vm::ptr dia input::SetIntercepted(false); }; - if (info.osk_continuous_mode == CELL_OSKDIALOG_CONTINUOUS_MODE_HIDE) + // Set key callback + osk->on_osk_key_input_entered = [wptr = std::weak_ptr(osk)](CellOskDialogKeyMessage key_message) { - info.last_dialog_state = CELL_SYSUTIL_OSKDIALOG_LOADED; - sysutil_send_system_cmd(CELL_SYSUTIL_OSKDIALOG_LOADED, 0); - return CELL_OK; - } + const auto osk = wptr.lock(); + auto& info = g_fxo->get(); + bool is_kook_key = false; + + switch (key_message.keycode) + { + case CELL_KEYC_NO_EVENT: + { + // Any shift/alt/ctrl key + is_kook_key = key_message.mkey > 0 && (info.hook_event_mode & CELL_OSKDIALOG_EVENT_HOOK_TYPE_ONLY_MODIFIER); + break; + } + case CELL_KEYC_E_ROLLOVER: + case CELL_KEYC_E_POSTFAIL: + case CELL_KEYC_E_UNDEF: + case CELL_KEYC_ESCAPE: + case CELL_KEYC_106_KANJI: + case CELL_KEYC_CAPS_LOCK: + case CELL_KEYC_F1: + case CELL_KEYC_F2: + case CELL_KEYC_F3: + case CELL_KEYC_F4: + case CELL_KEYC_F5: + case CELL_KEYC_F6: + case CELL_KEYC_F7: + case CELL_KEYC_F8: + case CELL_KEYC_F9: + case CELL_KEYC_F10: + case CELL_KEYC_F11: + case CELL_KEYC_F12: + case CELL_KEYC_PRINTSCREEN: + case CELL_KEYC_SCROLL_LOCK: + case CELL_KEYC_PAUSE: + case CELL_KEYC_INSERT: + case CELL_KEYC_HOME: + case CELL_KEYC_PAGE_UP: + case CELL_KEYC_DELETE: + case CELL_KEYC_END: + case CELL_KEYC_PAGE_DOWN: + case CELL_KEYC_RIGHT_ARROW: + case CELL_KEYC_LEFT_ARROW: + case CELL_KEYC_DOWN_ARROW: + case CELL_KEYC_UP_ARROW: + case CELL_KEYC_NUM_LOCK: + case CELL_KEYC_APPLICATION: + case CELL_KEYC_KANA: + case CELL_KEYC_HENKAN: + case CELL_KEYC_MUHENKAN: + { + // Any function key or other special key like Delete + is_kook_key = (info.hook_event_mode & CELL_OSKDIALOG_EVENT_HOOK_TYPE_FUNCTION_KEY); + break; + } + default: + { + // Any regular ascii key + is_kook_key = (info.hook_event_mode & CELL_OSKDIALOG_EVENT_HOOK_TYPE_ASCII_KEY); + break; + } + } + + if (!is_kook_key) + return; + + if (auto ccb = info.osk_hardware_keyboard_event_hook_callback.load()) + { + constexpr u32 max_size = 101; + std::vector string_to_send(max_size, 0); + atomic_t done = false; + + for (u32 i = 0; i < max_size - 1; i++) + { + string_to_send[i] = osk->osk_text[i]; + if (osk->osk_text[i] == 0) break; + } + + sysutil_register_cb([&](ppu_thread& cb_ppu) -> s32 + { + vm::var keyMessage(key_message); + vm::var action(CELL_OSKDIALOG_CHANGE_NO_EVENT); + vm::var> pActionInfo(max_size, string_to_send.data()); + + const bool return_value = ccb(cb_ppu, keyMessage, action, pActionInfo.begin()); + cellOskDialog.warning("osk_hardware_keyboard_event_hook_callback: return_value=%d, action=%d)", return_value, action.get_ptr() ? static_cast(*action) : 0); + + if (return_value) + { + ensure(action); + ensure(pActionInfo); + + switch (*action) + { + case CELL_OSKDIALOG_CHANGE_NO_EVENT: + case CELL_OSKDIALOG_CHANGE_EVENT_CANCEL: + { + // Do nothing + break; + } + case CELL_OSKDIALOG_CHANGE_WORDS_INPUT: + { + // Set unconfirmed string and reset unconfirmed string + for (u32 i = 0; i < max_size; i++) + { + osk->osk_text[i] = pActionInfo.begin()[i]; + } + break; + } + case CELL_OSKDIALOG_CHANGE_WORDS_INSERT: + { + // Set confirmed string and reset unconfirmed string + for (u32 i = 0; i < max_size; i++) + { + info.valid_text[i] = pActionInfo.begin()[i]; + osk->osk_text[i] = 0; + } + break; + } + case CELL_OSKDIALOG_CHANGE_WORDS_REPLACE_ALL: + { + // Set confirmed string and reset all strings + for (u32 i = 0; i < max_size; i++) + { + info.valid_text[i] = pActionInfo.begin()[i]; + osk->osk_text[i] = 0; + } + break; + } + default: + { + cellOskDialog.error("osk_hardware_keyboard_event_hook_callback returned invalid action (%d)", *action); + break; + } + } + } + + done = true; + return 0; + }); + + // wait for callback + while (!done && !Emu.IsStopped()) + { + std::this_thread::yield(); + } + } + }; // Set device mask and event lock osk->ignore_input_events = info.lock_ext_input.load(); @@ -358,6 +504,13 @@ error_code cellOskDialogLoadAsync(u32 container, vm::ptr dia input::SetIntercepted(osk->pad_input_enabled, osk->keyboard_input_enabled, osk->mouse_input_enabled); + if (info.osk_continuous_mode == CELL_OSKDIALOG_CONTINUOUS_MODE_HIDE) + { + info.last_dialog_state = CELL_SYSUTIL_OSKDIALOG_LOADED; + sysutil_send_system_cmd(CELL_SYSUTIL_OSKDIALOG_LOADED, 0); + return CELL_OK; + } + Emu.CallFromMainThread([=, &result]() { osk->Create(get_localized_string(localized_string_id::CELL_OSK_DIALOG_TITLE), message, osk->osk_text, maxLength, prohibitFlgs, allowOskPanelFlg, firstViewPanel); @@ -713,25 +866,7 @@ error_code register_keyboard_event_hook_callback(u16 hookEventMode, vm::ptrget().osk_hardware_keyboard_event_hook_callback = pCallback; - - // TODO: use callback - // 1. Check if the callback needs to be called. This depends on the pressed key and on the OR of the hookEventMode parameter: - // CELL_OSKDIALOG_EVENT_HOOK_TYPE_FUNCTION_KEY: Any function keys + other special keys like Delete - // CELL_OSKDIALOG_EVENT_HOOK_TYPE_ASCII_KEY: Any regular ascii key - // CELL_OSKDIALOG_EVENT_HOOK_TYPE_ONLY_MODIFIER: Any shift/alt/ctrl key - // 2. When a hardware key is pressed, call osk_hardware_keyboard_event_hook_callback with the following params: - // keyMessage: our info about the current key press (and release?) - // action: an out pointer. The game will set this value and we will have to react to it - // pActionInfo: the currently unconfirmed string. max 100 characters + '\0' character. This should be the valid_text member of osk_info. - // 3. Check return value: - // false: do nothing - // true: go to the next step - // 4. Check 'action' pointer value. React accordingly: - // CELL_OSKDIALOG_CHANGE_NO_EVENT: do nothing - // CELL_OSKDIALOG_CHANGE_EVENT_CANCEL: cancel input - // CELL_OSKDIALOG_CHANGE_WORDS_INPUT: change unconfirmed string and delete unconfirmed string - // CELL_OSKDIALOG_CHANGE_WORDS_INSERT: change "confirmed string to insert" and delete unconfirmed string - // CELL_OSKDIALOG_CHANGE_WORDS_REPLACE_ALL: change "confirmed string to insert" and delete ALL strings + g_fxo->get().hook_event_mode = hookEventMode; return CELL_OK; } diff --git a/rpcs3/Emu/Cell/Modules/cellOskDialog.h b/rpcs3/Emu/Cell/Modules/cellOskDialog.h index aa5253109c..ca6767bfb7 100644 --- a/rpcs3/Emu/Cell/Modules/cellOskDialog.h +++ b/rpcs3/Emu/Cell/Modules/cellOskDialog.h @@ -262,7 +262,7 @@ public: virtual ~OskDialogBase(); std::function on_osk_close; - std::function on_osk_input_entered; + std::function on_osk_key_input_entered; atomic_t state{ OskDialogState::Unloaded }; atomic_t pad_input_enabled{ true }; // Determines if the OSK consumes the device's events. diff --git a/rpcs3/Emu/RSX/Overlays/overlay_osk.cpp b/rpcs3/Emu/RSX/Overlays/overlay_osk.cpp index 4bc49d430b..d3fe9e2b47 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_osk.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlay_osk.cpp @@ -651,6 +651,7 @@ namespace rsx const std::string out_key_string = utf16_to_ascii8(utf16_string); // Find matching key in the OSK + bool found_key = false; for (const cell& current_cell : m_grid) { // TODO: maybe just ignore the current charset and check all outputs @@ -669,30 +670,46 @@ namespace rsx { on_default_callback(str); } - return; + + found_key = true; + break; } } + + if (found_key) + { + break; + } } } // Handle special input - if (!out_key_string.empty()) + if (!found_key && !out_key_string.empty()) { switch (out_key_string[0]) { case ' ': on_space(u32_string); - return; + break; case '\b': on_backspace(u32_string); - return; + break; case '\n': on_enter(u32_string); - return; + break; default: break; } } + + if (on_osk_key_input_entered) + { + CellOskDialogKeyMessage key_message{}; + key_message.led = led; + key_message.mkey = mkey; + key_message.keycode = key_code; + on_osk_key_input_entered(key_message); + } } void osk_dialog::on_text_changed() @@ -701,11 +718,6 @@ namespace rsx const auto length = (ws.length() + 1) * sizeof(char16_t); memcpy(osk_text, ws.c_str(), length); - if (on_osk_input_entered) - { - on_osk_input_entered(); - } - // Muted contrast for placeholder text m_preview.fore_color.a = m_preview.value.empty() ? 0.5f : 1.f; diff --git a/rpcs3/rpcs3qt/osk_dialog_frame.cpp b/rpcs3/rpcs3qt/osk_dialog_frame.cpp index 989371c70f..cbe1081a48 100644 --- a/rpcs3/rpcs3qt/osk_dialog_frame.cpp +++ b/rpcs3/rpcs3qt/osk_dialog_frame.cpp @@ -77,7 +77,7 @@ void osk_dialog_frame::Create(const std::string& title, const std::u16string& me { input_count_label->setText(QString("%1/%2").arg(text.length()).arg(charlimit)); SetOskText(text); - on_osk_input_entered(); + // if (on_osk_key_input_entered) on_osk_key_input_entered({}); // Not applicable }); connect(input, &QLineEdit::returnPressed, m_dialog, &QDialog::accept); @@ -141,7 +141,7 @@ void osk_dialog_frame::Create(const std::string& title, const std::u16string& me input_count_label->setText(QString("%1/%2").arg(text.length()).arg(charlimit)); SetOskText(text); - on_osk_input_entered(); + // if (on_osk_key_input_entered) on_osk_key_input_entered({}); // Not applicable }); inputLayout->addWidget(input);