diff --git a/rpcs3/Emu/Cell/Modules/cellOskDialog.cpp b/rpcs3/Emu/Cell/Modules/cellOskDialog.cpp index 9fed61aa40..f552bba0e1 100644 --- a/rpcs3/Emu/Cell/Modules/cellOskDialog.cpp +++ b/rpcs3/Emu/Cell/Modules/cellOskDialog.cpp @@ -35,7 +35,7 @@ error_code cellOskDialogLoadAsync(u32 container, vm::ptr dia { cellOskDialog.warning("cellOskDialogLoadAsync(container=0x%x, dialogParam=*0x%x, inputFieldInfo=*0x%x)", container, dialogParam, inputFieldInfo); - if (!inputFieldInfo || !inputFieldInfo->message || !inputFieldInfo->init_text || inputFieldInfo->limit_length > CELL_OSKDIALOG_STRING_SIZE) + if (!dialogParam || !inputFieldInfo || !inputFieldInfo->message || !inputFieldInfo->init_text || inputFieldInfo->limit_length > CELL_OSKDIALOG_STRING_SIZE) { return CELL_OSKDIALOG_ERROR_PARAM; } @@ -52,8 +52,11 @@ error_code cellOskDialogLoadAsync(u32 container, vm::ptr dia return CELL_SYSUTIL_ERROR_BUSY; } - // Get max length of the return value + // Get the OSK options u32 maxLength = (inputFieldInfo->limit_length >= CELL_OSKDIALOG_STRING_SIZE) ? 511 : (u32)inputFieldInfo->limit_length; + u32 options = dialogParam->prohibitFlgs; + bool use_seperate_windows = false; // TODO + bool keep_dialog_open = false; // TODO maybe same as use_seperate_windows // Get init text and prepare return value osk->osk_input_result = CELL_OSKDIALOG_INPUT_FIELD_RESULT_OK; @@ -84,28 +87,15 @@ error_code cellOskDialogLoadAsync(u32 container, vm::ptr dia bool result = false; - osk->on_close = [&maxLength, wptr = std::weak_ptr(osk)](s32 status) + osk->on_close = [maxLength, use_seperate_windows, keep_dialog_open, wptr = std::weak_ptr(osk)](s32 status) { const auto osk = wptr.lock(); osk->state = OskDialogState::Close; - if (status != CELL_MSGDIALOG_BUTTON_OK) - { - osk->osk_input_result = CELL_OSKDIALOG_INPUT_FIELD_RESULT_CANCELED; + const bool accepted = status == CELL_MSGDIALOG_BUTTON_OK; - if (false/* TODO: check for seperate window */) - { - sysutil_send_system_cmd(CELL_SYSUTIL_OSKDIALOG_INPUT_CANCELED, 0); - } - } - else + if (accepted) { - if (osk->osk_text[0] == 0 && false/* TODO: check for seperate window */) - { - 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; - } - if (osk->osk_confirm_callback) { vm::ptr string_to_send = vm::cast(vm::alloc(CELL_OSKDIALOG_STRING_SIZE * 2, vm::main)); @@ -133,24 +123,68 @@ error_code cellOskDialogLoadAsync(u32 container, vm::ptr dia return 0; }); + // wait for check callback while (done == false) { - // wait for check callback + std::this_thread::yield(); } // reset this here (just to make sure it's null) osk->osk_confirm_callback = vm::null; } + + if (use_seperate_windows) + { + if (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_OK; + } + } + else + { + osk->osk_input_result = CELL_OSKDIALOG_INPUT_FIELD_RESULT_CANCELED; } - sysutil_send_system_cmd(CELL_SYSUTIL_OSKDIALOG_FINISHED, 0); + // Send OSK status + if (use_seperate_windows) + { + if (accepted) + { + if (keep_dialog_open) + { + sysutil_send_system_cmd(CELL_SYSUTIL_OSKDIALOG_INPUT_ENTERED, 0); + } + else + { + sysutil_send_system_cmd(CELL_SYSUTIL_OSKDIALOG_FINISHED, 0); + } + } + else + { + sysutil_send_system_cmd(CELL_SYSUTIL_OSKDIALOG_INPUT_CANCELED, 0); + } + } + else + { + sysutil_send_system_cmd(CELL_SYSUTIL_OSKDIALOG_FINISHED, 0); + } pad::SetIntercepted(false); }; - osk->on_osk_input_entered = [&]() + osk->on_osk_input_entered = [=]() { - if (false/* TODO: check for seperate window */) + if (use_seperate_windows) { sysutil_send_system_cmd(CELL_SYSUTIL_OSKDIALOG_INPUT_ENTERED, 0); } @@ -158,9 +192,9 @@ error_code cellOskDialogLoadAsync(u32 container, vm::ptr dia pad::SetIntercepted(true); - Emu.CallAfter([&]() + Emu.CallAfter([=, &result]() { - osk->Create("On Screen Keyboard", message, osk->osk_text, maxLength); + osk->Create("On Screen Keyboard", message, osk->osk_text, maxLength, options); result = true; }); @@ -274,7 +308,8 @@ error_code cellOskDialogAbort() } osk->osk_input_result = CELL_OSKDIALOG_INPUT_FIELD_RESULT_ABORT; - sysutil_send_system_cmd(CELL_SYSUTIL_OSKDIALOG_FINISHED, 0); + osk->Close(false); + return CELL_OK; } @@ -365,9 +400,20 @@ error_code cellOskDialogExtEnableClipboard() return CELL_OK; } -error_code cellOskDialogExtSendFinishMessage(s32 /*CellOskDialogFinishReason*/ finishReason) +error_code cellOskDialogExtSendFinishMessage(u32 /*CellOskDialogFinishReason*/ finishReason) { - cellOskDialog.todo("cellOskDialogExtSendFinishMessage(finishReason=%d)", finishReason); + cellOskDialog.warning("cellOskDialogExtSendFinishMessage(finishReason=%d)", finishReason); + + const auto osk = fxm::get(); + + // 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; } diff --git a/rpcs3/Emu/Cell/Modules/cellOskDialog.h b/rpcs3/Emu/Cell/Modules/cellOskDialog.h index 71ec20c389..c502429fa8 100644 --- a/rpcs3/Emu/Cell/Modules/cellOskDialog.h +++ b/rpcs3/Emu/Cell/Modules/cellOskDialog.h @@ -243,7 +243,8 @@ enum class OskDialogState class OskDialogBase { public: - virtual void Create(const std::string& title, const std::u16string& message, char16_t* init_text, u32 charlimit) = 0; + virtual void Create(const std::string& title, const std::u16string& message, char16_t* init_text, u32 charlimit, u32 options) = 0; + virtual void Close(bool accepted) = 0; virtual ~OskDialogBase(); std::function on_close; diff --git a/rpcs3/rpcs3qt/osk_dialog_frame.cpp b/rpcs3/rpcs3qt/osk_dialog_frame.cpp index b093931958..e7db833ce6 100644 --- a/rpcs3/rpcs3qt/osk_dialog_frame.cpp +++ b/rpcs3/rpcs3qt/osk_dialog_frame.cpp @@ -1,12 +1,14 @@ - + #include "osk_dialog_frame.h" #include "Emu/Cell/Modules/cellMsgDialog.h" #include #include +#include #include #include #include +#include constexpr auto qstr = QString::fromStdString; @@ -22,7 +24,7 @@ osk_dialog_frame::~osk_dialog_frame() } } -void osk_dialog_frame::Create(const std::string& title, const std::u16string& message, char16_t* init_text, u32 charlimit) +void osk_dialog_frame::Create(const std::string& title, const std::u16string& message, char16_t* init_text, u32 charlimit, u32 options) { state = OskDialogState::Open; @@ -43,15 +45,9 @@ void osk_dialog_frame::Create(const std::string& title, const std::u16string& me // Message QLabel* message_label = new QLabel(QString::fromStdU16String(message)); - // Text Input - QLineEdit* input = new QLineEdit(m_dialog); - input->setFixedWidth(lineEditWidth()); - input->setMaxLength(charlimit); - input->setText(QString::fromStdU16String(std::u16string(init_text))); - input->setFocus(); - // Text Input Counter - QLabel* inputCount = new QLabel(QString("%1/%2").arg(input->text().length()).arg(charlimit)); + const QString text = QString::fromStdU16String(std::u16string(init_text)); + QLabel* inputCount = new QLabel(QString("%1/%2").arg(text.length()).arg(charlimit)); // Ok Button QPushButton* button_ok = new QPushButton("Ok", m_dialog); @@ -66,7 +62,63 @@ void osk_dialog_frame::Create(const std::string& title, const std::u16string& me // Input Layout QHBoxLayout* inputLayout = new QHBoxLayout; inputLayout->setAlignment(Qt::AlignHCenter); - inputLayout->addWidget(input); + + // Text Input + if (options & CELL_OSKDIALOG_NO_RETURN) + { + QLineEdit* input = new QLineEdit(m_dialog); + input->setFixedWidth(lineEditWidth()); + input->setMaxLength(charlimit); + input->setText(text); + input->setFocus(); + + if (options & CELL_OSKDIALOG_NO_SPACE) + { + input->setValidator(new QRegExpValidator(QRegExp("^\S*$"), this)); + } + + connect(input, &QLineEdit::textChanged, [=](const QString& text) + { + inputCount->setText(QString("%1/%2").arg(text.length()).arg(charlimit)); + SetOskText(text); + on_osk_input_entered(); + }); + connect(input, &QLineEdit::returnPressed, m_dialog, &QDialog::accept); + + inputLayout->addWidget(input); + } + else + { + QTextEdit* input = new QTextEdit(m_dialog); + input->setFixedWidth(lineEditWidth()); + input->setText(text); + input->setFocus(); + + connect(input, &QTextEdit::textChanged, [=]() + { + QString text = input->toPlainText(); + text.chop(text.length() - charlimit); + + if (options & CELL_OSKDIALOG_NO_SPACE) + { + text.remove(QRegExp("\\s+")); + } + + input->setPlainText(text); + + QTextCursor cursor = input->textCursor(); + cursor.setPosition(QTextCursor::End); + input->setTextCursor(cursor); + + inputCount->setText(QString("%1/%2").arg(text.length()).arg(charlimit)); + SetOskText(text); + + on_osk_input_entered(); + }); + + inputLayout->addWidget(input); + } + inputLayout->addWidget(inputCount); QFormLayout* layout = new QFormLayout(m_dialog); @@ -77,18 +129,10 @@ void osk_dialog_frame::Create(const std::string& title, const std::u16string& me m_dialog->setLayout(layout); // Events - connect(input, &QLineEdit::textChanged, [=](const QString& text) - { - inputCount->setText(QString("%1/%2").arg(text.length()).arg(charlimit)); - on_osk_input_entered(); - }); - - connect(input, &QLineEdit::returnPressed, m_dialog, &QDialog::accept); connect(button_ok, &QAbstractButton::clicked, m_dialog, &QDialog::accept); connect(m_dialog, &QDialog::accepted, [=] { - std::memcpy(osk_text, reinterpret_cast(input->text().constData()), ((input->text()).size() + 1) * sizeof(char16_t)); on_close(CELL_MSGDIALOG_BUTTON_OK); }); @@ -101,3 +145,16 @@ void osk_dialog_frame::Create(const std::string& title, const std::u16string& me m_dialog->layout()->setSizeConstraint(QLayout::SetFixedSize); m_dialog->show(); } + +void osk_dialog_frame::SetOskText(const QString& text) +{ + std::memcpy(osk_text, reinterpret_cast(text.constData()), size_t(text.size() + 1) * sizeof(char16_t)); +} + +void osk_dialog_frame::Close(bool accepted) +{ + if (m_dialog) + { + m_dialog->done(accepted ? QDialog::Accepted : QDialog::Rejected); + } +} diff --git a/rpcs3/rpcs3qt/osk_dialog_frame.h b/rpcs3/rpcs3qt/osk_dialog_frame.h index a6e0ba1b1a..c98174f21a 100644 --- a/rpcs3/rpcs3qt/osk_dialog_frame.h +++ b/rpcs3/rpcs3qt/osk_dialog_frame.h @@ -1,4 +1,4 @@ -#pragma once +#pragma once #include "stdafx.h" #include "Emu/Memory/vm.h" @@ -15,8 +15,11 @@ class osk_dialog_frame : public QObject, public OskDialogBase public: osk_dialog_frame(); ~osk_dialog_frame(); - virtual void Create(const std::string& title, const std::u16string& message, char16_t* init_text, u32 charlimit) override; + virtual void Create(const std::string& title, const std::u16string& message, char16_t* init_text, u32 charlimit, u32 options) override; + virtual void Close(bool accepted) override; private: + void SetOskText(const QString& text); + custom_dialog* m_dialog = nullptr; };