Browse Source
Merge pull request #1667 from DarkLordZach/swkbd
Merge pull request #1667 from DarkLordZach/swkbd
am: Implement HLE software keyboard appletnce_cpp
committed by
GitHub
19 changed files with 1133 additions and 106 deletions
-
9src/common/string_util.cpp
-
8src/common/string_util.h
-
6src/core/CMakeLists.txt
-
17src/core/core.cpp
-
5src/core/core.h
-
29src/core/frontend/applets/software_keyboard.cpp
-
54src/core/frontend/applets/software_keyboard.h
-
36src/core/hle/kernel/svc.cpp
-
331src/core/hle/service/am/am.cpp
-
29src/core/hle/service/am/am.h
-
115src/core/hle/service/am/applets/applets.cpp
-
94src/core/hle/service/am/applets/applets.h
-
161src/core/hle/service/am/applets/software_keyboard.cpp
-
69src/core/hle/service/am/applets/software_keyboard.h
-
2src/yuzu/CMakeLists.txt
-
152src/yuzu/applets/software_keyboard.cpp
-
80src/yuzu/applets/software_keyboard.h
-
31src/yuzu/main.cpp
-
11src/yuzu/main.h
@ -0,0 +1,29 @@ |
|||
// Copyright 2018 yuzu emulator team
|
|||
// Licensed under GPLv2 or any later version
|
|||
// Refer to the license.txt file included.
|
|||
|
|||
#include "common/logging/backend.h"
|
|||
#include "common/string_util.h"
|
|||
#include "core/frontend/applets/software_keyboard.h"
|
|||
|
|||
namespace Core::Frontend { |
|||
SoftwareKeyboardApplet::~SoftwareKeyboardApplet() = default; |
|||
|
|||
void DefaultSoftwareKeyboardApplet::RequestText( |
|||
std::function<void(std::optional<std::u16string>)> out, |
|||
SoftwareKeyboardParameters parameters) const { |
|||
if (parameters.initial_text.empty()) |
|||
out(u"yuzu"); |
|||
|
|||
out(parameters.initial_text); |
|||
} |
|||
|
|||
void DefaultSoftwareKeyboardApplet::SendTextCheckDialog( |
|||
std::u16string error_message, std::function<void()> finished_check) const { |
|||
LOG_WARNING(Service_AM, |
|||
"(STUBBED) called - Default fallback software keyboard does not support text " |
|||
"check! (error_message={})", |
|||
Common::UTF16ToUTF8(error_message)); |
|||
finished_check(); |
|||
} |
|||
} // namespace Core::Frontend
|
|||
@ -0,0 +1,54 @@ |
|||
// Copyright 2018 yuzu emulator team |
|||
// Licensed under GPLv2 or any later version |
|||
// Refer to the license.txt file included. |
|||
|
|||
#pragma once |
|||
|
|||
#include <functional> |
|||
#include <optional> |
|||
#include <string> |
|||
#include "common/bit_field.h" |
|||
#include "common/common_types.h" |
|||
|
|||
namespace Core::Frontend { |
|||
struct SoftwareKeyboardParameters { |
|||
std::u16string submit_text; |
|||
std::u16string header_text; |
|||
std::u16string sub_text; |
|||
std::u16string guide_text; |
|||
std::u16string initial_text; |
|||
std::size_t max_length; |
|||
bool password; |
|||
bool cursor_at_beginning; |
|||
|
|||
union { |
|||
u8 value; |
|||
|
|||
BitField<1, 1, u8> disable_space; |
|||
BitField<2, 1, u8> disable_address; |
|||
BitField<3, 1, u8> disable_percent; |
|||
BitField<4, 1, u8> disable_slash; |
|||
BitField<6, 1, u8> disable_number; |
|||
BitField<7, 1, u8> disable_download_code; |
|||
}; |
|||
}; |
|||
|
|||
class SoftwareKeyboardApplet { |
|||
public: |
|||
virtual ~SoftwareKeyboardApplet(); |
|||
|
|||
virtual void RequestText(std::function<void(std::optional<std::u16string>)> out, |
|||
SoftwareKeyboardParameters parameters) const = 0; |
|||
virtual void SendTextCheckDialog(std::u16string error_message, |
|||
std::function<void()> finished_check) const = 0; |
|||
}; |
|||
|
|||
class DefaultSoftwareKeyboardApplet final : public SoftwareKeyboardApplet { |
|||
public: |
|||
void RequestText(std::function<void(std::optional<std::u16string>)> out, |
|||
SoftwareKeyboardParameters parameters) const override; |
|||
void SendTextCheckDialog(std::u16string error_message, |
|||
std::function<void()> finished_check) const override; |
|||
}; |
|||
|
|||
} // namespace Core::Frontend |
|||
@ -0,0 +1,115 @@ |
|||
// Copyright 2018 yuzu emulator team
|
|||
// Licensed under GPLv2 or any later version
|
|||
// Refer to the license.txt file included.
|
|||
|
|||
#include <cstring>
|
|||
#include "common/assert.h"
|
|||
#include "core/core.h"
|
|||
#include "core/hle/kernel/event.h"
|
|||
#include "core/hle/kernel/server_port.h"
|
|||
#include "core/hle/service/am/am.h"
|
|||
#include "core/hle/service/am/applets/applets.h"
|
|||
|
|||
namespace Service::AM::Applets { |
|||
|
|||
AppletDataBroker::AppletDataBroker() { |
|||
auto& kernel = Core::System::GetInstance().Kernel(); |
|||
state_changed_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, |
|||
"ILibraryAppletAccessor:StateChangedEvent"); |
|||
pop_out_data_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, |
|||
"ILibraryAppletAccessor:PopDataOutEvent"); |
|||
pop_interactive_out_data_event = Kernel::Event::Create( |
|||
kernel, Kernel::ResetType::OneShot, "ILibraryAppletAccessor:PopInteractiveDataOutEvent"); |
|||
} |
|||
|
|||
AppletDataBroker::~AppletDataBroker() = default; |
|||
|
|||
std::unique_ptr<IStorage> AppletDataBroker::PopNormalDataToGame() { |
|||
if (out_channel.empty()) |
|||
return nullptr; |
|||
|
|||
auto out = std::move(out_channel.front()); |
|||
out_channel.pop(); |
|||
return out; |
|||
} |
|||
|
|||
std::unique_ptr<IStorage> AppletDataBroker::PopNormalDataToApplet() { |
|||
if (in_channel.empty()) |
|||
return nullptr; |
|||
|
|||
auto out = std::move(in_channel.front()); |
|||
in_channel.pop(); |
|||
return out; |
|||
} |
|||
|
|||
std::unique_ptr<IStorage> AppletDataBroker::PopInteractiveDataToGame() { |
|||
if (out_interactive_channel.empty()) |
|||
return nullptr; |
|||
|
|||
auto out = std::move(out_interactive_channel.front()); |
|||
out_interactive_channel.pop(); |
|||
return out; |
|||
} |
|||
|
|||
std::unique_ptr<IStorage> AppletDataBroker::PopInteractiveDataToApplet() { |
|||
if (in_interactive_channel.empty()) |
|||
return nullptr; |
|||
|
|||
auto out = std::move(in_interactive_channel.front()); |
|||
in_interactive_channel.pop(); |
|||
return out; |
|||
} |
|||
|
|||
void AppletDataBroker::PushNormalDataFromGame(IStorage storage) { |
|||
in_channel.push(std::make_unique<IStorage>(storage)); |
|||
} |
|||
|
|||
void AppletDataBroker::PushNormalDataFromApplet(IStorage storage) { |
|||
out_channel.push(std::make_unique<IStorage>(storage)); |
|||
pop_out_data_event->Signal(); |
|||
} |
|||
|
|||
void AppletDataBroker::PushInteractiveDataFromGame(IStorage storage) { |
|||
in_interactive_channel.push(std::make_unique<IStorage>(storage)); |
|||
} |
|||
|
|||
void AppletDataBroker::PushInteractiveDataFromApplet(IStorage storage) { |
|||
out_interactive_channel.push(std::make_unique<IStorage>(storage)); |
|||
pop_interactive_out_data_event->Signal(); |
|||
} |
|||
|
|||
void AppletDataBroker::SignalStateChanged() const { |
|||
state_changed_event->Signal(); |
|||
} |
|||
|
|||
Kernel::SharedPtr<Kernel::Event> AppletDataBroker::GetNormalDataEvent() const { |
|||
return pop_out_data_event; |
|||
} |
|||
|
|||
Kernel::SharedPtr<Kernel::Event> AppletDataBroker::GetInteractiveDataEvent() const { |
|||
return pop_interactive_out_data_event; |
|||
} |
|||
|
|||
Kernel::SharedPtr<Kernel::Event> AppletDataBroker::GetStateChangedEvent() const { |
|||
return state_changed_event; |
|||
} |
|||
|
|||
Applet::Applet() = default; |
|||
|
|||
Applet::~Applet() = default; |
|||
|
|||
void Applet::Initialize(std::shared_ptr<AppletDataBroker> broker_) { |
|||
broker = std::move(broker_); |
|||
|
|||
const auto common = broker->PopNormalDataToApplet(); |
|||
ASSERT(common != nullptr); |
|||
|
|||
const auto common_data = common->GetData(); |
|||
|
|||
ASSERT(common_data.size() >= sizeof(CommonArguments)); |
|||
std::memcpy(&common_args, common_data.data(), sizeof(CommonArguments)); |
|||
|
|||
initialized = true; |
|||
} |
|||
|
|||
} // namespace Service::AM::Applets
|
|||
@ -0,0 +1,94 @@ |
|||
// Copyright 2018 yuzu emulator team |
|||
// Licensed under GPLv2 or any later version |
|||
// Refer to the license.txt file included. |
|||
|
|||
#pragma once |
|||
|
|||
#include <functional> |
|||
#include <memory> |
|||
#include <queue> |
|||
#include "common/swap.h" |
|||
#include "core/hle/kernel/event.h" |
|||
|
|||
union ResultCode; |
|||
|
|||
namespace Service::AM { |
|||
|
|||
class IStorage; |
|||
|
|||
namespace Applets { |
|||
|
|||
class AppletDataBroker final { |
|||
public: |
|||
AppletDataBroker(); |
|||
~AppletDataBroker(); |
|||
|
|||
std::unique_ptr<IStorage> PopNormalDataToGame(); |
|||
std::unique_ptr<IStorage> PopNormalDataToApplet(); |
|||
|
|||
std::unique_ptr<IStorage> PopInteractiveDataToGame(); |
|||
std::unique_ptr<IStorage> PopInteractiveDataToApplet(); |
|||
|
|||
void PushNormalDataFromGame(IStorage storage); |
|||
void PushNormalDataFromApplet(IStorage storage); |
|||
|
|||
void PushInteractiveDataFromGame(IStorage storage); |
|||
void PushInteractiveDataFromApplet(IStorage storage); |
|||
|
|||
void SignalStateChanged() const; |
|||
|
|||
Kernel::SharedPtr<Kernel::Event> GetNormalDataEvent() const; |
|||
Kernel::SharedPtr<Kernel::Event> GetInteractiveDataEvent() const; |
|||
Kernel::SharedPtr<Kernel::Event> GetStateChangedEvent() const; |
|||
|
|||
private: |
|||
// Queues are named from applet's perspective |
|||
std::queue<std::unique_ptr<IStorage>> |
|||
in_channel; // PopNormalDataToApplet and PushNormalDataFromGame |
|||
std::queue<std::unique_ptr<IStorage>> |
|||
out_channel; // PopNormalDataToGame and PushNormalDataFromApplet |
|||
std::queue<std::unique_ptr<IStorage>> |
|||
in_interactive_channel; // PopInteractiveDataToApplet and PushInteractiveDataFromGame |
|||
std::queue<std::unique_ptr<IStorage>> |
|||
out_interactive_channel; // PopInteractiveDataToGame and PushInteractiveDataFromApplet |
|||
|
|||
Kernel::SharedPtr<Kernel::Event> state_changed_event; |
|||
Kernel::SharedPtr<Kernel::Event> pop_out_data_event; // Signaled on PushNormalDataFromApplet |
|||
Kernel::SharedPtr<Kernel::Event> |
|||
pop_interactive_out_data_event; // Signaled on PushInteractiveDataFromApplet |
|||
}; |
|||
|
|||
class Applet { |
|||
public: |
|||
Applet(); |
|||
virtual ~Applet(); |
|||
|
|||
virtual void Initialize(std::shared_ptr<AppletDataBroker> broker); |
|||
|
|||
virtual bool TransactionComplete() const = 0; |
|||
virtual ResultCode GetStatus() const = 0; |
|||
virtual void ExecuteInteractive() = 0; |
|||
virtual void Execute() = 0; |
|||
|
|||
bool IsInitialized() const { |
|||
return initialized; |
|||
} |
|||
|
|||
protected: |
|||
struct CommonArguments { |
|||
u32_le arguments_version; |
|||
u32_le size; |
|||
u32_le library_version; |
|||
u32_le theme_color; |
|||
u8 play_startup_sound; |
|||
u64_le system_tick; |
|||
}; |
|||
static_assert(sizeof(CommonArguments) == 0x20, "CommonArguments has incorrect size."); |
|||
|
|||
CommonArguments common_args; |
|||
std::shared_ptr<AppletDataBroker> broker; |
|||
bool initialized = false; |
|||
}; |
|||
|
|||
} // namespace Applets |
|||
} // namespace Service::AM |
|||
@ -0,0 +1,161 @@ |
|||
// Copyright 2018 yuzu emulator team
|
|||
// Licensed under GPLv2 or any later version
|
|||
// Refer to the license.txt file included.
|
|||
|
|||
#include <cstring>
|
|||
#include "common/assert.h"
|
|||
#include "common/string_util.h"
|
|||
#include "core/core.h"
|
|||
#include "core/frontend/applets/software_keyboard.h"
|
|||
#include "core/hle/service/am/am.h"
|
|||
#include "core/hle/service/am/applets/software_keyboard.h"
|
|||
|
|||
namespace Service::AM::Applets { |
|||
|
|||
constexpr std::size_t SWKBD_OUTPUT_BUFFER_SIZE = 0x7D8; |
|||
constexpr std::size_t SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE = 0x7D4; |
|||
constexpr std::size_t DEFAULT_MAX_LENGTH = 500; |
|||
constexpr bool INTERACTIVE_STATUS_OK = false; |
|||
|
|||
static Core::Frontend::SoftwareKeyboardParameters ConvertToFrontendParameters( |
|||
KeyboardConfig config, std::u16string initial_text) { |
|||
Core::Frontend::SoftwareKeyboardParameters params{}; |
|||
|
|||
params.submit_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( |
|||
config.submit_text.data(), config.submit_text.size()); |
|||
params.header_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( |
|||
config.header_text.data(), config.header_text.size()); |
|||
params.sub_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(config.sub_text.data(), |
|||
config.sub_text.size()); |
|||
params.guide_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(config.guide_text.data(), |
|||
config.guide_text.size()); |
|||
params.initial_text = initial_text; |
|||
params.max_length = config.length_limit == 0 ? DEFAULT_MAX_LENGTH : config.length_limit; |
|||
params.password = static_cast<bool>(config.is_password); |
|||
params.cursor_at_beginning = static_cast<bool>(config.initial_cursor_position); |
|||
params.value = static_cast<u8>(config.keyset_disable_bitmask); |
|||
|
|||
return params; |
|||
} |
|||
|
|||
SoftwareKeyboard::SoftwareKeyboard() = default; |
|||
|
|||
SoftwareKeyboard::~SoftwareKeyboard() = default; |
|||
|
|||
void SoftwareKeyboard::Initialize(std::shared_ptr<AppletDataBroker> broker_) { |
|||
complete = false; |
|||
initial_text.clear(); |
|||
final_data.clear(); |
|||
|
|||
Applet::Initialize(std::move(broker_)); |
|||
|
|||
const auto keyboard_config_storage = broker->PopNormalDataToApplet(); |
|||
ASSERT(keyboard_config_storage != nullptr); |
|||
const auto& keyboard_config = keyboard_config_storage->GetData(); |
|||
|
|||
ASSERT(keyboard_config.size() >= sizeof(KeyboardConfig)); |
|||
std::memcpy(&config, keyboard_config.data(), sizeof(KeyboardConfig)); |
|||
|
|||
const auto work_buffer_storage = broker->PopNormalDataToApplet(); |
|||
ASSERT(work_buffer_storage != nullptr); |
|||
const auto& work_buffer = work_buffer_storage->GetData(); |
|||
|
|||
if (config.initial_string_size == 0) |
|||
return; |
|||
|
|||
std::vector<char16_t> string(config.initial_string_size); |
|||
std::memcpy(string.data(), work_buffer.data() + config.initial_string_offset, |
|||
string.size() * 2); |
|||
initial_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(string.data(), string.size()); |
|||
} |
|||
|
|||
bool SoftwareKeyboard::TransactionComplete() const { |
|||
return complete; |
|||
} |
|||
|
|||
ResultCode SoftwareKeyboard::GetStatus() const { |
|||
return RESULT_SUCCESS; |
|||
} |
|||
|
|||
void SoftwareKeyboard::ExecuteInteractive() { |
|||
if (complete) |
|||
return; |
|||
|
|||
const auto storage = broker->PopInteractiveDataToApplet(); |
|||
ASSERT(storage != nullptr); |
|||
const auto data = storage->GetData(); |
|||
const auto status = static_cast<bool>(data[0]); |
|||
|
|||
if (status == INTERACTIVE_STATUS_OK) { |
|||
complete = true; |
|||
} else { |
|||
const auto& frontend{Core::System::GetInstance().GetSoftwareKeyboard()}; |
|||
|
|||
std::array<char16_t, SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE / 2 - 2> string; |
|||
std::memcpy(string.data(), data.data() + 4, string.size() * 2); |
|||
frontend.SendTextCheckDialog( |
|||
Common::UTF16StringFromFixedZeroTerminatedBuffer(string.data(), string.size()), |
|||
[this] { broker->SignalStateChanged(); }); |
|||
} |
|||
} |
|||
|
|||
void SoftwareKeyboard::Execute() { |
|||
if (complete) { |
|||
broker->PushNormalDataFromApplet(IStorage{final_data}); |
|||
return; |
|||
} |
|||
|
|||
const auto& frontend{Core::System::GetInstance().GetSoftwareKeyboard()}; |
|||
|
|||
const auto parameters = ConvertToFrontendParameters(config, initial_text); |
|||
|
|||
frontend.RequestText([this](std::optional<std::u16string> text) { WriteText(text); }, |
|||
parameters); |
|||
} |
|||
|
|||
void SoftwareKeyboard::WriteText(std::optional<std::u16string> text) { |
|||
std::vector<u8> output_main(SWKBD_OUTPUT_BUFFER_SIZE); |
|||
|
|||
if (text.has_value()) { |
|||
std::vector<u8> output_sub(SWKBD_OUTPUT_BUFFER_SIZE); |
|||
|
|||
if (config.utf_8) { |
|||
const u64 size = text->size() + 8; |
|||
const auto new_text = Common::UTF16ToUTF8(*text); |
|||
|
|||
std::memcpy(output_sub.data(), &size, sizeof(u64)); |
|||
std::memcpy(output_sub.data() + 8, new_text.data(), |
|||
std::min(new_text.size(), SWKBD_OUTPUT_BUFFER_SIZE - 8)); |
|||
|
|||
output_main[0] = INTERACTIVE_STATUS_OK; |
|||
std::memcpy(output_main.data() + 4, new_text.data(), |
|||
std::min(new_text.size(), SWKBD_OUTPUT_BUFFER_SIZE - 4)); |
|||
} else { |
|||
const u64 size = text->size() * 2 + 8; |
|||
std::memcpy(output_sub.data(), &size, sizeof(u64)); |
|||
std::memcpy(output_sub.data() + 8, text->data(), |
|||
std::min(text->size() * 2, SWKBD_OUTPUT_BUFFER_SIZE - 8)); |
|||
|
|||
output_main[0] = INTERACTIVE_STATUS_OK; |
|||
std::memcpy(output_main.data() + 4, text->data(), |
|||
std::min(text->size() * 2, SWKBD_OUTPUT_BUFFER_SIZE - 4)); |
|||
} |
|||
|
|||
complete = !config.text_check; |
|||
final_data = output_main; |
|||
|
|||
if (complete) { |
|||
broker->PushNormalDataFromApplet(IStorage{output_main}); |
|||
} else { |
|||
broker->PushInteractiveDataFromApplet(IStorage{output_sub}); |
|||
} |
|||
|
|||
broker->SignalStateChanged(); |
|||
} else { |
|||
output_main[0] = 1; |
|||
complete = true; |
|||
broker->PushNormalDataFromApplet(IStorage{output_main}); |
|||
broker->SignalStateChanged(); |
|||
} |
|||
} |
|||
} // namespace Service::AM::Applets
|
|||
@ -0,0 +1,69 @@ |
|||
// Copyright 2018 yuzu emulator team |
|||
// Licensed under GPLv2 or any later version |
|||
// Refer to the license.txt file included. |
|||
|
|||
#pragma once |
|||
|
|||
#include "common/common_funcs.h" |
|||
#include "core/hle/service/am/am.h" |
|||
#include "core/hle/service/am/applets/applets.h" |
|||
|
|||
namespace Service::AM::Applets { |
|||
|
|||
enum class KeysetDisable : u32 { |
|||
Space = 0x02, |
|||
Address = 0x04, |
|||
Percent = 0x08, |
|||
Slashes = 0x10, |
|||
Numbers = 0x40, |
|||
DownloadCode = 0x80, |
|||
}; |
|||
|
|||
struct KeyboardConfig { |
|||
INSERT_PADDING_BYTES(4); |
|||
std::array<char16_t, 9> submit_text; |
|||
u16_le left_symbol_key; |
|||
u16_le right_symbol_key; |
|||
INSERT_PADDING_BYTES(1); |
|||
KeysetDisable keyset_disable_bitmask; |
|||
u32_le initial_cursor_position; |
|||
std::array<char16_t, 65> header_text; |
|||
std::array<char16_t, 129> sub_text; |
|||
std::array<char16_t, 257> guide_text; |
|||
u32_le length_limit; |
|||
INSERT_PADDING_BYTES(4); |
|||
u32_le is_password; |
|||
INSERT_PADDING_BYTES(5); |
|||
bool utf_8; |
|||
bool draw_background; |
|||
u32_le initial_string_offset; |
|||
u32_le initial_string_size; |
|||
u32_le user_dictionary_offset; |
|||
u32_le user_dictionary_size; |
|||
bool text_check; |
|||
u64_le text_check_callback; |
|||
}; |
|||
static_assert(sizeof(KeyboardConfig) == 0x3E0, "KeyboardConfig has incorrect size."); |
|||
|
|||
class SoftwareKeyboard final : public Applet { |
|||
public: |
|||
SoftwareKeyboard(); |
|||
~SoftwareKeyboard() override; |
|||
|
|||
void Initialize(std::shared_ptr<AppletDataBroker> broker) override; |
|||
|
|||
bool TransactionComplete() const override; |
|||
ResultCode GetStatus() const override; |
|||
void ExecuteInteractive() override; |
|||
void Execute() override; |
|||
|
|||
void WriteText(std::optional<std::u16string> text); |
|||
|
|||
private: |
|||
KeyboardConfig config; |
|||
std::u16string initial_text; |
|||
bool complete = false; |
|||
std::vector<u8> final_data; |
|||
}; |
|||
|
|||
} // namespace Service::AM::Applets |
|||
@ -0,0 +1,152 @@ |
|||
// Copyright 2018 yuzu Emulator Project
|
|||
// Licensed under GPLv2 or any later version
|
|||
// Refer to the license.txt file included.
|
|||
|
|||
#include <algorithm>
|
|||
#include <mutex>
|
|||
#include <QDialogButtonBox>
|
|||
#include <QFont>
|
|||
#include <QLabel>
|
|||
#include <QLineEdit>
|
|||
#include <QVBoxLayout>
|
|||
#include "core/hle/lock.h"
|
|||
#include "yuzu/applets/software_keyboard.h"
|
|||
#include "yuzu/main.h"
|
|||
|
|||
QtSoftwareKeyboardValidator::QtSoftwareKeyboardValidator( |
|||
Core::Frontend::SoftwareKeyboardParameters parameters) |
|||
: parameters(std::move(parameters)) {} |
|||
|
|||
QValidator::State QtSoftwareKeyboardValidator::validate(QString& input, int& pos) const { |
|||
if (input.size() > parameters.max_length) |
|||
return Invalid; |
|||
if (parameters.disable_space && input.contains(' ')) |
|||
return Invalid; |
|||
if (parameters.disable_address && input.contains('@')) |
|||
return Invalid; |
|||
if (parameters.disable_percent && input.contains('%')) |
|||
return Invalid; |
|||
if (parameters.disable_slash && (input.contains('/') || input.contains('\\'))) |
|||
return Invalid; |
|||
if (parameters.disable_number && |
|||
std::any_of(input.begin(), input.end(), [](QChar c) { return c.isDigit(); })) { |
|||
return Invalid; |
|||
} |
|||
|
|||
if (parameters.disable_download_code && |
|||
std::any_of(input.begin(), input.end(), [](QChar c) { return c == 'O' || c == 'I'; })) { |
|||
return Invalid; |
|||
} |
|||
|
|||
return Acceptable; |
|||
} |
|||
|
|||
QtSoftwareKeyboardDialog::QtSoftwareKeyboardDialog( |
|||
QWidget* parent, Core::Frontend::SoftwareKeyboardParameters parameters_) |
|||
: QDialog(parent), parameters(std::move(parameters_)) { |
|||
layout = new QVBoxLayout; |
|||
|
|||
header_label = new QLabel(QString::fromStdU16String(parameters.header_text)); |
|||
header_label->setFont({header_label->font().family(), 11, QFont::Bold}); |
|||
if (header_label->text().isEmpty()) |
|||
header_label->setText(tr("Enter text:")); |
|||
|
|||
sub_label = new QLabel(QString::fromStdU16String(parameters.sub_text)); |
|||
sub_label->setFont({sub_label->font().family(), sub_label->font().pointSize(), |
|||
sub_label->font().weight(), true}); |
|||
sub_label->setHidden(parameters.sub_text.empty()); |
|||
|
|||
guide_label = new QLabel(QString::fromStdU16String(parameters.guide_text)); |
|||
guide_label->setHidden(parameters.guide_text.empty()); |
|||
|
|||
length_label = new QLabel(QStringLiteral("0/%1").arg(parameters.max_length)); |
|||
length_label->setAlignment(Qt::AlignRight); |
|||
length_label->setFont({length_label->font().family(), 8}); |
|||
|
|||
line_edit = new QLineEdit; |
|||
line_edit->setValidator(new QtSoftwareKeyboardValidator(parameters)); |
|||
line_edit->setMaxLength(static_cast<int>(parameters.max_length)); |
|||
line_edit->setText(QString::fromStdU16String(parameters.initial_text)); |
|||
line_edit->setCursorPosition( |
|||
parameters.cursor_at_beginning ? 0 : static_cast<int>(parameters.initial_text.size())); |
|||
line_edit->setEchoMode(parameters.password ? QLineEdit::Password : QLineEdit::Normal); |
|||
|
|||
connect(line_edit, &QLineEdit::textChanged, this, [this](const QString& text) { |
|||
length_label->setText(QStringLiteral("%1/%2").arg(text.size()).arg(parameters.max_length)); |
|||
}); |
|||
|
|||
buttons = new QDialogButtonBox; |
|||
buttons->addButton(tr("Cancel"), QDialogButtonBox::RejectRole); |
|||
buttons->addButton(parameters.submit_text.empty() |
|||
? tr("OK") |
|||
: QString::fromStdU16String(parameters.submit_text), |
|||
QDialogButtonBox::AcceptRole); |
|||
|
|||
connect(buttons, &QDialogButtonBox::accepted, this, &QtSoftwareKeyboardDialog::Submit); |
|||
connect(buttons, &QDialogButtonBox::rejected, this, &QtSoftwareKeyboardDialog::Reject); |
|||
layout->addWidget(header_label); |
|||
layout->addWidget(sub_label); |
|||
layout->addWidget(guide_label); |
|||
layout->addWidget(length_label); |
|||
layout->addWidget(line_edit); |
|||
layout->addWidget(buttons); |
|||
setLayout(layout); |
|||
setWindowTitle(tr("Software Keyboard")); |
|||
} |
|||
|
|||
QtSoftwareKeyboardDialog::~QtSoftwareKeyboardDialog() = default; |
|||
|
|||
void QtSoftwareKeyboardDialog::Submit() { |
|||
ok = true; |
|||
text = line_edit->text().toStdU16String(); |
|||
accept(); |
|||
} |
|||
|
|||
void QtSoftwareKeyboardDialog::Reject() { |
|||
ok = false; |
|||
text.clear(); |
|||
accept(); |
|||
} |
|||
|
|||
std::u16string QtSoftwareKeyboardDialog::GetText() const { |
|||
return text; |
|||
} |
|||
|
|||
bool QtSoftwareKeyboardDialog::GetStatus() const { |
|||
return ok; |
|||
} |
|||
|
|||
QtSoftwareKeyboard::QtSoftwareKeyboard(GMainWindow& main_window) { |
|||
connect(this, &QtSoftwareKeyboard::MainWindowGetText, &main_window, |
|||
&GMainWindow::SoftwareKeyboardGetText, Qt::QueuedConnection); |
|||
connect(this, &QtSoftwareKeyboard::MainWindowTextCheckDialog, &main_window, |
|||
&GMainWindow::SoftwareKeyboardInvokeCheckDialog, Qt::BlockingQueuedConnection); |
|||
connect(&main_window, &GMainWindow::SoftwareKeyboardFinishedText, this, |
|||
&QtSoftwareKeyboard::MainWindowFinishedText, Qt::QueuedConnection); |
|||
} |
|||
|
|||
QtSoftwareKeyboard::~QtSoftwareKeyboard() = default; |
|||
|
|||
void QtSoftwareKeyboard::RequestText(std::function<void(std::optional<std::u16string>)> out, |
|||
Core::Frontend::SoftwareKeyboardParameters parameters) const { |
|||
text_output = out; |
|||
emit MainWindowGetText(parameters); |
|||
} |
|||
|
|||
void QtSoftwareKeyboard::SendTextCheckDialog(std::u16string error_message, |
|||
std::function<void()> finished_check) const { |
|||
this->finished_check = finished_check; |
|||
emit MainWindowTextCheckDialog(error_message); |
|||
} |
|||
|
|||
void QtSoftwareKeyboard::MainWindowFinishedText(std::optional<std::u16string> text) { |
|||
// Acquire the HLE mutex
|
|||
std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); |
|||
text_output(text); |
|||
} |
|||
|
|||
void QtSoftwareKeyboard::MainWindowFinishedCheckDialog() { |
|||
// Acquire the HLE mutex
|
|||
std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); |
|||
finished_check(); |
|||
} |
|||
@ -0,0 +1,80 @@ |
|||
// Copyright 2018 yuzu Emulator Project |
|||
// Licensed under GPLv2 or any later version |
|||
// Refer to the license.txt file included. |
|||
|
|||
#pragma once |
|||
|
|||
#include <QDialog> |
|||
#include <QValidator> |
|||
#include "common/assert.h" |
|||
#include "core/frontend/applets/software_keyboard.h" |
|||
|
|||
class GMainWindow; |
|||
class QDialogButtonBox; |
|||
class QLabel; |
|||
class QLineEdit; |
|||
class QVBoxLayout; |
|||
class QtSoftwareKeyboard; |
|||
|
|||
class QtSoftwareKeyboardValidator final : public QValidator { |
|||
public: |
|||
explicit QtSoftwareKeyboardValidator(Core::Frontend::SoftwareKeyboardParameters parameters); |
|||
State validate(QString& input, int& pos) const override; |
|||
|
|||
private: |
|||
Core::Frontend::SoftwareKeyboardParameters parameters; |
|||
}; |
|||
|
|||
class QtSoftwareKeyboardDialog final : public QDialog { |
|||
Q_OBJECT |
|||
|
|||
public: |
|||
QtSoftwareKeyboardDialog(QWidget* parent, |
|||
Core::Frontend::SoftwareKeyboardParameters parameters); |
|||
~QtSoftwareKeyboardDialog() override; |
|||
|
|||
void Submit(); |
|||
void Reject(); |
|||
|
|||
std::u16string GetText() const; |
|||
bool GetStatus() const; |
|||
|
|||
private: |
|||
bool ok = false; |
|||
std::u16string text; |
|||
|
|||
QDialogButtonBox* buttons; |
|||
QLabel* header_label; |
|||
QLabel* sub_label; |
|||
QLabel* guide_label; |
|||
QLabel* length_label; |
|||
QLineEdit* line_edit; |
|||
QVBoxLayout* layout; |
|||
|
|||
Core::Frontend::SoftwareKeyboardParameters parameters; |
|||
}; |
|||
|
|||
class QtSoftwareKeyboard final : public QObject, public Core::Frontend::SoftwareKeyboardApplet { |
|||
Q_OBJECT |
|||
|
|||
public: |
|||
explicit QtSoftwareKeyboard(GMainWindow& parent); |
|||
~QtSoftwareKeyboard() override; |
|||
|
|||
void RequestText(std::function<void(std::optional<std::u16string>)> out, |
|||
Core::Frontend::SoftwareKeyboardParameters parameters) const override; |
|||
void SendTextCheckDialog(std::u16string error_message, |
|||
std::function<void()> finished_check) const override; |
|||
|
|||
signals: |
|||
void MainWindowGetText(Core::Frontend::SoftwareKeyboardParameters parameters) const; |
|||
void MainWindowTextCheckDialog(std::u16string error_message) const; |
|||
|
|||
public slots: |
|||
void MainWindowFinishedText(std::optional<std::u16string> text); |
|||
void MainWindowFinishedCheckDialog(); |
|||
|
|||
private: |
|||
mutable std::function<void(std::optional<std::u16string>)> text_output; |
|||
mutable std::function<void()> finished_check; |
|||
}; |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue