cellOskDialog: implement KeyboardEventHookCallback

This commit is contained in:
Megamouse 2022-04-19 23:36:18 +02:00
parent 0ff293707a
commit 71f8280c5e
4 changed files with 184 additions and 37 deletions

View File

@ -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<vm::ptr<cellOskDialogConfirmWordFilterCallback>> osk_confirm_callback{};
atomic_t<vm::ptr<cellOskDialogForceFinishCallback>> osk_force_finish_callback{};
atomic_t<vm::ptr<cellOskDialogHardwareKeyboardEventHookCallback>> osk_hardware_keyboard_event_hook_callback{};
atomic_t<u16> 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<CellOskDialogParam> 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<OskDialogBase>(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<osk_info>();
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<u16> string_to_send(max_size, 0);
atomic_t<bool> 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<CellOskDialogKeyMessage> keyMessage(key_message);
vm::var<u32> action(CELL_OSKDIALOG_CHANGE_NO_EVENT);
vm::var<u16[], vm::page_allocator<>> 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<u32>(*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<CellOskDialogParam> 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::ptr<cell
}
g_fxo->get<osk_info>().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<osk_info>().hook_event_mode = hookEventMode;
return CELL_OK;
}

View File

@ -262,7 +262,7 @@ public:
virtual ~OskDialogBase();
std::function<void(s32 status)> on_osk_close;
std::function<void()> on_osk_input_entered;
std::function<void(CellOskDialogKeyMessage key_message)> on_osk_key_input_entered;
atomic_t<OskDialogState> state{ OskDialogState::Unloaded };
atomic_t<bool> pad_input_enabled{ true }; // Determines if the OSK consumes the device's events.

View File

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

View File

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