rpcs3/rpcs3/Emu/Cell/Modules/cellOskDialog.cpp
2020-12-06 15:30:12 +03:00

673 lines
18 KiB
C++

#include "stdafx.h"
#include "Emu/System.h"
#include "Emu/system_config.h"
#include "Emu/Cell/PPUModule.h"
#include "Emu/Io/interception.h"
#include "Emu/RSX/Overlays/overlay_osk.h"
#include "Emu/IdManager.h"
#include "cellSysutil.h"
#include "cellOskDialog.h"
#include "cellMsgDialog.h"
#include "util/init_mutex.hpp"
#include <thread>
LOG_CHANNEL(cellOskDialog);
template<>
void fmt_class_string<CellOskDialogError>::format(std::string& out, u64 arg)
{
format_enum(out, arg, [](auto error)
{
switch (error)
{
STR_CASE(CELL_OSKDIALOG_ERROR_IME_ALREADY_IN_USE);
STR_CASE(CELL_OSKDIALOG_ERROR_GET_SIZE_ERROR);
STR_CASE(CELL_OSKDIALOG_ERROR_UNKNOWN);
STR_CASE(CELL_OSKDIALOG_ERROR_PARAM);
}
return unknown;
});
}
OskDialogBase::~OskDialogBase()
{
}
struct osk_info
{
std::shared_ptr<OskDialogBase> dlg;
atomic_t<bool> use_separate_windows = false;
atomic_t<CellOskDialogContinuousMode> osk_continuous_mode = CELL_OSKDIALOG_CONTINUOUS_MODE_NONE;
atomic_t<vm::ptr<cellOskDialogConfirmWordFilterCallback>> osk_confirm_callback{};
stx::init_mutex init;
};
// TODO: don't use this function
std::shared_ptr<OskDialogBase> _get_osk_dialog(bool create = false)
{
const auto osk = g_fxo->get<osk_info>();
if (create)
{
const auto init = osk->init.init();
if (!init)
{
return nullptr;
}
if (auto manager = g_fxo->get<rsx::overlays::display_manager>())
{
std::shared_ptr<rsx::overlays::osk_dialog> dlg = std::make_shared<rsx::overlays::osk_dialog>();
osk->dlg = manager->add(dlg);
}
else
{
osk->dlg = Emu.GetCallbacks().get_osk_dialog();
}
return osk->dlg;
}
else
{
const auto init = osk->init.access();
if (!init)
{
return nullptr;
}
return osk->dlg;
}
}
error_code cellOskDialogLoadAsync(u32 container, vm::ptr<CellOskDialogParam> dialogParam, vm::ptr<CellOskDialogInputFieldInfo> inputFieldInfo)
{
cellOskDialog.warning("cellOskDialogLoadAsync(container=0x%x, dialogParam=*0x%x, inputFieldInfo=*0x%x)", container, dialogParam, inputFieldInfo);
if (!dialogParam || !inputFieldInfo || !inputFieldInfo->message || !inputFieldInfo->init_text || inputFieldInfo->limit_length > CELL_OSKDIALOG_STRING_SIZE)
{
return CELL_OSKDIALOG_ERROR_PARAM;
}
auto osk = _get_osk_dialog(true);
// Can't open another dialog if this one is already open.
if (!osk || osk->state.load() == OskDialogState::Open)
{
return CELL_SYSUTIL_ERROR_BUSY;
}
// Get the OSK options
u32 maxLength = (inputFieldInfo->limit_length >= CELL_OSKDIALOG_STRING_SIZE) ? 511 : u32{inputFieldInfo->limit_length};
const u32 prohibitFlgs = dialogParam->prohibitFlgs;
const u32 allowOskPanelFlg = dialogParam->allowOskPanelFlg;
const u32 firstViewPanel = dialogParam->firstViewPanel;
// Get init text and prepare return value
osk->osk_input_result = CELL_OSKDIALOG_INPUT_FIELD_RESULT_OK;
std::memset(osk->osk_text, 0, sizeof(osk->osk_text));
std::memset(osk->osk_text_old, 0, sizeof(osk->osk_text_old));
if (inputFieldInfo->init_text)
{
for (u32 i = 0; (i < maxLength) && (inputFieldInfo->init_text[i] != 0); i++)
{
osk->osk_text[i] = inputFieldInfo->init_text[i];
osk->osk_text_old[i] = inputFieldInfo->init_text[i];
}
}
// Get message to display above the input field
// Guarantees 0 terminated (+1). In praxis only 128 but for now lets display all of it
char16_t message[CELL_OSKDIALOG_STRING_SIZE + 1]{};
if (inputFieldInfo->message)
{
for (u32 i = 0; (i < CELL_OSKDIALOG_STRING_SIZE) && (inputFieldInfo->message[i] != 0); i++)
{
message[i] = inputFieldInfo->message[i];
}
}
bool result = false;
osk->on_osk_close = [maxLength, wptr = std::weak_ptr<OskDialogBase>(osk)](s32 status)
{
const auto osk = wptr.lock();
if (osk->state.atomic_op([&](OskDialogState& state)
{
if (state == OskDialogState::Abort)
{
return true;
}
state = OskDialogState::Close;
return false;
}))
{
input::SetIntercepted(false);
sysutil_send_system_cmd(CELL_SYSUTIL_OSKDIALOG_FINISHED, 0);
return;
}
const bool accepted = status == CELL_MSGDIALOG_BUTTON_OK;
if (accepted)
{
if (auto ccb = g_fxo->get<osk_info>()->osk_confirm_callback.exchange({}))
{
vm::ptr<u16> string_to_send = vm::cast(vm::alloc(CELL_OSKDIALOG_STRING_SIZE * 2, vm::main));
atomic_t<bool> done = false;
u32 return_value;
u32 i;
for (i = 0; i < CELL_OSKDIALOG_STRING_SIZE - 1; i++)
{
string_to_send[i] = osk->osk_text[i];
if (osk->osk_text[i] == 0) break;
}
sysutil_register_cb([&, length = i](ppu_thread& cb_ppu) -> s32
{
return_value = ccb(cb_ppu, string_to_send, static_cast<s32>(length));
cellOskDialog.warning("osk_confirm_callback return_value=%d", return_value);
for (u32 i = 0; i < CELL_OSKDIALOG_STRING_SIZE - 1; i++)
{
osk->osk_text[i] = string_to_send[i];
}
done = true;
return 0;
});
// wait for check callback
while (done == false)
{
std::this_thread::yield();
}
}
if (g_fxo->get<osk_info>()->use_separate_windows.load() && osk->osk_text[0] == 0)
{
cellOskDialog.warning("cellOskDialogLoadAsync: input result is CELL_OSKDIALOG_INPUT_FIELD_RESULT_NO_INPUT_TEXT");
osk->osk_input_result = CELL_OSKDIALOG_INPUT_FIELD_RESULT_NO_INPUT_TEXT;
}
else
{
osk->osk_input_result = CELL_OSKDIALOG_INPUT_FIELD_RESULT_OK;
}
}
else
{
osk->osk_input_result = CELL_OSKDIALOG_INPUT_FIELD_RESULT_CANCELED;
}
// Send OSK status
if (g_fxo->get<osk_info>()->use_separate_windows.load() && (g_fxo->get<osk_info>()->osk_continuous_mode.load() != CELL_OSKDIALOG_CONTINUOUS_MODE_NONE))
{
if (accepted)
{
sysutil_send_system_cmd(CELL_SYSUTIL_OSKDIALOG_INPUT_ENTERED, 0);
}
else
{
sysutil_send_system_cmd(CELL_SYSUTIL_OSKDIALOG_INPUT_CANCELED, 0);
}
}
else
{
sysutil_send_system_cmd(CELL_SYSUTIL_OSKDIALOG_FINISHED, 0);
}
input::SetIntercepted(false);
};
osk->on_osk_input_entered = [wptr = std::weak_ptr<OskDialogBase>(osk)]()
{
const auto osk = wptr.lock();
if (g_fxo->get<osk_info>()->use_separate_windows.load())
{
sysutil_send_system_cmd(CELL_SYSUTIL_OSKDIALOG_INPUT_ENTERED, 0);
}
};
input::SetIntercepted(true);
Emu.CallAfter([=, &result]()
{
osk->Create(get_localized_string(localized_string_id::CELL_OSK_DIALOG_TITLE), message, osk->osk_text, maxLength, prohibitFlgs, allowOskPanelFlg, firstViewPanel);
result = true;
});
sysutil_send_system_cmd(CELL_SYSUTIL_OSKDIALOG_LOADED, 0);
while (!result)
{
thread_ctrl::wait_for(1000);
}
return CELL_OK;
}
error_code cellOskDialogLoadAsyncExt()
{
UNIMPLEMENTED_FUNC(cellOskDialog);
return CELL_OK;
}
error_code getText(vm::ptr<CellOskDialogCallbackReturnParam> OutputInfo, bool is_unload)
{
if (!OutputInfo || OutputInfo->numCharsResultString < 0)
{
return CELL_OSKDIALOG_ERROR_PARAM;
}
const auto osk = _get_osk_dialog();
if (!osk)
{
return CELL_MSGDIALOG_ERROR_DIALOG_NOT_OPENED;
}
if (is_unload)
{
OutputInfo->result = osk->osk_input_result.load();
}
else
{
if (memcmp(osk->osk_text_old, osk->osk_text, sizeof(osk->osk_text)) == 0)
{
OutputInfo->result = CELL_OSKDIALOG_INPUT_FIELD_RESULT_NO_INPUT_TEXT;
}
else
{
OutputInfo->result = CELL_OSKDIALOG_INPUT_FIELD_RESULT_OK;
}
}
bool do_copy = OutputInfo->pResultString && (OutputInfo->result == CELL_OSKDIALOG_INPUT_FIELD_RESULT_OK || (is_unload && OutputInfo->result == CELL_OSKDIALOG_INPUT_FIELD_RESULT_NO_INPUT_TEXT));
for (s32 i = 0; i < CELL_OSKDIALOG_STRING_SIZE - 1; i++)
{
osk->osk_text_old[i] = osk->osk_text[i];
if (do_copy)
{
if (i < OutputInfo->numCharsResultString)
{
if (osk->osk_text[i] == 0)
{
do_copy = false;
}
OutputInfo->pResultString[i] = osk->osk_text[i];
}
else
{
OutputInfo->pResultString[i] = 0;
do_copy = false;
}
}
}
if (is_unload)
{
// Unload should be called last, so remove the dialog here
if (const auto reset_lock = g_fxo->get<osk_info>()->init.reset())
{
// TODO
g_fxo->get<osk_info>()->dlg.reset();
}
sysutil_send_system_cmd(CELL_SYSUTIL_OSKDIALOG_UNLOADED, 0);
}
return CELL_OK;
}
error_code cellOskDialogUnloadAsync(vm::ptr<CellOskDialogCallbackReturnParam> OutputInfo)
{
cellOskDialog.warning("cellOskDialogUnloadAsync(OutputInfo=*0x%x)", OutputInfo);
return getText(OutputInfo, true);
}
error_code cellOskDialogGetSize(vm::ptr<u16> width, vm::ptr<u16> height, u32 /*CellOskDialogType*/ dialogType)
{
cellOskDialog.warning("cellOskDialogGetSize(width=*0x%x, height=*0x%x, dialogType=%d)", width, height, dialogType);
if (!width || !height)
{
return CELL_OSKDIALOG_ERROR_PARAM;
}
if (dialogType >= CELL_OSKDIALOG_TYPE_SEPARATE_SINGLELINE_TEXT_WINDOW)
{
*width = 0;
}
else
{
*width = 1;
}
*height = 1;
return CELL_OK;
}
error_code cellOskDialogAbort()
{
cellOskDialog.warning("cellOskDialogAbort()");
const auto osk = _get_osk_dialog();
if (!osk)
{
return CELL_MSGDIALOG_ERROR_DIALOG_NOT_OPENED;
}
const s32 result = osk->state.atomic_op([](OskDialogState& state)
{
// Check for open dialog. In this case the dialog is only "Open" if it was not aborted before.
if (state == OskDialogState::Abort)
{
return static_cast<s32>(CELL_MSGDIALOG_ERROR_DIALOG_NOT_OPENED);
}
// If the dialog has the Open state then it is in use. Only dialogs with the Close state can be aborted.
if (state == OskDialogState::Open)
{
return static_cast<s32>(CELL_SYSUTIL_ERROR_BUSY);
}
state = OskDialogState::Abort;
return static_cast<s32>(CELL_OK);
});
if (result != CELL_OK)
{
return result;
}
osk->osk_input_result = CELL_OSKDIALOG_INPUT_FIELD_RESULT_ABORT;
osk->Close(false);
return CELL_OK;
}
error_code cellOskDialogSetDeviceMask(u32 deviceMask)
{
cellOskDialog.todo("cellOskDialogSetDeviceMask(deviceMask=0x%x)", deviceMask);
return CELL_OK;
}
error_code cellOskDialogSetSeparateWindowOption(vm::ptr<CellOskDialogSeparateWindowOption> windowOption)
{
cellOskDialog.todo("cellOskDialogSetSeparateWindowOption(windowOption=*0x%x)", windowOption);
if (!windowOption)
{
return CELL_OSKDIALOG_ERROR_PARAM;
}
if (const auto osk = g_fxo->get<osk_info>(); true)
{
osk->use_separate_windows = true;
osk->osk_continuous_mode = static_cast<CellOskDialogContinuousMode>(+windowOption->continuousMode);
}
return CELL_OK;
}
error_code cellOskDialogSetInitialInputDevice(vm::ptr<CellOskDialogInputDevice> inputDevice)
{
cellOskDialog.todo("cellOskDialogSetInitialInputDevice(inputDevice=*0x%x)", inputDevice);
return CELL_OK;
}
error_code cellOskDialogSetInitialKeyLayout(vm::ptr<CellOskDialogInitialKeyLayout> initialKeyLayout)
{
cellOskDialog.todo("cellOskDialogSetInitialKeyLayout(initialKeyLayout=*0x%x)", initialKeyLayout);
return CELL_OK;
}
error_code cellOskDialogDisableDimmer()
{
cellOskDialog.todo("cellOskDialogDisableDimmer()");
return CELL_OK;
}
error_code cellOskDialogSetKeyLayoutOption(u32 option)
{
cellOskDialog.todo("cellOskDialogSetKeyLayoutOption(option=0x%x)", option);
if (option == 0 || option > 3) // CELL_OSKDIALOG_10KEY_PANEL OR CELL_OSKDIALOG_FULLKEY_PANEL
{
return CELL_OSKDIALOG_ERROR_PARAM;
}
return CELL_OK;
}
error_code cellOskDialogAddSupportLanguage(u32 supportLanguage)
{
cellOskDialog.todo("cellOskDialogAddSupportLanguage(supportLanguage=0x%x)", supportLanguage);
return CELL_OK;
}
error_code cellOskDialogSetLayoutMode(s32 layoutMode)
{
cellOskDialog.todo("cellOskDialogSetLayoutMode(layoutMode=%d)", layoutMode);
return CELL_OK;
}
error_code cellOskDialogGetInputText(vm::ptr<CellOskDialogCallbackReturnParam> OutputInfo)
{
cellOskDialog.warning("cellOskDialogGetInputText(OutputInfo=*0x%x)", OutputInfo);
return getText(OutputInfo, false);
}
error_code cellOskDialogExtInputDeviceUnlock()
{
cellOskDialog.todo("cellOskDialogExtInputDeviceUnlock()");
return CELL_OK;
}
error_code register_keyboard_event_hook_callback(u16 hookEventMode, vm::ptr<cellOskDialogHardwareKeyboardEventHookCallback> pCallback)
{
cellOskDialog.todo("register_keyboard_event_hook_callback(hookEventMode=%u, pCallback=*0x%x)", hookEventMode, pCallback);
if (!pCallback)
{
return CELL_OSKDIALOG_ERROR_PARAM;
}
return CELL_OK;
}
error_code cellOskDialogExtRegisterKeyboardEventHookCallback(u16 hookEventMode, vm::ptr<cellOskDialogHardwareKeyboardEventHookCallback> pCallback)
{
cellOskDialog.todo("cellOskDialogExtRegisterKeyboardEventHookCallback(hookEventMode=%u, pCallback=*0x%x)", hookEventMode, pCallback);
if (hookEventMode == 0 || hookEventMode > 3) // CELL_OSKDIALOG_EVENT_HOOK_TYPE_FUNCTION_KEY OR CELL_OSKDIALOG_EVENT_HOOK_TYPE_ASCII_KEY
{
return CELL_OSKDIALOG_ERROR_PARAM;
}
return register_keyboard_event_hook_callback(hookEventMode, pCallback);
}
error_code cellOskDialogExtRegisterKeyboardEventHookCallbackEx(u16 hookEventMode, vm::ptr<cellOskDialogHardwareKeyboardEventHookCallback> pCallback)
{
cellOskDialog.todo("cellOskDialogExtRegisterKeyboardEventHookCallbackEx(hookEventMode=%u, pCallback=*0x%x)", hookEventMode, pCallback);
if (hookEventMode == 0 || hookEventMode > 7) // CELL_OSKDIALOG_EVENT_HOOK_TYPE_FUNCTION_KEY OR CELL_OSKDIALOG_EVENT_HOOK_TYPE_ASCII_KEY OR CELL_OSKDIALOG_EVENT_HOOK_TYPE_ONLY_MODIFIER
{
return CELL_OSKDIALOG_ERROR_PARAM;
}
return register_keyboard_event_hook_callback(hookEventMode, pCallback);
}
error_code cellOskDialogExtAddJapaneseOptionDictionary(vm::cpptr<char> filePath)
{
cellOskDialog.todo("cellOskDialogExtAddJapaneseOptionDictionary(filePath=**0x%0x)", filePath);
return CELL_OK;
}
error_code cellOskDialogExtEnableClipboard()
{
cellOskDialog.todo("cellOskDialogExtEnableClipboard()");
return CELL_OK;
}
error_code cellOskDialogExtSendFinishMessage(u32 /*CellOskDialogFinishReason*/ finishReason)
{
cellOskDialog.warning("cellOskDialogExtSendFinishMessage(finishReason=%d)", finishReason);
const auto osk = _get_osk_dialog();
// Check for open dialog.
if (!osk || osk->state.load() != OskDialogState::Open)
{
return CELL_MSGDIALOG_ERROR_DIALOG_NOT_OPENED;
}
osk->Close(finishReason == CELL_OSKDIALOG_CLOSE_CONFIRM);
return CELL_OK;
}
error_code cellOskDialogExtAddOptionDictionary(vm::cptr<CellOskDialogImeDictionaryInfo> dictionaryInfo)
{
cellOskDialog.todo("cellOskDialogExtAddOptionDictionary(dictionaryInfo=*0x%x)", dictionaryInfo);
return CELL_OK;
}
error_code cellOskDialogExtSetInitialScale(f32 initialScale)
{
cellOskDialog.todo("cellOskDialogExtSetInitialScale(initialScale=%f)", initialScale);
return CELL_OK;
}
error_code cellOskDialogExtInputDeviceLock()
{
cellOskDialog.todo("cellOskDialogExtInputDeviceLock()");
return CELL_OK;
}
error_code cellOskDialogExtSetBaseColor(f32 red, f32 blue, f32 green, f32 alpha)
{
cellOskDialog.todo("cellOskDialogExtSetBaseColor(red=%f, blue=%f, green=%f, alpha=%f)", red, blue, green, alpha);
return CELL_OK;
}
error_code cellOskDialogExtRegisterConfirmWordFilterCallback(vm::ptr<cellOskDialogConfirmWordFilterCallback> pCallback)
{
cellOskDialog.warning("cellOskDialogExtRegisterConfirmWordFilterCallback(pCallback=*0x%x)", pCallback);
if (!pCallback)
{
return CELL_OSKDIALOG_ERROR_PARAM;
}
if (const auto osk = g_fxo->get<osk_info>(); true)
{
osk->osk_confirm_callback = pCallback;
}
return CELL_OK;
}
error_code cellOskDialogExtUpdateInputText()
{
cellOskDialog.todo("cellOskDialogExtUpdateInputText()");
return CELL_OK;
}
error_code cellOskDialogExtDisableHalfByteKana()
{
cellOskDialog.todo("cellOskDialogExtDisableHalfByteKana()");
return CELL_OK;
}
error_code cellOskDialogExtSetPointerEnable(b8 enable)
{
cellOskDialog.todo("cellOskDialogExtSetPointerEnable(enable=%d)", enable);
return CELL_OK;
}
error_code cellOskDialogExtUpdatePointerDisplayPos(/*const CellOskDialogPoint pos*/)
{
cellOskDialog.todo("cellOskDialogExtUpdatePointerDisplayPos(posX=%f, posY=%f)"/*, pos.x, pos.y*/);
return CELL_OK;
}
error_code cellOskDialogExtEnableHalfByteKana()
{
cellOskDialog.todo("cellOskDialogExtEnableHalfByteKana()");
return CELL_OK;
}
error_code cellOskDialogExtRegisterForceFinishCallback(vm::ptr<cellOskDialogForceFinishCallback> pCallback)
{
cellOskDialog.todo("cellOskDialogExtRegisterForceFinishCallback(pCallback=*0x%x)", pCallback);
if (!pCallback)
{
return CELL_OSKDIALOG_ERROR_PARAM;
}
return CELL_OK;
}
void cellSysutil_OskDialog_init()
{
REG_FUNC(cellSysutil, cellOskDialogLoadAsync);
REG_FUNC(cellSysutil, cellOskDialogLoadAsyncExt);
REG_FUNC(cellSysutil, cellOskDialogUnloadAsync);
REG_FUNC(cellSysutil, cellOskDialogGetSize);
REG_FUNC(cellSysutil, cellOskDialogAbort);
REG_FUNC(cellSysutil, cellOskDialogSetDeviceMask);
REG_FUNC(cellSysutil, cellOskDialogSetSeparateWindowOption);
REG_FUNC(cellSysutil, cellOskDialogSetInitialInputDevice);
REG_FUNC(cellSysutil, cellOskDialogSetInitialKeyLayout);
REG_FUNC(cellSysutil, cellOskDialogDisableDimmer);
REG_FUNC(cellSysutil, cellOskDialogSetKeyLayoutOption);
REG_FUNC(cellSysutil, cellOskDialogAddSupportLanguage);
REG_FUNC(cellSysutil, cellOskDialogSetLayoutMode);
REG_FUNC(cellSysutil, cellOskDialogGetInputText);
}
DECLARE(ppu_module_manager::cellOskDialog)("cellOskExtUtility", []()
{
REG_FUNC(cellOskExtUtility, cellOskDialogExtInputDeviceUnlock);
REG_FUNC(cellOskExtUtility, cellOskDialogExtRegisterKeyboardEventHookCallback);
REG_FUNC(cellOskExtUtility, cellOskDialogExtRegisterKeyboardEventHookCallbackEx);
REG_FUNC(cellOskExtUtility, cellOskDialogExtAddJapaneseOptionDictionary);
REG_FUNC(cellOskExtUtility, cellOskDialogExtEnableClipboard);
REG_FUNC(cellOskExtUtility, cellOskDialogExtSendFinishMessage);
REG_FUNC(cellOskExtUtility, cellOskDialogExtAddOptionDictionary);
REG_FUNC(cellOskExtUtility, cellOskDialogExtSetInitialScale);
REG_FUNC(cellOskExtUtility, cellOskDialogExtInputDeviceLock);
REG_FUNC(cellOskExtUtility, cellOskDialogExtSetBaseColor);
REG_FUNC(cellOskExtUtility, cellOskDialogExtRegisterConfirmWordFilterCallback);
REG_FUNC(cellOskExtUtility, cellOskDialogExtUpdateInputText);
REG_FUNC(cellOskExtUtility, cellOskDialogExtDisableHalfByteKana);
REG_FUNC(cellOskExtUtility, cellOskDialogExtSetPointerEnable);
REG_FUNC(cellOskExtUtility, cellOskDialogExtUpdatePointerDisplayPos);
REG_FUNC(cellOskExtUtility, cellOskDialogExtEnableHalfByteKana);
REG_FUNC(cellOskExtUtility, cellOskDialogExtRegisterForceFinishCallback);
});