mirror of
https://github.com/RPCS3/rpcs3.git
synced 2024-11-22 18:53:28 +01:00
cellOskDialog: improve kb event hook callback
This commit is contained in:
parent
fb974a4551
commit
fc34b3f144
@ -488,31 +488,36 @@ error_code cellOskDialogLoadAsync(u32 container, vm::ptr<CellOskDialogParam> dia
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The max size is 100 characters plus '\0'. Apparently this used to be 30 plus '\0' in older firmware.
|
||||||
constexpr u32 max_size = 101;
|
constexpr u32 max_size = 101;
|
||||||
std::vector<u16> string_to_send(max_size, 0);
|
|
||||||
|
|
||||||
for (u32 i = 0; i < max_size - 1; i++)
|
// TODO: Send unconfirmed string if there is one.
|
||||||
{
|
// As far as I understand, this is for example supposed to be the IME preview.
|
||||||
string_to_send[i] = osk->osk_text[i];
|
// So when you type in japanese, you get some word propositions which you can select and confirm.
|
||||||
if (osk->osk_text[i] == 0) break;
|
// The "confirmed" string is basically everything you already wrote, while the "unconfirmed"
|
||||||
}
|
// string is the auto-completion part that you haven't accepted as proper word yet.
|
||||||
|
// The game expects you to send this "preview", or an empty string if there is none.
|
||||||
|
std::array<u16, max_size> string_to_send{};
|
||||||
|
|
||||||
sysutil_register_cb([key_message, string_to_send, event_hook_callback](ppu_thread& cb_ppu) -> s32
|
sysutil_register_cb([key_message, string_to_send, event_hook_callback](ppu_thread& cb_ppu) -> s32
|
||||||
{
|
{
|
||||||
|
// Prepare callback variables
|
||||||
vm::var<CellOskDialogKeyMessage> keyMessage(key_message);
|
vm::var<CellOskDialogKeyMessage> keyMessage(key_message);
|
||||||
vm::var<u32> action(CELL_OSKDIALOG_CHANGE_NO_EVENT);
|
vm::var<u32> action(CELL_OSKDIALOG_CHANGE_NO_EVENT);
|
||||||
vm::var<u16[], vm::page_allocator<>> pActionInfo(string_to_send.size(), string_to_send.data());
|
vm::var<u16[], vm::page_allocator<>> pActionInfo(string_to_send.size(), string_to_send.data());
|
||||||
|
|
||||||
std::u16string utf16_string;
|
// Create helpers for logging
|
||||||
utf16_string.insert(0, reinterpret_cast<const char16_t*>(string_to_send.data()), string_to_send.size());
|
std::u16string utf16_string(reinterpret_cast<const char16_t*>(string_to_send.data()), string_to_send.size());
|
||||||
std::string action_info = utf16_to_ascii8(utf16_string);
|
std::string utf8_string = utf16_to_ascii8(utf16_string);
|
||||||
|
|
||||||
cellOskDialog.notice("osk_hardware_keyboard_event_hook_callback(led=%d, mkey=%d, keycode=%d, action=%d, pActionInfo='%s')", keyMessage->led, keyMessage->mkey, keyMessage->keycode, *action, action_info);
|
cellOskDialog.notice("osk_hardware_keyboard_event_hook_callback(led=%d, mkey=%d, keycode=%d, action=%d, pActionInfo='%s')", keyMessage->led, keyMessage->mkey, keyMessage->keycode, *action, utf8_string);
|
||||||
|
|
||||||
const bool return_value = event_hook_callback(cb_ppu, keyMessage, action, pActionInfo.begin());
|
// Call the hook function. The game reads and writes pActionInfo. We need to react based on the returned action.
|
||||||
|
const bool return_value = event_hook_callback(cb_ppu, keyMessage, action, pActionInfo);
|
||||||
ensure(action);
|
ensure(action);
|
||||||
ensure(pActionInfo);
|
ensure(pActionInfo);
|
||||||
|
|
||||||
|
// Parse returned text for logging
|
||||||
utf16_string.clear();
|
utf16_string.clear();
|
||||||
for (u32 i = 0; i < max_size; i++)
|
for (u32 i = 0; i < max_size; i++)
|
||||||
{
|
{
|
||||||
@ -520,10 +525,11 @@ error_code cellOskDialogLoadAsync(u32 container, vm::ptr<CellOskDialogParam> dia
|
|||||||
if (!code) break;
|
if (!code) break;
|
||||||
utf16_string.push_back(code);
|
utf16_string.push_back(code);
|
||||||
}
|
}
|
||||||
action_info = utf16_to_ascii8(utf16_string);
|
utf8_string = utf16_to_ascii8(utf16_string);
|
||||||
|
|
||||||
cellOskDialog.notice("osk_hardware_keyboard_event_hook_callback: return_value=%d, action=%d, pActionInfo='%s'", return_value, *action, action_info);
|
cellOskDialog.notice("osk_hardware_keyboard_event_hook_callback: return_value=%d, action=%d, pActionInfo='%s'", return_value, *action, utf8_string);
|
||||||
|
|
||||||
|
// Check if the hook function was successful
|
||||||
if (return_value)
|
if (return_value)
|
||||||
{
|
{
|
||||||
const auto osk = _get_osk_dialog(false);
|
const auto osk = _get_osk_dialog(false);
|
||||||
@ -546,31 +552,25 @@ error_code cellOskDialogLoadAsync(u32 container, vm::ptr<CellOskDialogParam> dia
|
|||||||
}
|
}
|
||||||
case CELL_OSKDIALOG_CHANGE_WORDS_INPUT:
|
case CELL_OSKDIALOG_CHANGE_WORDS_INPUT:
|
||||||
{
|
{
|
||||||
// Set unconfirmed string and reset unconfirmed string
|
// TODO: Replace unconfirmed string.
|
||||||
for (u32 i = 0; i < max_size; i++)
|
cellOskDialog.todo("osk_hardware_keyboard_event_hook_callback: replace unconfirmed string with '%s'", utf8_string);
|
||||||
{
|
|
||||||
osk->osk_text[i] = pActionInfo.begin()[i];
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CELL_OSKDIALOG_CHANGE_WORDS_INSERT:
|
case CELL_OSKDIALOG_CHANGE_WORDS_INSERT:
|
||||||
{
|
{
|
||||||
|
// TODO: Remove unconfirmed string
|
||||||
|
cellOskDialog.todo("osk_hardware_keyboard_event_hook_callback: remove unconfirmed string");
|
||||||
|
|
||||||
// Set confirmed string and reset unconfirmed string
|
// Set confirmed string and reset unconfirmed string
|
||||||
for (u32 i = 0; i < max_size; i++)
|
cellOskDialog.notice("osk_hardware_keyboard_event_hook_callback: inserting string '%s'", utf8_string);
|
||||||
{
|
osk->Insert(utf16_string);
|
||||||
info.valid_text[i] = pActionInfo.begin()[i];
|
|
||||||
osk->osk_text[i] = 0;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CELL_OSKDIALOG_CHANGE_WORDS_REPLACE_ALL:
|
case CELL_OSKDIALOG_CHANGE_WORDS_REPLACE_ALL:
|
||||||
{
|
{
|
||||||
// Set confirmed string and reset all strings
|
// Replace confirmed string and remove unconfirmed string.
|
||||||
for (u32 i = 0; i < max_size; i++)
|
cellOskDialog.notice("osk_hardware_keyboard_event_hook_callback: replacing all strings with '%s'", utf8_string);
|
||||||
{
|
osk->SetText(utf16_string);
|
||||||
info.valid_text[i] = pActionInfo.begin()[i];
|
|
||||||
osk->osk_text[i] = 0;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
@ -302,6 +302,8 @@ public:
|
|||||||
|
|
||||||
virtual void Create(const osk_params& params) = 0;
|
virtual void Create(const osk_params& params) = 0;
|
||||||
virtual void Clear(bool clear_all_data) = 0;
|
virtual void Clear(bool clear_all_data) = 0;
|
||||||
|
virtual void Insert(const std::u16string& text) = 0;
|
||||||
|
virtual void SetText(const std::u16string& text) = 0;
|
||||||
|
|
||||||
// Closes the dialog.
|
// Closes the dialog.
|
||||||
// Set status to CELL_OSKDIALOG_CLOSE_CONFIRM or CELL_OSKDIALOG_CLOSE_CANCEL for user input.
|
// Set status to CELL_OSKDIALOG_CLOSE_CONFIRM or CELL_OSKDIALOG_CLOSE_CANCEL for user input.
|
||||||
@ -329,7 +331,7 @@ struct osk_info
|
|||||||
{
|
{
|
||||||
std::shared_ptr<OskDialogBase> dlg;
|
std::shared_ptr<OskDialogBase> dlg;
|
||||||
|
|
||||||
std::array<char16_t, CELL_OSKDIALOG_STRING_SIZE> valid_text{};
|
std::array<char16_t, CELL_OSKDIALOG_STRING_SIZE> valid_text{}; // The string that's going to be served to the game.
|
||||||
shared_mutex text_mtx;
|
shared_mutex text_mtx;
|
||||||
|
|
||||||
atomic_t<bool> use_separate_windows = false;
|
atomic_t<bool> use_separate_windows = false;
|
||||||
|
@ -67,9 +67,6 @@ namespace rsx
|
|||||||
close(true, true);
|
close(true, true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear text edit in continuous separate window mode. Keep actual data just in case.
|
|
||||||
Clear(false);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
fade_animation.active = true;
|
fade_animation.active = true;
|
||||||
@ -77,11 +74,11 @@ namespace rsx
|
|||||||
|
|
||||||
void osk_dialog::Clear(bool clear_all_data)
|
void osk_dialog::Clear(bool clear_all_data)
|
||||||
{
|
{
|
||||||
osk.notice("Clearing osk (clear_all_data=%d)", clear_all_data);
|
|
||||||
|
|
||||||
// Try to lock. Clear might be called recursively.
|
// Try to lock. Clear might be called recursively.
|
||||||
const bool locked = m_preview_mutex.try_lock();
|
const bool locked = m_preview_mutex.try_lock();
|
||||||
|
|
||||||
|
osk.notice("Clearing osk (clear_all_data=%d)", clear_all_data);
|
||||||
|
|
||||||
m_preview.caret_position = 0;
|
m_preview.caret_position = 0;
|
||||||
m_preview.set_text({});
|
m_preview.set_text({});
|
||||||
|
|
||||||
@ -98,6 +95,62 @@ namespace rsx
|
|||||||
m_update = true;
|
m_update = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void osk_dialog::SetText(const std::u16string& text)
|
||||||
|
{
|
||||||
|
// Try to lock. Insert might be called recursively.
|
||||||
|
const bool locked = m_preview_mutex.try_lock();
|
||||||
|
|
||||||
|
const std::u16string new_str = text.length() <= char_limit ? text : text.substr(0, char_limit);
|
||||||
|
|
||||||
|
osk.notice("Setting osk text (text='%s', new_str='%s', char_limit=%d)", utf16_to_ascii8(text), utf16_to_ascii8(new_str), char_limit);
|
||||||
|
|
||||||
|
m_preview.caret_position = new_str.length();
|
||||||
|
m_preview.set_unicode_text(utf16_to_u32string(new_str));
|
||||||
|
|
||||||
|
on_text_changed();
|
||||||
|
|
||||||
|
if (locked)
|
||||||
|
{
|
||||||
|
m_preview_mutex.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_update = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void osk_dialog::Insert(const std::u16string& text)
|
||||||
|
{
|
||||||
|
// Try to lock. Insert might be called recursively.
|
||||||
|
const bool locked = m_preview_mutex.try_lock();
|
||||||
|
|
||||||
|
osk.notice("Inserting into osk at position %d (text='%s', char_limit=%d)", m_preview.caret_position, utf16_to_ascii8(text), char_limit);
|
||||||
|
|
||||||
|
// Append to output text
|
||||||
|
if (m_preview.value.empty())
|
||||||
|
{
|
||||||
|
const std::u16string new_str = text.length() <= char_limit ? text : text.substr(0, char_limit);
|
||||||
|
|
||||||
|
m_preview.caret_position = new_str.length();
|
||||||
|
m_preview.set_unicode_text(utf16_to_u32string(new_str));
|
||||||
|
}
|
||||||
|
else if ((m_preview.value.length() + text.length()) <= char_limit)
|
||||||
|
{
|
||||||
|
m_preview.insert_text(utf16_to_u32string(text));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
osk.notice("Can't insert into osk: Character limit reached.");
|
||||||
|
}
|
||||||
|
|
||||||
|
on_text_changed();
|
||||||
|
|
||||||
|
if (locked)
|
||||||
|
{
|
||||||
|
m_preview_mutex.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_update = true;
|
||||||
|
}
|
||||||
|
|
||||||
void osk_dialog::add_panel(const osk_panel& panel)
|
void osk_dialog::add_panel(const osk_panel& panel)
|
||||||
{
|
{
|
||||||
// On PS3 apparently only 7 panels are added, the rest is ignored
|
// On PS3 apparently only 7 panels are added, the rest is ignored
|
||||||
@ -1008,13 +1061,12 @@ namespace rsx
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (m_preview.value.length() == char_limit)
|
if (m_preview.value.length() >= char_limit)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto new_str = m_preview.value + str;
|
if ((m_preview.value.length() + str.length()) <= char_limit)
|
||||||
if (new_str.length() <= char_limit)
|
|
||||||
{
|
{
|
||||||
m_preview.insert_text(str);
|
m_preview.insert_text(str);
|
||||||
}
|
}
|
||||||
|
@ -108,6 +108,8 @@ namespace rsx
|
|||||||
void Create(const osk_params& params) override;
|
void Create(const osk_params& params) override;
|
||||||
void Close(s32 status) override;
|
void Close(s32 status) override;
|
||||||
void Clear(bool clear_all_data) override;
|
void Clear(bool clear_all_data) override;
|
||||||
|
void SetText(const std::u16string& text) override;
|
||||||
|
void Insert(const std::u16string& text) override;
|
||||||
|
|
||||||
void initialize_layout(const std::u32string& title, const std::u32string& initial_text);
|
void initialize_layout(const std::u32string& title, const std::u32string& initial_text);
|
||||||
void add_panel(const osk_panel& panel);
|
void add_panel(const osk_panel& panel);
|
||||||
|
@ -211,3 +211,20 @@ void osk_dialog_frame::Clear(bool clear_all_data)
|
|||||||
SetOskText("");
|
SetOskText("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void osk_dialog_frame::SetText(const std::u16string& text)
|
||||||
|
{
|
||||||
|
if (m_dialog)
|
||||||
|
{
|
||||||
|
SetOskText(QString::fromStdU16String(text));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void osk_dialog_frame::Insert(const std::u16string& text)
|
||||||
|
{
|
||||||
|
if (m_dialog)
|
||||||
|
{
|
||||||
|
// TODO: Correct position (will probably never get implemented because this dialog is just a fallback)
|
||||||
|
SetOskText(QString::fromStdU16String(text));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -19,6 +19,8 @@ public:
|
|||||||
void Create(const osk_params& params) override;
|
void Create(const osk_params& params) override;
|
||||||
void Close(s32 status) override;
|
void Close(s32 status) override;
|
||||||
void Clear(bool clear_all_data) override;
|
void Clear(bool clear_all_data) override;
|
||||||
|
void SetText(const std::u16string& text) override;
|
||||||
|
void Insert(const std::u16string& text) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void SetOskText(const QString& text);
|
void SetOskText(const QString& text);
|
||||||
|
Loading…
Reference in New Issue
Block a user