summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/CMakeLists.txt1
-rw-r--r--src/core/frontend/applets/software_keyboard.cpp148
-rw-r--r--src/core/frontend/applets/software_keyboard.h118
-rw-r--r--src/core/frontend/input_interpreter.cpp15
-rw-r--r--src/core/frontend/input_interpreter.h3
-rw-r--r--src/core/hle/kernel/hle_ipc.cpp8
-rw-r--r--src/core/hle/kernel/hle_ipc.h10
-rw-r--r--src/core/hle/service/am/am.cpp89
-rw-r--r--src/core/hle/service/am/am.h4
-rw-r--r--src/core/hle/service/am/applets/applets.cpp57
-rw-r--r--src/core/hle/service/am/applets/applets.h30
-rw-r--r--src/core/hle/service/am/applets/controller.cpp5
-rw-r--r--src/core/hle/service/am/applets/controller.h3
-rw-r--r--src/core/hle/service/am/applets/error.cpp5
-rw-r--r--src/core/hle/service/am/applets/error.h3
-rw-r--r--src/core/hle/service/am/applets/general_backend.cpp14
-rw-r--r--src/core/hle/service/am/applets/general_backend.h8
-rw-r--r--src/core/hle/service/am/applets/profile_select.cpp4
-rw-r--r--src/core/hle/service/am/applets/profile_select.h2
-rw-r--r--src/core/hle/service/am/applets/software_keyboard.cpp1153
-rw-r--r--src/core/hle/service/am/applets/software_keyboard.h186
-rw-r--r--src/core/hle/service/am/applets/software_keyboard_types.h295
-rw-r--r--src/core/hle/service/am/applets/web_browser.cpp5
-rw-r--r--src/core/hle/service/am/applets/web_browser.h3
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp10
-rw-r--r--src/video_core/gpu.cpp12
-rw-r--r--src/video_core/gpu.h3
-rw-r--r--src/yuzu/CMakeLists.txt4
-rw-r--r--src/yuzu/applets/error.cpp22
-rw-r--r--src/yuzu/applets/error.h2
-rw-r--r--src/yuzu/applets/software_keyboard.cpp1712
-rw-r--r--src/yuzu/applets/software_keyboard.h283
-rw-r--r--src/yuzu/applets/software_keyboard.ui3503
-rw-r--r--src/yuzu/main.cpp185
-rw-r--r--src/yuzu/main.h35
-rw-r--r--src/yuzu/util/overlay_dialog.cpp249
-rw-r--r--src/yuzu/util/overlay_dialog.h107
-rw-r--r--src/yuzu/util/overlay_dialog.ui404
38 files changed, 8204 insertions, 496 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 286e912e3..532e418b0 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -273,6 +273,7 @@ add_library(core STATIC
273 hle/service/am/applets/profile_select.h 273 hle/service/am/applets/profile_select.h
274 hle/service/am/applets/software_keyboard.cpp 274 hle/service/am/applets/software_keyboard.cpp
275 hle/service/am/applets/software_keyboard.h 275 hle/service/am/applets/software_keyboard.h
276 hle/service/am/applets/software_keyboard_types.h
276 hle/service/am/applets/web_browser.cpp 277 hle/service/am/applets/web_browser.cpp
277 hle/service/am/applets/web_browser.h 278 hle/service/am/applets/web_browser.h
278 hle/service/am/applets/web_types.h 279 hle/service/am/applets/web_types.h
diff --git a/src/core/frontend/applets/software_keyboard.cpp b/src/core/frontend/applets/software_keyboard.cpp
index 856ed33da..12c76c9ee 100644
--- a/src/core/frontend/applets/software_keyboard.cpp
+++ b/src/core/frontend/applets/software_keyboard.cpp
@@ -1,29 +1,149 @@
1// Copyright 2018 yuzu emulator team 1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/logging/backend.h" 5#include <thread>
6
7#include "common/logging/log.h"
6#include "common/string_util.h" 8#include "common/string_util.h"
7#include "core/frontend/applets/software_keyboard.h" 9#include "core/frontend/applets/software_keyboard.h"
8 10
9namespace Core::Frontend { 11namespace Core::Frontend {
12
10SoftwareKeyboardApplet::~SoftwareKeyboardApplet() = default; 13SoftwareKeyboardApplet::~SoftwareKeyboardApplet() = default;
11 14
12void DefaultSoftwareKeyboardApplet::RequestText( 15DefaultSoftwareKeyboardApplet::~DefaultSoftwareKeyboardApplet() = default;
13 std::function<void(std::optional<std::u16string>)> out, 16
14 SoftwareKeyboardParameters parameters) const { 17void DefaultSoftwareKeyboardApplet::InitializeKeyboard(
15 if (parameters.initial_text.empty()) 18 bool is_inline, KeyboardInitializeParameters initialize_parameters,
16 out(u"yuzu"); 19 std::function<void(Service::AM::Applets::SwkbdResult, std::u16string)> submit_normal_callback_,
20 std::function<void(Service::AM::Applets::SwkbdReplyType, std::u16string, s32)>
21 submit_inline_callback_) {
22 if (is_inline) {
23 LOG_WARNING(
24 Service_AM,
25 "(STUBBED) called, backend requested to initialize the inline software keyboard.");
26
27 submit_inline_callback = std::move(submit_inline_callback_);
28 } else {
29 LOG_WARNING(
30 Service_AM,
31 "(STUBBED) called, backend requested to initialize the normal software keyboard.");
32
33 submit_normal_callback = std::move(submit_normal_callback_);
34 }
35
36 parameters = std::move(initialize_parameters);
37
38 LOG_INFO(Service_AM,
39 "\nKeyboardInitializeParameters:"
40 "\nok_text={}"
41 "\nheader_text={}"
42 "\nsub_text={}"
43 "\nguide_text={}"
44 "\ninitial_text={}"
45 "\nmax_text_length={}"
46 "\nmin_text_length={}"
47 "\ninitial_cursor_position={}"
48 "\ntype={}"
49 "\npassword_mode={}"
50 "\ntext_draw_type={}"
51 "\nkey_disable_flags={}"
52 "\nuse_blur_background={}"
53 "\nenable_backspace_button={}"
54 "\nenable_return_button={}"
55 "\ndisable_cancel_button={}",
56 Common::UTF16ToUTF8(parameters.ok_text), Common::UTF16ToUTF8(parameters.header_text),
57 Common::UTF16ToUTF8(parameters.sub_text), Common::UTF16ToUTF8(parameters.guide_text),
58 Common::UTF16ToUTF8(parameters.initial_text), parameters.max_text_length,
59 parameters.min_text_length, parameters.initial_cursor_position, parameters.type,
60 parameters.password_mode, parameters.text_draw_type, parameters.key_disable_flags.raw,
61 parameters.use_blur_background, parameters.enable_backspace_button,
62 parameters.enable_return_button, parameters.disable_cancel_button);
63}
64
65void DefaultSoftwareKeyboardApplet::ShowNormalKeyboard() const {
66 LOG_WARNING(Service_AM,
67 "(STUBBED) called, backend requested to show the normal software keyboard.");
68
69 SubmitNormalText(u"yuzu");
70}
71
72void DefaultSoftwareKeyboardApplet::ShowTextCheckDialog(
73 Service::AM::Applets::SwkbdTextCheckResult text_check_result,
74 std::u16string text_check_message) const {
75 LOG_WARNING(Service_AM, "(STUBBED) called, backend requested to show the text check dialog.");
76}
77
78void DefaultSoftwareKeyboardApplet::ShowInlineKeyboard(
79 InlineAppearParameters appear_parameters) const {
80 LOG_WARNING(Service_AM,
81 "(STUBBED) called, backend requested to show the inline software keyboard.");
82
83 LOG_INFO(Service_AM,
84 "\nInlineAppearParameters:"
85 "\nmax_text_length={}"
86 "\nmin_text_length={}"
87 "\nkey_top_scale_x={}"
88 "\nkey_top_scale_y={}"
89 "\nkey_top_translate_x={}"
90 "\nkey_top_translate_y={}"
91 "\ntype={}"
92 "\nkey_disable_flags={}"
93 "\nkey_top_as_floating={}"
94 "\nenable_backspace_button={}"
95 "\nenable_return_button={}"
96 "\ndisable_cancel_button={}",
97 appear_parameters.max_text_length, appear_parameters.min_text_length,
98 appear_parameters.key_top_scale_x, appear_parameters.key_top_scale_y,
99 appear_parameters.key_top_translate_x, appear_parameters.key_top_translate_y,
100 appear_parameters.type, appear_parameters.key_disable_flags.raw,
101 appear_parameters.key_top_as_floating, appear_parameters.enable_backspace_button,
102 appear_parameters.enable_return_button, appear_parameters.disable_cancel_button);
103
104 std::thread([this] { SubmitInlineText(u"yuzu"); }).detach();
105}
17 106
18 out(parameters.initial_text); 107void DefaultSoftwareKeyboardApplet::HideInlineKeyboard() const {
108 LOG_WARNING(Service_AM,
109 "(STUBBED) called, backend requested to hide the inline software keyboard.");
19} 110}
20 111
21void DefaultSoftwareKeyboardApplet::SendTextCheckDialog( 112void DefaultSoftwareKeyboardApplet::InlineTextChanged(InlineTextParameters text_parameters) const {
22 std::u16string error_message, std::function<void()> finished_check) const {
23 LOG_WARNING(Service_AM, 113 LOG_WARNING(Service_AM,
24 "(STUBBED) called - Default fallback software keyboard does not support text " 114 "(STUBBED) called, backend requested to change the inline keyboard text.");
25 "check! (error_message={})", 115
26 Common::UTF16ToUTF8(error_message)); 116 LOG_INFO(Service_AM,
27 finished_check(); 117 "\nInlineTextParameters:"
118 "\ninput_text={}"
119 "\ncursor_position={}",
120 Common::UTF16ToUTF8(text_parameters.input_text), text_parameters.cursor_position);
121
122 submit_inline_callback(Service::AM::Applets::SwkbdReplyType::ChangedString,
123 text_parameters.input_text, text_parameters.cursor_position);
124}
125
126void DefaultSoftwareKeyboardApplet::ExitKeyboard() const {
127 LOG_WARNING(Service_AM, "(STUBBED) called, backend requested to exit the software keyboard.");
28} 128}
129
130void DefaultSoftwareKeyboardApplet::SubmitNormalText(std::u16string text) const {
131 submit_normal_callback(Service::AM::Applets::SwkbdResult::Ok, text);
132}
133
134void DefaultSoftwareKeyboardApplet::SubmitInlineText(std::u16string_view text) const {
135 std::this_thread::sleep_for(std::chrono::milliseconds(500));
136
137 for (std::size_t index = 0; index < text.size(); ++index) {
138 submit_inline_callback(Service::AM::Applets::SwkbdReplyType::ChangedString,
139 std::u16string(text.data(), text.data() + index + 1),
140 static_cast<s32>(index) + 1);
141
142 std::this_thread::sleep_for(std::chrono::milliseconds(250));
143 }
144
145 submit_inline_callback(Service::AM::Applets::SwkbdReplyType::DecidedEnter, std::u16string(text),
146 static_cast<s32>(text.size()));
147}
148
29} // namespace Core::Frontend 149} // namespace Core::Frontend
diff --git a/src/core/frontend/applets/software_keyboard.h b/src/core/frontend/applets/software_keyboard.h
index f9b202664..506eb35bb 100644
--- a/src/core/frontend/applets/software_keyboard.h
+++ b/src/core/frontend/applets/software_keyboard.h
@@ -1,54 +1,116 @@
1// Copyright 2018 yuzu emulator team 1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
6 6
7#include <functional> 7#include <functional>
8#include <optional> 8#include <thread>
9#include <string> 9
10#include "common/bit_field.h"
11#include "common/common_types.h" 10#include "common/common_types.h"
12 11
12#include "core/hle/service/am/applets/software_keyboard_types.h"
13
13namespace Core::Frontend { 14namespace Core::Frontend {
14struct SoftwareKeyboardParameters { 15
15 std::u16string submit_text; 16struct KeyboardInitializeParameters {
17 std::u16string ok_text;
16 std::u16string header_text; 18 std::u16string header_text;
17 std::u16string sub_text; 19 std::u16string sub_text;
18 std::u16string guide_text; 20 std::u16string guide_text;
19 std::u16string initial_text; 21 std::u16string initial_text;
20 std::size_t max_length; 22 u32 max_text_length;
21 bool password; 23 u32 min_text_length;
22 bool cursor_at_beginning; 24 s32 initial_cursor_position;
23 25 Service::AM::Applets::SwkbdType type;
24 union { 26 Service::AM::Applets::SwkbdPasswordMode password_mode;
25 u8 value; 27 Service::AM::Applets::SwkbdTextDrawType text_draw_type;
26 28 Service::AM::Applets::SwkbdKeyDisableFlags key_disable_flags;
27 BitField<1, 1, u8> disable_space; 29 bool use_blur_background;
28 BitField<2, 1, u8> disable_address; 30 bool enable_backspace_button;
29 BitField<3, 1, u8> disable_percent; 31 bool enable_return_button;
30 BitField<4, 1, u8> disable_slash; 32 bool disable_cancel_button;
31 BitField<6, 1, u8> disable_number; 33};
32 BitField<7, 1, u8> disable_download_code; 34
33 }; 35struct InlineAppearParameters {
36 u32 max_text_length;
37 u32 min_text_length;
38 f32 key_top_scale_x;
39 f32 key_top_scale_y;
40 f32 key_top_translate_x;
41 f32 key_top_translate_y;
42 Service::AM::Applets::SwkbdType type;
43 Service::AM::Applets::SwkbdKeyDisableFlags key_disable_flags;
44 bool key_top_as_floating;
45 bool enable_backspace_button;
46 bool enable_return_button;
47 bool disable_cancel_button;
48};
49
50struct InlineTextParameters {
51 std::u16string input_text;
52 s32 cursor_position;
34}; 53};
35 54
36class SoftwareKeyboardApplet { 55class SoftwareKeyboardApplet {
37public: 56public:
38 virtual ~SoftwareKeyboardApplet(); 57 virtual ~SoftwareKeyboardApplet();
39 58
40 virtual void RequestText(std::function<void(std::optional<std::u16string>)> out, 59 virtual void InitializeKeyboard(
41 SoftwareKeyboardParameters parameters) const = 0; 60 bool is_inline, KeyboardInitializeParameters initialize_parameters,
42 virtual void SendTextCheckDialog(std::u16string error_message, 61 std::function<void(Service::AM::Applets::SwkbdResult, std::u16string)>
43 std::function<void()> finished_check) const = 0; 62 submit_normal_callback_,
63 std::function<void(Service::AM::Applets::SwkbdReplyType, std::u16string, s32)>
64 submit_inline_callback_) = 0;
65
66 virtual void ShowNormalKeyboard() const = 0;
67
68 virtual void ShowTextCheckDialog(Service::AM::Applets::SwkbdTextCheckResult text_check_result,
69 std::u16string text_check_message) const = 0;
70
71 virtual void ShowInlineKeyboard(InlineAppearParameters appear_parameters) const = 0;
72
73 virtual void HideInlineKeyboard() const = 0;
74
75 virtual void InlineTextChanged(InlineTextParameters text_parameters) const = 0;
76
77 virtual void ExitKeyboard() const = 0;
44}; 78};
45 79
46class DefaultSoftwareKeyboardApplet final : public SoftwareKeyboardApplet { 80class DefaultSoftwareKeyboardApplet final : public SoftwareKeyboardApplet {
47public: 81public:
48 void RequestText(std::function<void(std::optional<std::u16string>)> out, 82 ~DefaultSoftwareKeyboardApplet() override;
49 SoftwareKeyboardParameters parameters) const override; 83
50 void SendTextCheckDialog(std::u16string error_message, 84 void InitializeKeyboard(
51 std::function<void()> finished_check) const override; 85 bool is_inline, KeyboardInitializeParameters initialize_parameters,
86 std::function<void(Service::AM::Applets::SwkbdResult, std::u16string)>
87 submit_normal_callback_,
88 std::function<void(Service::AM::Applets::SwkbdReplyType, std::u16string, s32)>
89 submit_inline_callback_) override;
90
91 void ShowNormalKeyboard() const override;
92
93 void ShowTextCheckDialog(Service::AM::Applets::SwkbdTextCheckResult text_check_result,
94 std::u16string text_check_message) const override;
95
96 void ShowInlineKeyboard(InlineAppearParameters appear_parameters) const override;
97
98 void HideInlineKeyboard() const override;
99
100 void InlineTextChanged(InlineTextParameters text_parameters) const override;
101
102 void ExitKeyboard() const override;
103
104private:
105 void SubmitNormalText(std::u16string text) const;
106 void SubmitInlineText(std::u16string_view text) const;
107
108 KeyboardInitializeParameters parameters;
109
110 mutable std::function<void(Service::AM::Applets::SwkbdResult, std::u16string)>
111 submit_normal_callback;
112 mutable std::function<void(Service::AM::Applets::SwkbdReplyType, std::u16string, s32)>
113 submit_inline_callback;
52}; 114};
53 115
54} // namespace Core::Frontend 116} // namespace Core::Frontend
diff --git a/src/core/frontend/input_interpreter.cpp b/src/core/frontend/input_interpreter.cpp
index ec5fe660e..9f6a90e8f 100644
--- a/src/core/frontend/input_interpreter.cpp
+++ b/src/core/frontend/input_interpreter.cpp
@@ -12,7 +12,9 @@ InputInterpreter::InputInterpreter(Core::System& system)
12 : npad{system.ServiceManager() 12 : npad{system.ServiceManager()
13 .GetService<Service::HID::Hid>("hid") 13 .GetService<Service::HID::Hid>("hid")
14 ->GetAppletResource() 14 ->GetAppletResource()
15 ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad)} {} 15 ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad)} {
16 ResetButtonStates();
17}
16 18
17InputInterpreter::~InputInterpreter() = default; 19InputInterpreter::~InputInterpreter() = default;
18 20
@@ -25,6 +27,17 @@ void InputInterpreter::PollInput() {
25 button_states[current_index] = button_state; 27 button_states[current_index] = button_state;
26} 28}
27 29
30void InputInterpreter::ResetButtonStates() {
31 previous_index = 0;
32 current_index = 0;
33
34 button_states[0] = 0xFFFFFFFF;
35
36 for (std::size_t i = 1; i < button_states.size(); ++i) {
37 button_states[i] = 0;
38 }
39}
40
28bool InputInterpreter::IsButtonPressed(HIDButton button) const { 41bool InputInterpreter::IsButtonPressed(HIDButton button) const {
29 return (button_states[current_index] & (1U << static_cast<u8>(button))) != 0; 42 return (button_states[current_index] & (1U << static_cast<u8>(button))) != 0;
30} 43}
diff --git a/src/core/frontend/input_interpreter.h b/src/core/frontend/input_interpreter.h
index 73fc47ffb..9495e3daf 100644
--- a/src/core/frontend/input_interpreter.h
+++ b/src/core/frontend/input_interpreter.h
@@ -66,6 +66,9 @@ public:
66 /// Gets a button state from HID and inserts it into the array of button states. 66 /// Gets a button state from HID and inserts it into the array of button states.
67 void PollInput(); 67 void PollInput();
68 68
69 /// Resets all the button states to their defaults.
70 void ResetButtonStates();
71
69 /** 72 /**
70 * Checks whether the button is pressed. 73 * Checks whether the button is pressed.
71 * 74 *
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index 161d9f782..2b363b1d9 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -75,10 +75,14 @@ void HLERequestContext::ParseCommandBuffer(const HandleTable& handle_table, u32_
75 if (incoming) { 75 if (incoming) {
76 // Populate the object lists with the data in the IPC request. 76 // Populate the object lists with the data in the IPC request.
77 for (u32 handle = 0; handle < handle_descriptor_header->num_handles_to_copy; ++handle) { 77 for (u32 handle = 0; handle < handle_descriptor_header->num_handles_to_copy; ++handle) {
78 copy_objects.push_back(handle_table.GetGeneric(rp.Pop<Handle>())); 78 const u32 copy_handle{rp.Pop<Handle>()};
79 copy_handles.push_back(copy_handle);
80 copy_objects.push_back(handle_table.GetGeneric(copy_handle));
79 } 81 }
80 for (u32 handle = 0; handle < handle_descriptor_header->num_handles_to_move; ++handle) { 82 for (u32 handle = 0; handle < handle_descriptor_header->num_handles_to_move; ++handle) {
81 move_objects.push_back(handle_table.GetGeneric(rp.Pop<Handle>())); 83 const u32 move_handle{rp.Pop<Handle>()};
84 move_handles.push_back(move_handle);
85 move_objects.push_back(handle_table.GetGeneric(move_handle));
82 } 86 }
83 } else { 87 } else {
84 // For responses we just ignore the handles, they're empty and will be populated when 88 // For responses we just ignore the handles, they're empty and will be populated when
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h
index 9a769781b..6fba42615 100644
--- a/src/core/hle/kernel/hle_ipc.h
+++ b/src/core/hle/kernel/hle_ipc.h
@@ -210,6 +210,14 @@ public:
210 /// Helper function to test whether the output buffer at buffer_index can be written 210 /// Helper function to test whether the output buffer at buffer_index can be written
211 bool CanWriteBuffer(std::size_t buffer_index = 0) const; 211 bool CanWriteBuffer(std::size_t buffer_index = 0) const;
212 212
213 Handle GetCopyHandle(std::size_t index) const {
214 return copy_handles.at(index);
215 }
216
217 Handle GetMoveHandle(std::size_t index) const {
218 return move_handles.at(index);
219 }
220
213 template <typename T> 221 template <typename T>
214 std::shared_ptr<T> GetCopyObject(std::size_t index) { 222 std::shared_ptr<T> GetCopyObject(std::size_t index) {
215 return DynamicObjectCast<T>(copy_objects.at(index)); 223 return DynamicObjectCast<T>(copy_objects.at(index));
@@ -285,6 +293,8 @@ private:
285 std::shared_ptr<Kernel::ServerSession> server_session; 293 std::shared_ptr<Kernel::ServerSession> server_session;
286 std::shared_ptr<KThread> thread; 294 std::shared_ptr<KThread> thread;
287 // TODO(yuriks): Check common usage of this and optimize size accordingly 295 // TODO(yuriks): Check common usage of this and optimize size accordingly
296 boost::container::small_vector<Handle, 8> move_handles;
297 boost::container::small_vector<Handle, 8> copy_handles;
288 boost::container::small_vector<std::shared_ptr<Object>, 8> move_objects; 298 boost::container::small_vector<std::shared_ptr<Object>, 8> move_objects;
289 boost::container::small_vector<std::shared_ptr<Object>, 8> copy_objects; 299 boost::container::small_vector<std::shared_ptr<Object>, 8> copy_objects;
290 boost::container::small_vector<std::shared_ptr<SessionRequestHandler>, 8> domain_objects; 300 boost::container::small_vector<std::shared_ptr<SessionRequestHandler>, 8> domain_objects;
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 416c5239a..4c8216b47 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -619,16 +619,20 @@ std::size_t AppletMessageQueue::GetMessageCount() const {
619 return messages.size(); 619 return messages.size();
620} 620}
621 621
622void AppletMessageQueue::RequestExit() {
623 PushMessage(AppletMessage::ExitRequested);
624}
625
626void AppletMessageQueue::FocusStateChanged() {
627 PushMessage(AppletMessage::FocusStateChanged);
628}
629
622void AppletMessageQueue::OperationModeChanged() { 630void AppletMessageQueue::OperationModeChanged() {
623 PushMessage(AppletMessage::OperationModeChanged); 631 PushMessage(AppletMessage::OperationModeChanged);
624 PushMessage(AppletMessage::PerformanceModeChanged); 632 PushMessage(AppletMessage::PerformanceModeChanged);
625 on_operation_mode_changed->GetWritableEvent()->Signal(); 633 on_operation_mode_changed->GetWritableEvent()->Signal();
626} 634}
627 635
628void AppletMessageQueue::RequestExit() {
629 PushMessage(AppletMessage::ExitRequested);
630}
631
632ICommonStateGetter::ICommonStateGetter(Core::System& system_, 636ICommonStateGetter::ICommonStateGetter(Core::System& system_,
633 std::shared_ptr<AppletMessageQueue> msg_queue_) 637 std::shared_ptr<AppletMessageQueue> msg_queue_)
634 : ServiceFramework{system_, "ICommonStateGetter"}, msg_queue{std::move(msg_queue_)} { 638 : ServiceFramework{system_, "ICommonStateGetter"}, msg_queue{std::move(msg_queue_)} {
@@ -971,7 +975,7 @@ private:
971 975
972 auto storage = applet->GetBroker().PopNormalDataToGame(); 976 auto storage = applet->GetBroker().PopNormalDataToGame();
973 if (storage == nullptr) { 977 if (storage == nullptr) {
974 LOG_ERROR(Service_AM, 978 LOG_DEBUG(Service_AM,
975 "storage is a nullptr. There is no data in the current normal channel"); 979 "storage is a nullptr. There is no data in the current normal channel");
976 IPC::ResponseBuilder rb{ctx, 2}; 980 IPC::ResponseBuilder rb{ctx, 2};
977 rb.Push(ERR_NO_DATA_IN_CHANNEL); 981 rb.Push(ERR_NO_DATA_IN_CHANNEL);
@@ -1002,7 +1006,7 @@ private:
1002 1006
1003 auto storage = applet->GetBroker().PopInteractiveDataToGame(); 1007 auto storage = applet->GetBroker().PopInteractiveDataToGame();
1004 if (storage == nullptr) { 1008 if (storage == nullptr) {
1005 LOG_ERROR(Service_AM, 1009 LOG_DEBUG(Service_AM,
1006 "storage is a nullptr. There is no data in the current interactive channel"); 1010 "storage is a nullptr. There is no data in the current interactive channel");
1007 IPC::ResponseBuilder rb{ctx, 2}; 1011 IPC::ResponseBuilder rb{ctx, 2};
1008 rb.Push(ERR_NO_DATA_IN_CHANNEL); 1012 rb.Push(ERR_NO_DATA_IN_CHANNEL);
@@ -1125,7 +1129,7 @@ ILibraryAppletCreator::ILibraryAppletCreator(Core::System& system_)
1125 {2, nullptr, "AreAnyLibraryAppletsLeft"}, 1129 {2, nullptr, "AreAnyLibraryAppletsLeft"},
1126 {10, &ILibraryAppletCreator::CreateStorage, "CreateStorage"}, 1130 {10, &ILibraryAppletCreator::CreateStorage, "CreateStorage"},
1127 {11, &ILibraryAppletCreator::CreateTransferMemoryStorage, "CreateTransferMemoryStorage"}, 1131 {11, &ILibraryAppletCreator::CreateTransferMemoryStorage, "CreateTransferMemoryStorage"},
1128 {12, nullptr, "CreateHandleStorage"}, 1132 {12, &ILibraryAppletCreator::CreateHandleStorage, "CreateHandleStorage"},
1129 }; 1133 };
1130 RegisterHandlers(functions); 1134 RegisterHandlers(functions);
1131} 1135}
@@ -1134,14 +1138,15 @@ ILibraryAppletCreator::~ILibraryAppletCreator() = default;
1134 1138
1135void ILibraryAppletCreator::CreateLibraryApplet(Kernel::HLERequestContext& ctx) { 1139void ILibraryAppletCreator::CreateLibraryApplet(Kernel::HLERequestContext& ctx) {
1136 IPC::RequestParser rp{ctx}; 1140 IPC::RequestParser rp{ctx};
1141
1137 const auto applet_id = rp.PopRaw<Applets::AppletId>(); 1142 const auto applet_id = rp.PopRaw<Applets::AppletId>();
1138 const auto applet_mode = rp.PopRaw<u32>(); 1143 const auto applet_mode = rp.PopRaw<Applets::LibraryAppletMode>();
1139 1144
1140 LOG_DEBUG(Service_AM, "called with applet_id={:08X}, applet_mode={:08X}", applet_id, 1145 LOG_DEBUG(Service_AM, "called with applet_id={:08X}, applet_mode={:08X}", applet_id,
1141 applet_mode); 1146 applet_mode);
1142 1147
1143 const auto& applet_manager{system.GetAppletManager()}; 1148 const auto& applet_manager{system.GetAppletManager()};
1144 const auto applet = applet_manager.GetApplet(applet_id); 1149 const auto applet = applet_manager.GetApplet(applet_id, applet_mode);
1145 1150
1146 if (applet == nullptr) { 1151 if (applet == nullptr) {
1147 LOG_ERROR(Service_AM, "Applet doesn't exist! applet_id={}", applet_id); 1152 LOG_ERROR(Service_AM, "Applet doesn't exist! applet_id={}", applet_id);
@@ -1159,9 +1164,18 @@ void ILibraryAppletCreator::CreateLibraryApplet(Kernel::HLERequestContext& ctx)
1159 1164
1160void ILibraryAppletCreator::CreateStorage(Kernel::HLERequestContext& ctx) { 1165void ILibraryAppletCreator::CreateStorage(Kernel::HLERequestContext& ctx) {
1161 IPC::RequestParser rp{ctx}; 1166 IPC::RequestParser rp{ctx};
1162 const u64 size{rp.Pop<u64>()}; 1167
1168 const s64 size{rp.Pop<s64>()};
1169
1163 LOG_DEBUG(Service_AM, "called, size={}", size); 1170 LOG_DEBUG(Service_AM, "called, size={}", size);
1164 1171
1172 if (size <= 0) {
1173 LOG_ERROR(Service_AM, "size is less than or equal to 0");
1174 IPC::ResponseBuilder rb{ctx, 2};
1175 rb.Push(RESULT_UNKNOWN);
1176 return;
1177 }
1178
1165 std::vector<u8> buffer(size); 1179 std::vector<u8> buffer(size);
1166 1180
1167 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 1181 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
@@ -1170,18 +1184,65 @@ void ILibraryAppletCreator::CreateStorage(Kernel::HLERequestContext& ctx) {
1170} 1184}
1171 1185
1172void ILibraryAppletCreator::CreateTransferMemoryStorage(Kernel::HLERequestContext& ctx) { 1186void ILibraryAppletCreator::CreateTransferMemoryStorage(Kernel::HLERequestContext& ctx) {
1173 LOG_DEBUG(Service_AM, "called"); 1187 IPC::RequestParser rp{ctx};
1188
1189 struct Parameters {
1190 u8 permissions;
1191 s64 size;
1192 };
1193
1194 const auto parameters{rp.PopRaw<Parameters>()};
1195 const auto handle{ctx.GetCopyHandle(0)};
1196
1197 LOG_DEBUG(Service_AM, "called, permissions={}, size={}, handle={:08X}", parameters.permissions,
1198 parameters.size, handle);
1199
1200 if (parameters.size <= 0) {
1201 LOG_ERROR(Service_AM, "size is less than or equal to 0");
1202 IPC::ResponseBuilder rb{ctx, 2};
1203 rb.Push(RESULT_UNKNOWN);
1204 return;
1205 }
1206
1207 auto transfer_mem =
1208 system.CurrentProcess()->GetHandleTable().Get<Kernel::TransferMemory>(handle);
1209
1210 if (transfer_mem == nullptr) {
1211 LOG_ERROR(Service_AM, "transfer_mem is a nullptr for handle={:08X}", handle);
1212 IPC::ResponseBuilder rb{ctx, 2};
1213 rb.Push(RESULT_UNKNOWN);
1214 return;
1215 }
1174 1216
1217 const u8* const mem_begin = transfer_mem->GetPointer();
1218 const u8* const mem_end = mem_begin + transfer_mem->GetSize();
1219 std::vector<u8> memory{mem_begin, mem_end};
1220
1221 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
1222 rb.Push(RESULT_SUCCESS);
1223 rb.PushIpcInterface<IStorage>(system, std::move(memory));
1224}
1225
1226void ILibraryAppletCreator::CreateHandleStorage(Kernel::HLERequestContext& ctx) {
1175 IPC::RequestParser rp{ctx}; 1227 IPC::RequestParser rp{ctx};
1176 1228
1177 rp.SetCurrentOffset(3); 1229 const s64 size{rp.Pop<s64>()};
1178 const auto handle{rp.Pop<Kernel::Handle>()}; 1230 const auto handle{ctx.GetCopyHandle(0)};
1231
1232 LOG_DEBUG(Service_AM, "called, size={}, handle={:08X}", size, handle);
1233
1234 if (size <= 0) {
1235 LOG_ERROR(Service_AM, "size is less than or equal to 0");
1236 IPC::ResponseBuilder rb{ctx, 2};
1237 rb.Push(RESULT_UNKNOWN);
1238 return;
1239 }
1179 1240
1180 auto transfer_mem = 1241 auto transfer_mem =
1181 system.CurrentProcess()->GetHandleTable().Get<Kernel::TransferMemory>(handle); 1242 system.CurrentProcess()->GetHandleTable().Get<Kernel::TransferMemory>(handle);
1182 1243
1183 if (transfer_mem == nullptr) { 1244 if (transfer_mem == nullptr) {
1184 LOG_ERROR(Service_AM, "shared_mem is a nullpr for handle={:08X}", handle); 1245 LOG_ERROR(Service_AM, "transfer_mem is a nullptr for handle={:08X}", handle);
1185 IPC::ResponseBuilder rb{ctx, 2}; 1246 IPC::ResponseBuilder rb{ctx, 2};
1186 rb.Push(RESULT_UNKNOWN); 1247 rb.Push(RESULT_UNKNOWN);
1187 return; 1248 return;
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index f6a453ab7..756434716 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -61,8 +61,9 @@ public:
61 void PushMessage(AppletMessage msg); 61 void PushMessage(AppletMessage msg);
62 AppletMessage PopMessage(); 62 AppletMessage PopMessage();
63 std::size_t GetMessageCount() const; 63 std::size_t GetMessageCount() const;
64 void OperationModeChanged();
65 void RequestExit(); 64 void RequestExit();
65 void FocusStateChanged();
66 void OperationModeChanged();
66 67
67private: 68private:
68 std::queue<AppletMessage> messages; 69 std::queue<AppletMessage> messages;
@@ -254,6 +255,7 @@ private:
254 void CreateLibraryApplet(Kernel::HLERequestContext& ctx); 255 void CreateLibraryApplet(Kernel::HLERequestContext& ctx);
255 void CreateStorage(Kernel::HLERequestContext& ctx); 256 void CreateStorage(Kernel::HLERequestContext& ctx);
256 void CreateTransferMemoryStorage(Kernel::HLERequestContext& ctx); 257 void CreateTransferMemoryStorage(Kernel::HLERequestContext& ctx);
258 void CreateHandleStorage(Kernel::HLERequestContext& ctx);
257}; 259};
258 260
259class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> { 261class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> {
diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp
index e2f3b7563..a56df6a7e 100644
--- a/src/core/hle/service/am/applets/applets.cpp
+++ b/src/core/hle/service/am/applets/applets.cpp
@@ -17,6 +17,8 @@
17#include "core/hle/kernel/k_writable_event.h" 17#include "core/hle/kernel/k_writable_event.h"
18#include "core/hle/kernel/server_session.h" 18#include "core/hle/kernel/server_session.h"
19#include "core/hle/service/am/am.h" 19#include "core/hle/service/am/am.h"
20#include "core/hle/service/am/applet_ae.h"
21#include "core/hle/service/am/applet_oe.h"
20#include "core/hle/service/am/applets/applets.h" 22#include "core/hle/service/am/applets/applets.h"
21#include "core/hle/service/am/applets/controller.h" 23#include "core/hle/service/am/applets/controller.h"
22#include "core/hle/service/am/applets/error.h" 24#include "core/hle/service/am/applets/error.h"
@@ -24,17 +26,20 @@
24#include "core/hle/service/am/applets/profile_select.h" 26#include "core/hle/service/am/applets/profile_select.h"
25#include "core/hle/service/am/applets/software_keyboard.h" 27#include "core/hle/service/am/applets/software_keyboard.h"
26#include "core/hle/service/am/applets/web_browser.h" 28#include "core/hle/service/am/applets/web_browser.h"
29#include "core/hle/service/sm/sm.h"
27 30
28namespace Service::AM::Applets { 31namespace Service::AM::Applets {
29 32
30AppletDataBroker::AppletDataBroker(Kernel::KernelCore& kernel) { 33AppletDataBroker::AppletDataBroker(Core::System& system_, LibraryAppletMode applet_mode_)
34 : system{system_}, applet_mode{applet_mode_} {
31 state_changed_event = 35 state_changed_event =
32 Kernel::KEvent::Create(kernel, "ILibraryAppletAccessor:StateChangedEvent"); 36 Kernel::KEvent::Create(system.Kernel(), "ILibraryAppletAccessor:StateChangedEvent");
33 state_changed_event->Initialize(); 37 state_changed_event->Initialize();
34 pop_out_data_event = Kernel::KEvent::Create(kernel, "ILibraryAppletAccessor:PopDataOutEvent"); 38 pop_out_data_event =
39 Kernel::KEvent::Create(system.Kernel(), "ILibraryAppletAccessor:PopDataOutEvent");
35 pop_out_data_event->Initialize(); 40 pop_out_data_event->Initialize();
36 pop_interactive_out_data_event = 41 pop_interactive_out_data_event = Kernel::KEvent::Create(
37 Kernel::KEvent::Create(kernel, "ILibraryAppletAccessor:PopInteractiveDataOutEvent"); 42 system.Kernel(), "ILibraryAppletAccessor:PopInteractiveDataOutEvent");
38 pop_interactive_out_data_event->Initialize(); 43 pop_interactive_out_data_event->Initialize();
39} 44}
40 45
@@ -114,6 +119,27 @@ void AppletDataBroker::PushInteractiveDataFromApplet(std::shared_ptr<IStorage>&&
114 119
115void AppletDataBroker::SignalStateChanged() const { 120void AppletDataBroker::SignalStateChanged() const {
116 state_changed_event->GetWritableEvent()->Signal(); 121 state_changed_event->GetWritableEvent()->Signal();
122
123 switch (applet_mode) {
124 case LibraryAppletMode::AllForeground:
125 case LibraryAppletMode::AllForegroundInitiallyHidden: {
126 auto applet_oe = system.ServiceManager().GetService<AppletOE>("appletOE");
127 auto applet_ae = system.ServiceManager().GetService<AppletAE>("appletAE");
128
129 if (applet_oe) {
130 applet_oe->GetMessageQueue()->FocusStateChanged();
131 break;
132 }
133
134 if (applet_ae) {
135 applet_ae->GetMessageQueue()->FocusStateChanged();
136 break;
137 }
138 break;
139 }
140 default:
141 break;
142 }
117} 143}
118 144
119std::shared_ptr<Kernel::KReadableEvent> AppletDataBroker::GetNormalDataEvent() const { 145std::shared_ptr<Kernel::KReadableEvent> AppletDataBroker::GetNormalDataEvent() const {
@@ -128,7 +154,8 @@ std::shared_ptr<Kernel::KReadableEvent> AppletDataBroker::GetStateChangedEvent()
128 return state_changed_event->GetReadableEvent(); 154 return state_changed_event->GetReadableEvent();
129} 155}
130 156
131Applet::Applet(Kernel::KernelCore& kernel_) : broker{kernel_} {} 157Applet::Applet(Core::System& system_, LibraryAppletMode applet_mode_)
158 : broker{system_, applet_mode_}, applet_mode{applet_mode_} {}
132 159
133Applet::~Applet() = default; 160Applet::~Applet() = default;
134 161
@@ -241,31 +268,31 @@ void AppletManager::ClearAll() {
241 frontend = {}; 268 frontend = {};
242} 269}
243 270
244std::shared_ptr<Applet> AppletManager::GetApplet(AppletId id) const { 271std::shared_ptr<Applet> AppletManager::GetApplet(AppletId id, LibraryAppletMode mode) const {
245 switch (id) { 272 switch (id) {
246 case AppletId::Auth: 273 case AppletId::Auth:
247 return std::make_shared<Auth>(system, *frontend.parental_controls); 274 return std::make_shared<Auth>(system, mode, *frontend.parental_controls);
248 case AppletId::Controller: 275 case AppletId::Controller:
249 return std::make_shared<Controller>(system, *frontend.controller); 276 return std::make_shared<Controller>(system, mode, *frontend.controller);
250 case AppletId::Error: 277 case AppletId::Error:
251 return std::make_shared<Error>(system, *frontend.error); 278 return std::make_shared<Error>(system, mode, *frontend.error);
252 case AppletId::ProfileSelect: 279 case AppletId::ProfileSelect:
253 return std::make_shared<ProfileSelect>(system, *frontend.profile_select); 280 return std::make_shared<ProfileSelect>(system, mode, *frontend.profile_select);
254 case AppletId::SoftwareKeyboard: 281 case AppletId::SoftwareKeyboard:
255 return std::make_shared<SoftwareKeyboard>(system, *frontend.software_keyboard); 282 return std::make_shared<SoftwareKeyboard>(system, mode, *frontend.software_keyboard);
256 case AppletId::Web: 283 case AppletId::Web:
257 case AppletId::Shop: 284 case AppletId::Shop:
258 case AppletId::OfflineWeb: 285 case AppletId::OfflineWeb:
259 case AppletId::LoginShare: 286 case AppletId::LoginShare:
260 case AppletId::WebAuth: 287 case AppletId::WebAuth:
261 return std::make_shared<WebBrowser>(system, *frontend.web_browser); 288 return std::make_shared<WebBrowser>(system, mode, *frontend.web_browser);
262 case AppletId::PhotoViewer: 289 case AppletId::PhotoViewer:
263 return std::make_shared<PhotoViewer>(system, *frontend.photo_viewer); 290 return std::make_shared<PhotoViewer>(system, mode, *frontend.photo_viewer);
264 default: 291 default:
265 UNIMPLEMENTED_MSG( 292 UNIMPLEMENTED_MSG(
266 "No backend implementation exists for applet_id={:02X}! Falling back to stub applet.", 293 "No backend implementation exists for applet_id={:02X}! Falling back to stub applet.",
267 static_cast<u8>(id)); 294 static_cast<u8>(id));
268 return std::make_shared<StubApplet>(system, id); 295 return std::make_shared<StubApplet>(system, id, mode);
269 } 296 }
270} 297}
271 298
diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h
index b9a006317..4215d2232 100644
--- a/src/core/hle/service/am/applets/applets.h
+++ b/src/core/hle/service/am/applets/applets.h
@@ -62,9 +62,17 @@ enum class AppletId : u32 {
62 MyPage = 0x1A, 62 MyPage = 0x1A,
63}; 63};
64 64
65enum class LibraryAppletMode : u32 {
66 AllForeground = 0,
67 Background = 1,
68 NoUI = 2,
69 BackgroundIndirectDisplay = 3,
70 AllForegroundInitiallyHidden = 4,
71};
72
65class AppletDataBroker final { 73class AppletDataBroker final {
66public: 74public:
67 explicit AppletDataBroker(Kernel::KernelCore& kernel_); 75 explicit AppletDataBroker(Core::System& system_, LibraryAppletMode applet_mode_);
68 ~AppletDataBroker(); 76 ~AppletDataBroker();
69 77
70 struct RawChannelData { 78 struct RawChannelData {
@@ -94,6 +102,9 @@ public:
94 std::shared_ptr<Kernel::KReadableEvent> GetStateChangedEvent() const; 102 std::shared_ptr<Kernel::KReadableEvent> GetStateChangedEvent() const;
95 103
96private: 104private:
105 Core::System& system;
106 LibraryAppletMode applet_mode;
107
97 // Queues are named from applet's perspective 108 // Queues are named from applet's perspective
98 109
99 // PopNormalDataToApplet and PushNormalDataFromGame 110 // PopNormalDataToApplet and PushNormalDataFromGame
@@ -119,7 +130,7 @@ private:
119 130
120class Applet { 131class Applet {
121public: 132public:
122 explicit Applet(Kernel::KernelCore& kernel_); 133 explicit Applet(Core::System& system_, LibraryAppletMode applet_mode_);
123 virtual ~Applet(); 134 virtual ~Applet();
124 135
125 virtual void Initialize(); 136 virtual void Initialize();
@@ -129,10 +140,6 @@ public:
129 virtual void ExecuteInteractive() = 0; 140 virtual void ExecuteInteractive() = 0;
130 virtual void Execute() = 0; 141 virtual void Execute() = 0;
131 142
132 bool IsInitialized() const {
133 return initialized;
134 }
135
136 AppletDataBroker& GetBroker() { 143 AppletDataBroker& GetBroker() {
137 return broker; 144 return broker;
138 } 145 }
@@ -141,6 +148,14 @@ public:
141 return broker; 148 return broker;
142 } 149 }
143 150
151 LibraryAppletMode GetLibraryAppletMode() const {
152 return applet_mode;
153 }
154
155 bool IsInitialized() const {
156 return initialized;
157 }
158
144protected: 159protected:
145 struct CommonArguments { 160 struct CommonArguments {
146 u32_le arguments_version; 161 u32_le arguments_version;
@@ -154,6 +169,7 @@ protected:
154 169
155 CommonArguments common_args{}; 170 CommonArguments common_args{};
156 AppletDataBroker broker; 171 AppletDataBroker broker;
172 LibraryAppletMode applet_mode;
157 bool initialized = false; 173 bool initialized = false;
158}; 174};
159 175
@@ -200,7 +216,7 @@ public:
200 void SetDefaultAppletsIfMissing(); 216 void SetDefaultAppletsIfMissing();
201 void ClearAll(); 217 void ClearAll();
202 218
203 std::shared_ptr<Applet> GetApplet(AppletId id) const; 219 std::shared_ptr<Applet> GetApplet(AppletId id, LibraryAppletMode mode) const;
204 220
205private: 221private:
206 AppletFrontendSet frontend; 222 AppletFrontendSet frontend;
diff --git a/src/core/hle/service/am/applets/controller.cpp b/src/core/hle/service/am/applets/controller.cpp
index c2bfe698f..218c8d1e4 100644
--- a/src/core/hle/service/am/applets/controller.cpp
+++ b/src/core/hle/service/am/applets/controller.cpp
@@ -45,8 +45,9 @@ static Core::Frontend::ControllerParameters ConvertToFrontendParameters(
45 }; 45 };
46} 46}
47 47
48Controller::Controller(Core::System& system_, const Core::Frontend::ControllerApplet& frontend_) 48Controller::Controller(Core::System& system_, LibraryAppletMode applet_mode_,
49 : Applet{system_.Kernel()}, frontend{frontend_}, system{system_} {} 49 const Core::Frontend::ControllerApplet& frontend_)
50 : Applet{system_, applet_mode_}, frontend{frontend_}, system{system_} {}
50 51
51Controller::~Controller() = default; 52Controller::~Controller() = default;
52 53
diff --git a/src/core/hle/service/am/applets/controller.h b/src/core/hle/service/am/applets/controller.h
index d4c9da7b1..2d4dae0bd 100644
--- a/src/core/hle/service/am/applets/controller.h
+++ b/src/core/hle/service/am/applets/controller.h
@@ -106,7 +106,8 @@ static_assert(sizeof(ControllerSupportResultInfo) == 0xC,
106 106
107class Controller final : public Applet { 107class Controller final : public Applet {
108public: 108public:
109 explicit Controller(Core::System& system_, const Core::Frontend::ControllerApplet& frontend_); 109 explicit Controller(Core::System& system_, LibraryAppletMode applet_mode_,
110 const Core::Frontend::ControllerApplet& frontend_);
110 ~Controller() override; 111 ~Controller() override;
111 112
112 void Initialize() override; 113 void Initialize() override;
diff --git a/src/core/hle/service/am/applets/error.cpp b/src/core/hle/service/am/applets/error.cpp
index 0c8b632e8..23e30aa45 100644
--- a/src/core/hle/service/am/applets/error.cpp
+++ b/src/core/hle/service/am/applets/error.cpp
@@ -86,8 +86,9 @@ ResultCode Decode64BitError(u64 error) {
86 86
87} // Anonymous namespace 87} // Anonymous namespace
88 88
89Error::Error(Core::System& system_, const Core::Frontend::ErrorApplet& frontend_) 89Error::Error(Core::System& system_, LibraryAppletMode applet_mode_,
90 : Applet{system_.Kernel()}, frontend{frontend_}, system{system_} {} 90 const Core::Frontend::ErrorApplet& frontend_)
91 : Applet{system_, applet_mode_}, frontend{frontend_}, system{system_} {}
91 92
92Error::~Error() = default; 93Error::~Error() = default;
93 94
diff --git a/src/core/hle/service/am/applets/error.h b/src/core/hle/service/am/applets/error.h
index a105cdb0c..e606d12ce 100644
--- a/src/core/hle/service/am/applets/error.h
+++ b/src/core/hle/service/am/applets/error.h
@@ -25,7 +25,8 @@ enum class ErrorAppletMode : u8 {
25 25
26class Error final : public Applet { 26class Error final : public Applet {
27public: 27public:
28 explicit Error(Core::System& system_, const Core::Frontend::ErrorApplet& frontend_); 28 explicit Error(Core::System& system_, LibraryAppletMode applet_mode_,
29 const Core::Frontend::ErrorApplet& frontend_);
29 ~Error() override; 30 ~Error() override;
30 31
31 void Initialize() override; 32 void Initialize() override;
diff --git a/src/core/hle/service/am/applets/general_backend.cpp b/src/core/hle/service/am/applets/general_backend.cpp
index 4d1df5cbe..b26abad36 100644
--- a/src/core/hle/service/am/applets/general_backend.cpp
+++ b/src/core/hle/service/am/applets/general_backend.cpp
@@ -37,8 +37,9 @@ static void LogCurrentStorage(AppletDataBroker& broker, std::string_view prefix)
37 } 37 }
38} 38}
39 39
40Auth::Auth(Core::System& system_, Core::Frontend::ParentalControlsApplet& frontend_) 40Auth::Auth(Core::System& system_, LibraryAppletMode applet_mode_,
41 : Applet{system_.Kernel()}, frontend{frontend_}, system{system_} {} 41 Core::Frontend::ParentalControlsApplet& frontend_)
42 : Applet{system_, applet_mode_}, frontend{frontend_}, system{system_} {}
42 43
43Auth::~Auth() = default; 44Auth::~Auth() = default;
44 45
@@ -152,8 +153,9 @@ void Auth::AuthFinished(bool is_successful) {
152 broker.SignalStateChanged(); 153 broker.SignalStateChanged();
153} 154}
154 155
155PhotoViewer::PhotoViewer(Core::System& system_, const Core::Frontend::PhotoViewerApplet& frontend_) 156PhotoViewer::PhotoViewer(Core::System& system_, LibraryAppletMode applet_mode_,
156 : Applet{system_.Kernel()}, frontend{frontend_}, system{system_} {} 157 const Core::Frontend::PhotoViewerApplet& frontend_)
158 : Applet{system_, applet_mode_}, frontend{frontend_}, system{system_} {}
157 159
158PhotoViewer::~PhotoViewer() = default; 160PhotoViewer::~PhotoViewer() = default;
159 161
@@ -202,8 +204,8 @@ void PhotoViewer::ViewFinished() {
202 broker.SignalStateChanged(); 204 broker.SignalStateChanged();
203} 205}
204 206
205StubApplet::StubApplet(Core::System& system_, AppletId id_) 207StubApplet::StubApplet(Core::System& system_, AppletId id_, LibraryAppletMode applet_mode_)
206 : Applet{system_.Kernel()}, id{id_}, system{system_} {} 208 : Applet{system_, applet_mode_}, id{id_}, system{system_} {}
207 209
208StubApplet::~StubApplet() = default; 210StubApplet::~StubApplet() = default;
209 211
diff --git a/src/core/hle/service/am/applets/general_backend.h b/src/core/hle/service/am/applets/general_backend.h
index ba76ae3d3..7496ded88 100644
--- a/src/core/hle/service/am/applets/general_backend.h
+++ b/src/core/hle/service/am/applets/general_backend.h
@@ -20,7 +20,8 @@ enum class AuthAppletType : u32 {
20 20
21class Auth final : public Applet { 21class Auth final : public Applet {
22public: 22public:
23 explicit Auth(Core::System& system_, Core::Frontend::ParentalControlsApplet& frontend_); 23 explicit Auth(Core::System& system_, LibraryAppletMode applet_mode_,
24 Core::Frontend::ParentalControlsApplet& frontend_);
24 ~Auth() override; 25 ~Auth() override;
25 26
26 void Initialize() override; 27 void Initialize() override;
@@ -50,7 +51,8 @@ enum class PhotoViewerAppletMode : u8 {
50 51
51class PhotoViewer final : public Applet { 52class PhotoViewer final : public Applet {
52public: 53public:
53 explicit PhotoViewer(Core::System& system_, const Core::Frontend::PhotoViewerApplet& frontend_); 54 explicit PhotoViewer(Core::System& system_, LibraryAppletMode applet_mode_,
55 const Core::Frontend::PhotoViewerApplet& frontend_);
54 ~PhotoViewer() override; 56 ~PhotoViewer() override;
55 57
56 void Initialize() override; 58 void Initialize() override;
@@ -70,7 +72,7 @@ private:
70 72
71class StubApplet final : public Applet { 73class StubApplet final : public Applet {
72public: 74public:
73 explicit StubApplet(Core::System& system_, AppletId id_); 75 explicit StubApplet(Core::System& system_, AppletId id_, LibraryAppletMode applet_mode_);
74 ~StubApplet() override; 76 ~StubApplet() override;
75 77
76 void Initialize() override; 78 void Initialize() override;
diff --git a/src/core/hle/service/am/applets/profile_select.cpp b/src/core/hle/service/am/applets/profile_select.cpp
index 77fba16c7..c91a9776a 100644
--- a/src/core/hle/service/am/applets/profile_select.cpp
+++ b/src/core/hle/service/am/applets/profile_select.cpp
@@ -15,9 +15,9 @@ namespace Service::AM::Applets {
15 15
16constexpr ResultCode ERR_USER_CANCELLED_SELECTION{ErrorModule::Account, 1}; 16constexpr ResultCode ERR_USER_CANCELLED_SELECTION{ErrorModule::Account, 1};
17 17
18ProfileSelect::ProfileSelect(Core::System& system_, 18ProfileSelect::ProfileSelect(Core::System& system_, LibraryAppletMode applet_mode_,
19 const Core::Frontend::ProfileSelectApplet& frontend_) 19 const Core::Frontend::ProfileSelectApplet& frontend_)
20 : Applet{system_.Kernel()}, frontend{frontend_}, system{system_} {} 20 : Applet{system_, applet_mode_}, frontend{frontend_}, system{system_} {}
21 21
22ProfileSelect::~ProfileSelect() = default; 22ProfileSelect::~ProfileSelect() = default;
23 23
diff --git a/src/core/hle/service/am/applets/profile_select.h b/src/core/hle/service/am/applets/profile_select.h
index 648d33a24..f0dd6c1f3 100644
--- a/src/core/hle/service/am/applets/profile_select.h
+++ b/src/core/hle/service/am/applets/profile_select.h
@@ -33,7 +33,7 @@ static_assert(sizeof(UserSelectionOutput) == 0x18, "UserSelectionOutput has inco
33 33
34class ProfileSelect final : public Applet { 34class ProfileSelect final : public Applet {
35public: 35public:
36 explicit ProfileSelect(Core::System& system_, 36 explicit ProfileSelect(Core::System& system_, LibraryAppletMode applet_mode_,
37 const Core::Frontend::ProfileSelectApplet& frontend_); 37 const Core::Frontend::ProfileSelectApplet& frontend_);
38 ~ProfileSelect() override; 38 ~ProfileSelect() override;
39 39
diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp
index 79b209c6b..73a06def1 100644
--- a/src/core/hle/service/am/applets/software_keyboard.cpp
+++ b/src/core/hle/service/am/applets/software_keyboard.cpp
@@ -1,93 +1,80 @@
1// Copyright 2018 yuzu emulator team 1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <cstring>
6#include "common/assert.h"
7#include "common/string_util.h" 5#include "common/string_util.h"
8#include "core/core.h" 6#include "core/core.h"
9#include "core/frontend/applets/software_keyboard.h" 7#include "core/frontend/applets/software_keyboard.h"
10#include "core/hle/result.h"
11#include "core/hle/service/am/am.h" 8#include "core/hle/service/am/am.h"
12#include "core/hle/service/am/applets/software_keyboard.h" 9#include "core/hle/service/am/applets/software_keyboard.h"
13 10
14namespace Service::AM::Applets { 11namespace Service::AM::Applets {
15 12
16namespace { 13namespace {
17enum class Request : u32 { 14
18 Finalize = 0x4, 15// The maximum number of UTF-16 characters that can be input into the swkbd text field.
19 SetUserWordInfo = 0x6, 16constexpr u32 DEFAULT_MAX_TEXT_LENGTH = 500;
20 SetCustomizeDic = 0x7, 17
21 Calc = 0xa, 18constexpr std::size_t REPLY_BASE_SIZE = sizeof(SwkbdState) + sizeof(SwkbdReplyType);
22 SetCustomizedDictionaries = 0xb, 19constexpr std::size_t REPLY_UTF8_SIZE = 0x7D4;
23 UnsetCustomizedDictionaries = 0xc, 20constexpr std::size_t REPLY_UTF16_SIZE = 0x3EC;
24 UnknownD = 0xd, 21
25 UnknownE = 0xe, 22constexpr const char* GetTextCheckResultName(SwkbdTextCheckResult text_check_result) {
26}; 23 switch (text_check_result) {
27constexpr std::size_t SWKBD_INLINE_INIT_SIZE = 0x8; 24 case SwkbdTextCheckResult::Success:
28constexpr std::size_t SWKBD_OUTPUT_BUFFER_SIZE = 0x7D8; 25 return "Success";
29constexpr std::size_t SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE = 0x7D4; 26 case SwkbdTextCheckResult::Failure:
30constexpr std::size_t DEFAULT_MAX_LENGTH = 500; 27 return "Failure";
31constexpr bool INTERACTIVE_STATUS_OK = false; 28 case SwkbdTextCheckResult::Confirm:
29 return "Confirm";
30 case SwkbdTextCheckResult::Silent:
31 return "Silent";
32 default:
33 UNIMPLEMENTED_MSG("Unknown TextCheckResult={}", text_check_result);
34 return "Unknown";
35 }
36}
37
38void SetReplyBase(std::vector<u8>& reply, SwkbdState state, SwkbdReplyType reply_type) {
39 std::memcpy(reply.data(), &state, sizeof(SwkbdState));
40 std::memcpy(reply.data() + sizeof(SwkbdState), &reply_type, sizeof(SwkbdReplyType));
41}
42
32} // Anonymous namespace 43} // Anonymous namespace
33static Core::Frontend::SoftwareKeyboardParameters ConvertToFrontendParameters( 44
34 KeyboardConfig config, std::u16string initial_text) { 45SoftwareKeyboard::SoftwareKeyboard(Core::System& system_, LibraryAppletMode applet_mode_,
35 Core::Frontend::SoftwareKeyboardParameters params{}; 46 Core::Frontend::SoftwareKeyboardApplet& frontend_)
36 47 : Applet{system_, applet_mode_}, frontend{frontend_}, system{system_} {}
37 params.submit_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
38 config.submit_text.data(), config.submit_text.size());
39 params.header_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
40 config.header_text.data(), config.header_text.size());
41 params.sub_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(config.sub_text.data(),
42 config.sub_text.size());
43 params.guide_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(config.guide_text.data(),
44 config.guide_text.size());
45 params.initial_text = std::move(initial_text);
46 params.max_length = config.length_limit == 0 ? DEFAULT_MAX_LENGTH : config.length_limit;
47 params.password = static_cast<bool>(config.is_password);
48 params.cursor_at_beginning = static_cast<bool>(config.initial_cursor_position);
49 params.value = static_cast<u8>(config.keyset_disable_bitmask);
50
51 return params;
52}
53
54SoftwareKeyboard::SoftwareKeyboard(Core::System& system_,
55 const Core::Frontend::SoftwareKeyboardApplet& frontend_)
56 : Applet{system_.Kernel()}, frontend{frontend_}, system{system_} {}
57 48
58SoftwareKeyboard::~SoftwareKeyboard() = default; 49SoftwareKeyboard::~SoftwareKeyboard() = default;
59 50
60void SoftwareKeyboard::Initialize() { 51void SoftwareKeyboard::Initialize() {
61 complete = false;
62 is_inline = false;
63 initial_text.clear();
64 final_data.clear();
65
66 Applet::Initialize(); 52 Applet::Initialize();
67 53
68 const auto keyboard_config_storage = broker.PopNormalDataToApplet(); 54 LOG_INFO(Service_AM, "Initializing Software Keyboard Applet with LibraryAppletMode={}",
69 ASSERT(keyboard_config_storage != nullptr); 55 applet_mode);
70 const auto& keyboard_config = keyboard_config_storage->GetData();
71
72 if (keyboard_config.size() == SWKBD_INLINE_INIT_SIZE) {
73 is_inline = true;
74 return;
75 }
76
77 ASSERT(keyboard_config.size() >= sizeof(KeyboardConfig));
78 std::memcpy(&config, keyboard_config.data(), sizeof(KeyboardConfig));
79 56
80 const auto work_buffer_storage = broker.PopNormalDataToApplet(); 57 LOG_DEBUG(Service_AM,
81 ASSERT_OR_EXECUTE(work_buffer_storage != nullptr, { return; }); 58 "Initializing Applet with common_args: arg_version={}, lib_version={}, "
82 const auto& work_buffer = work_buffer_storage->GetData(); 59 "play_startup_sound={}, size={}, system_tick={}, theme_color={}",
60 common_args.arguments_version, common_args.library_version,
61 common_args.play_startup_sound, common_args.size, common_args.system_tick,
62 common_args.theme_color);
83 63
84 if (config.initial_string_size == 0) 64 swkbd_applet_version = SwkbdAppletVersion{common_args.library_version};
85 return;
86 65
87 std::vector<char16_t> string(config.initial_string_size); 66 switch (applet_mode) {
88 std::memcpy(string.data(), work_buffer.data() + config.initial_string_offset, 67 case LibraryAppletMode::AllForeground:
89 string.size() * 2); 68 InitializeForeground();
90 initial_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(string.data(), string.size()); 69 break;
70 case LibraryAppletMode::Background:
71 case LibraryAppletMode::BackgroundIndirectDisplay:
72 InitializeBackground(applet_mode);
73 break;
74 default:
75 UNREACHABLE_MSG("Invalid LibraryAppletMode={}", applet_mode);
76 break;
77 }
91} 78}
92 79
93bool SoftwareKeyboard::TransactionComplete() const { 80bool SoftwareKeyboard::TransactionComplete() const {
@@ -95,106 +82,996 @@ bool SoftwareKeyboard::TransactionComplete() const {
95} 82}
96 83
97ResultCode SoftwareKeyboard::GetStatus() const { 84ResultCode SoftwareKeyboard::GetStatus() const {
98 return RESULT_SUCCESS; 85 return status;
99} 86}
100 87
101void SoftwareKeyboard::ExecuteInteractive() { 88void SoftwareKeyboard::ExecuteInteractive() {
102 if (complete) 89 if (complete) {
103 return; 90 return;
91 }
104 92
105 const auto storage = broker.PopInteractiveDataToApplet(); 93 if (is_background) {
106 ASSERT(storage != nullptr); 94 ProcessInlineKeyboardRequest();
107 const auto data = storage->GetData();
108 if (!is_inline) {
109 const auto status = static_cast<bool>(data[0]);
110 if (status == INTERACTIVE_STATUS_OK) {
111 complete = true;
112 } else {
113 std::array<char16_t, SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE / 2 - 2> string;
114 std::memcpy(string.data(), data.data() + 4, string.size() * 2);
115 frontend.SendTextCheckDialog(
116 Common::UTF16StringFromFixedZeroTerminatedBuffer(string.data(), string.size()),
117 [this] { broker.SignalStateChanged(); });
118 }
119 } else { 95 } else {
120 Request request{}; 96 ProcessTextCheck();
121 std::memcpy(&request, data.data(), sizeof(Request)); 97 }
98}
99
100void SoftwareKeyboard::Execute() {
101 if (complete) {
102 return;
103 }
104
105 if (is_background) {
106 return;
107 }
108
109 ShowNormalKeyboard();
110}
122 111
123 switch (request) { 112void SoftwareKeyboard::SubmitTextNormal(SwkbdResult result, std::u16string submitted_text) {
124 case Request::Finalize: 113 if (complete) {
125 complete = true; 114 return;
126 broker.SignalStateChanged(); 115 }
116
117 if (swkbd_config_common.use_text_check && result == SwkbdResult::Ok) {
118 SubmitForTextCheck(submitted_text);
119 } else {
120 SubmitNormalOutputAndExit(result, submitted_text);
121 }
122}
123
124void SoftwareKeyboard::SubmitTextInline(SwkbdReplyType reply_type, std::u16string submitted_text,
125 s32 cursor_position) {
126 if (complete) {
127 return;
128 }
129
130 current_text = std::move(submitted_text);
131 current_cursor_position = cursor_position;
132
133 if (inline_use_utf8) {
134 switch (reply_type) {
135 case SwkbdReplyType::ChangedString:
136 reply_type = SwkbdReplyType::ChangedStringUtf8;
127 break; 137 break;
128 case Request::Calc: { 138 case SwkbdReplyType::MovedCursor:
129 broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::vector<u8>{1})); 139 reply_type = SwkbdReplyType::MovedCursorUtf8;
130 broker.SignalStateChanged(); 140 break;
141 case SwkbdReplyType::DecidedEnter:
142 reply_type = SwkbdReplyType::DecidedEnterUtf8;
143 break;
144 default:
131 break; 145 break;
132 } 146 }
147 }
148
149 if (use_changed_string_v2) {
150 switch (reply_type) {
151 case SwkbdReplyType::ChangedString:
152 reply_type = SwkbdReplyType::ChangedStringV2;
153 break;
154 case SwkbdReplyType::ChangedStringUtf8:
155 reply_type = SwkbdReplyType::ChangedStringUtf8V2;
156 break;
133 default: 157 default:
134 UNIMPLEMENTED_MSG("Request {:X} is not implemented", request);
135 break; 158 break;
136 } 159 }
137 } 160 }
161
162 if (use_moved_cursor_v2) {
163 switch (reply_type) {
164 case SwkbdReplyType::MovedCursor:
165 reply_type = SwkbdReplyType::MovedCursorV2;
166 break;
167 case SwkbdReplyType::MovedCursorUtf8:
168 reply_type = SwkbdReplyType::MovedCursorUtf8V2;
169 break;
170 default:
171 break;
172 }
173 }
174
175 SendReply(reply_type);
138} 176}
139 177
140void SoftwareKeyboard::Execute() { 178void SoftwareKeyboard::InitializeForeground() {
141 if (complete) { 179 LOG_INFO(Service_AM, "Initializing Normal Software Keyboard Applet.");
142 broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(final_data))); 180
143 broker.SignalStateChanged(); 181 is_background = false;
182
183 const auto swkbd_config_storage = broker.PopNormalDataToApplet();
184 ASSERT(swkbd_config_storage != nullptr);
185
186 const auto& swkbd_config_data = swkbd_config_storage->GetData();
187 ASSERT(swkbd_config_data.size() >= sizeof(SwkbdConfigCommon));
188
189 std::memcpy(&swkbd_config_common, swkbd_config_data.data(), sizeof(SwkbdConfigCommon));
190
191 switch (swkbd_applet_version) {
192 case SwkbdAppletVersion::Version5:
193 case SwkbdAppletVersion::Version65542:
194 ASSERT(swkbd_config_data.size() == sizeof(SwkbdConfigCommon) + sizeof(SwkbdConfigOld));
195 std::memcpy(&swkbd_config_old, swkbd_config_data.data() + sizeof(SwkbdConfigCommon),
196 sizeof(SwkbdConfigOld));
197 break;
198 case SwkbdAppletVersion::Version196615:
199 case SwkbdAppletVersion::Version262152:
200 case SwkbdAppletVersion::Version327689:
201 ASSERT(swkbd_config_data.size() == sizeof(SwkbdConfigCommon) + sizeof(SwkbdConfigOld2));
202 std::memcpy(&swkbd_config_old2, swkbd_config_data.data() + sizeof(SwkbdConfigCommon),
203 sizeof(SwkbdConfigOld2));
204 break;
205 case SwkbdAppletVersion::Version393227:
206 case SwkbdAppletVersion::Version524301:
207 ASSERT(swkbd_config_data.size() == sizeof(SwkbdConfigCommon) + sizeof(SwkbdConfigNew));
208 std::memcpy(&swkbd_config_new, swkbd_config_data.data() + sizeof(SwkbdConfigCommon),
209 sizeof(SwkbdConfigNew));
210 break;
211 default:
212 UNIMPLEMENTED_MSG("Unknown SwkbdConfig revision={} with size={}", swkbd_applet_version,
213 swkbd_config_data.size());
214 ASSERT(swkbd_config_data.size() >= sizeof(SwkbdConfigCommon) + sizeof(SwkbdConfigNew));
215 std::memcpy(&swkbd_config_new, swkbd_config_data.data() + sizeof(SwkbdConfigCommon),
216 sizeof(SwkbdConfigNew));
217 break;
218 }
219
220 const auto work_buffer_storage = broker.PopNormalDataToApplet();
221 ASSERT(work_buffer_storage != nullptr);
222
223 if (swkbd_config_common.initial_string_length == 0) {
224 InitializeFrontendKeyboard();
144 return; 225 return;
145 } 226 }
146 227
147 const auto parameters = ConvertToFrontendParameters(config, initial_text); 228 const auto& work_buffer = work_buffer_storage->GetData();
148 if (!is_inline) { 229
149 frontend.RequestText( 230 std::vector<char16_t> initial_string(swkbd_config_common.initial_string_length);
150 [this](std::optional<std::u16string> text) { WriteText(std::move(text)); }, parameters); 231
232 std::memcpy(initial_string.data(),
233 work_buffer.data() + swkbd_config_common.initial_string_offset,
234 swkbd_config_common.initial_string_length * sizeof(char16_t));
235
236 initial_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(initial_string.data(),
237 initial_string.size());
238
239 LOG_DEBUG(Service_AM, "\nInitial Text: {}", Common::UTF16ToUTF8(initial_text));
240
241 InitializeFrontendKeyboard();
242}
243
244void SoftwareKeyboard::InitializeBackground(LibraryAppletMode applet_mode) {
245 LOG_INFO(Service_AM, "Initializing Inline Software Keyboard Applet.");
246
247 is_background = true;
248
249 const auto swkbd_inline_initialize_arg_storage = broker.PopNormalDataToApplet();
250 ASSERT(swkbd_inline_initialize_arg_storage != nullptr);
251
252 const auto& swkbd_inline_initialize_arg = swkbd_inline_initialize_arg_storage->GetData();
253 ASSERT(swkbd_inline_initialize_arg.size() == sizeof(SwkbdInitializeArg));
254
255 std::memcpy(&swkbd_initialize_arg, swkbd_inline_initialize_arg.data(),
256 swkbd_inline_initialize_arg.size());
257
258 if (swkbd_initialize_arg.library_applet_mode_flag) {
259 ASSERT(applet_mode == LibraryAppletMode::Background);
260 } else {
261 ASSERT(applet_mode == LibraryAppletMode::BackgroundIndirectDisplay);
262 }
263}
264
265void SoftwareKeyboard::ProcessTextCheck() {
266 const auto text_check_storage = broker.PopInteractiveDataToApplet();
267 ASSERT(text_check_storage != nullptr);
268
269 const auto& text_check_data = text_check_storage->GetData();
270 ASSERT(text_check_data.size() == sizeof(SwkbdTextCheck));
271
272 SwkbdTextCheck swkbd_text_check;
273
274 std::memcpy(&swkbd_text_check, text_check_data.data(), sizeof(SwkbdTextCheck));
275
276 std::u16string text_check_message = Common::UTF16StringFromFixedZeroTerminatedBuffer(
277 swkbd_text_check.text_check_message.data(), swkbd_text_check.text_check_message.size());
278
279 LOG_INFO(Service_AM, "\nTextCheckResult: {}\nTextCheckMessage: {}",
280 GetTextCheckResultName(swkbd_text_check.text_check_result),
281 Common::UTF16ToUTF8(text_check_message));
282
283 switch (swkbd_text_check.text_check_result) {
284 case SwkbdTextCheckResult::Success:
285 SubmitNormalOutputAndExit(SwkbdResult::Ok, current_text);
286 break;
287 case SwkbdTextCheckResult::Failure:
288 ShowTextCheckDialog(SwkbdTextCheckResult::Failure, text_check_message);
289 break;
290 case SwkbdTextCheckResult::Confirm:
291 ShowTextCheckDialog(SwkbdTextCheckResult::Confirm, text_check_message);
292 break;
293 case SwkbdTextCheckResult::Silent:
294 default:
295 break;
151 } 296 }
152} 297}
153 298
154void SoftwareKeyboard::WriteText(std::optional<std::u16string> text) { 299void SoftwareKeyboard::ProcessInlineKeyboardRequest() {
155 std::vector<u8> output_main(SWKBD_OUTPUT_BUFFER_SIZE); 300 const auto request_data_storage = broker.PopInteractiveDataToApplet();
301 ASSERT(request_data_storage != nullptr);
302
303 const auto& request_data = request_data_storage->GetData();
304 ASSERT(request_data.size() >= sizeof(SwkbdRequestCommand));
156 305
157 if (text.has_value()) { 306 SwkbdRequestCommand request_command;
158 std::vector<u8> output_sub(SWKBD_OUTPUT_BUFFER_SIZE);
159 307
160 if (config.utf_8) { 308 std::memcpy(&request_command, request_data.data(), sizeof(SwkbdRequestCommand));
161 const u64 size = text->size() + sizeof(u64);
162 const auto new_text = Common::UTF16ToUTF8(*text);
163 309
164 std::memcpy(output_sub.data(), &size, sizeof(u64)); 310 switch (request_command) {
165 std::memcpy(output_sub.data() + 8, new_text.data(), 311 case SwkbdRequestCommand::Finalize:
166 std::min(new_text.size(), SWKBD_OUTPUT_BUFFER_SIZE - 8)); 312 RequestFinalize(request_data);
313 break;
314 case SwkbdRequestCommand::SetUserWordInfo:
315 RequestSetUserWordInfo(request_data);
316 break;
317 case SwkbdRequestCommand::SetCustomizeDic:
318 RequestSetCustomizeDic(request_data);
319 break;
320 case SwkbdRequestCommand::Calc:
321 RequestCalc(request_data);
322 break;
323 case SwkbdRequestCommand::SetCustomizedDictionaries:
324 RequestSetCustomizedDictionaries(request_data);
325 break;
326 case SwkbdRequestCommand::UnsetCustomizedDictionaries:
327 RequestUnsetCustomizedDictionaries(request_data);
328 break;
329 case SwkbdRequestCommand::SetChangedStringV2Flag:
330 RequestSetChangedStringV2Flag(request_data);
331 break;
332 case SwkbdRequestCommand::SetMovedCursorV2Flag:
333 RequestSetMovedCursorV2Flag(request_data);
334 break;
335 default:
336 UNIMPLEMENTED_MSG("Unknown SwkbdRequestCommand={}", request_command);
337 break;
338 }
339}
167 340
168 output_main[0] = INTERACTIVE_STATUS_OK; 341void SoftwareKeyboard::SubmitNormalOutputAndExit(SwkbdResult result,
169 std::memcpy(output_main.data() + 4, new_text.data(), 342 std::u16string submitted_text) {
170 std::min(new_text.size(), SWKBD_OUTPUT_BUFFER_SIZE - 4)); 343 std::vector<u8> out_data(sizeof(SwkbdResult) + STRING_BUFFER_SIZE);
171 } else {
172 const u64 size = text->size() * 2 + sizeof(u64);
173 std::memcpy(output_sub.data(), &size, sizeof(u64));
174 std::memcpy(output_sub.data() + 8, text->data(),
175 std::min(text->size() * 2, SWKBD_OUTPUT_BUFFER_SIZE - 8));
176 344
177 output_main[0] = INTERACTIVE_STATUS_OK; 345 if (swkbd_config_common.use_utf8) {
178 std::memcpy(output_main.data() + 4, text->data(), 346 std::string utf8_submitted_text = Common::UTF16ToUTF8(submitted_text);
179 std::min(text->size() * 2, SWKBD_OUTPUT_BUFFER_SIZE - 4));
180 }
181 347
182 complete = !config.text_check; 348 LOG_DEBUG(Service_AM, "\nSwkbdResult: {}\nUTF-8 Submitted Text: {}", result,
183 final_data = output_main; 349 utf8_submitted_text);
184 350
185 if (complete) { 351 std::memcpy(out_data.data(), &result, sizeof(SwkbdResult));
186 broker.PushNormalDataFromApplet( 352 std::memcpy(out_data.data() + sizeof(SwkbdResult), utf8_submitted_text.data(),
187 std::make_shared<IStorage>(system, std::move(output_main))); 353 utf8_submitted_text.size());
188 broker.SignalStateChanged();
189 } else {
190 broker.PushInteractiveDataFromApplet(
191 std::make_shared<IStorage>(system, std::move(output_sub)));
192 }
193 } else { 354 } else {
194 output_main[0] = 1; 355 LOG_DEBUG(Service_AM, "\nSwkbdResult: {}\nUTF-16 Submitted Text: {}", result,
195 complete = true; 356 Common::UTF16ToUTF8(submitted_text));
196 broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(output_main))); 357
197 broker.SignalStateChanged(); 358 std::memcpy(out_data.data(), &result, sizeof(SwkbdResult));
359 std::memcpy(out_data.data() + sizeof(SwkbdResult), submitted_text.data(),
360 submitted_text.size() * sizeof(char16_t));
361 }
362
363 broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(out_data)));
364
365 ExitKeyboard();
366}
367
368void SoftwareKeyboard::SubmitForTextCheck(std::u16string submitted_text) {
369 current_text = std::move(submitted_text);
370
371 std::vector<u8> out_data(sizeof(u64) + STRING_BUFFER_SIZE);
372
373 if (swkbd_config_common.use_utf8) {
374 std::string utf8_submitted_text = Common::UTF16ToUTF8(current_text);
375 const u64 buffer_size = sizeof(u64) + utf8_submitted_text.size();
376
377 LOG_DEBUG(Service_AM, "\nBuffer Size: {}\nUTF-8 Submitted Text: {}", buffer_size,
378 utf8_submitted_text);
379
380 std::memcpy(out_data.data(), &buffer_size, sizeof(u64));
381 std::memcpy(out_data.data() + sizeof(u64), utf8_submitted_text.data(),
382 utf8_submitted_text.size());
383 } else {
384 const u64 buffer_size = sizeof(u64) + current_text.size() * sizeof(char16_t);
385
386 LOG_DEBUG(Service_AM, "\nBuffer Size: {}\nUTF-16 Submitted Text: {}", buffer_size,
387 Common::UTF16ToUTF8(current_text));
388
389 std::memcpy(out_data.data(), &buffer_size, sizeof(u64));
390 std::memcpy(out_data.data() + sizeof(u64), current_text.data(),
391 current_text.size() * sizeof(char16_t));
392 }
393
394 broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(out_data)));
395}
396
397void SoftwareKeyboard::SendReply(SwkbdReplyType reply_type) {
398 switch (reply_type) {
399 case SwkbdReplyType::FinishedInitialize:
400 ReplyFinishedInitialize();
401 break;
402 case SwkbdReplyType::Default:
403 ReplyDefault();
404 break;
405 case SwkbdReplyType::ChangedString:
406 ReplyChangedString();
407 break;
408 case SwkbdReplyType::MovedCursor:
409 ReplyMovedCursor();
410 break;
411 case SwkbdReplyType::MovedTab:
412 ReplyMovedTab();
413 break;
414 case SwkbdReplyType::DecidedEnter:
415 ReplyDecidedEnter();
416 break;
417 case SwkbdReplyType::DecidedCancel:
418 ReplyDecidedCancel();
419 break;
420 case SwkbdReplyType::ChangedStringUtf8:
421 ReplyChangedStringUtf8();
422 break;
423 case SwkbdReplyType::MovedCursorUtf8:
424 ReplyMovedCursorUtf8();
425 break;
426 case SwkbdReplyType::DecidedEnterUtf8:
427 ReplyDecidedEnterUtf8();
428 break;
429 case SwkbdReplyType::UnsetCustomizeDic:
430 ReplyUnsetCustomizeDic();
431 break;
432 case SwkbdReplyType::ReleasedUserWordInfo:
433 ReplyReleasedUserWordInfo();
434 break;
435 case SwkbdReplyType::UnsetCustomizedDictionaries:
436 ReplyUnsetCustomizedDictionaries();
437 break;
438 case SwkbdReplyType::ChangedStringV2:
439 ReplyChangedStringV2();
440 break;
441 case SwkbdReplyType::MovedCursorV2:
442 ReplyMovedCursorV2();
443 break;
444 case SwkbdReplyType::ChangedStringUtf8V2:
445 ReplyChangedStringUtf8V2();
446 break;
447 case SwkbdReplyType::MovedCursorUtf8V2:
448 ReplyMovedCursorUtf8V2();
449 break;
450 default:
451 UNIMPLEMENTED_MSG("Unknown SwkbdReplyType={}", reply_type);
452 ReplyDefault();
453 break;
454 }
455}
456
457void SoftwareKeyboard::ChangeState(SwkbdState state) {
458 swkbd_state = state;
459
460 ReplyDefault();
461}
462
463void SoftwareKeyboard::InitializeFrontendKeyboard() {
464 if (is_background) {
465 const auto& appear_arg = swkbd_calc_arg.appear_arg;
466
467 std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
468 appear_arg.ok_text.data(), appear_arg.ok_text.size());
469
470 const u32 max_text_length =
471 appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
472 ? appear_arg.max_text_length
473 : DEFAULT_MAX_TEXT_LENGTH;
474
475 const u32 min_text_length =
476 appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0;
477
478 const s32 initial_cursor_position =
479 current_cursor_position > 0 ? current_cursor_position : 0;
480
481 const auto text_draw_type =
482 max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box;
483
484 Core::Frontend::KeyboardInitializeParameters initialize_parameters{
485 .ok_text{ok_text},
486 .header_text{},
487 .sub_text{},
488 .guide_text{},
489 .initial_text{current_text},
490 .max_text_length{max_text_length},
491 .min_text_length{min_text_length},
492 .initial_cursor_position{initial_cursor_position},
493 .type{appear_arg.type},
494 .password_mode{SwkbdPasswordMode::Disabled},
495 .text_draw_type{text_draw_type},
496 .key_disable_flags{appear_arg.key_disable_flags},
497 .use_blur_background{false},
498 .enable_backspace_button{swkbd_calc_arg.enable_backspace_button},
499 .enable_return_button{appear_arg.enable_return_button},
500 .disable_cancel_button{appear_arg.disable_cancel_button},
501 };
502
503 frontend.InitializeKeyboard(
504 true, std::move(initialize_parameters), {},
505 [this](SwkbdReplyType reply_type, std::u16string submitted_text, s32 cursor_position) {
506 SubmitTextInline(reply_type, submitted_text, cursor_position);
507 });
508 } else {
509 std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
510 swkbd_config_common.ok_text.data(), swkbd_config_common.ok_text.size());
511
512 std::u16string header_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
513 swkbd_config_common.header_text.data(), swkbd_config_common.header_text.size());
514
515 std::u16string sub_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
516 swkbd_config_common.sub_text.data(), swkbd_config_common.sub_text.size());
517
518 std::u16string guide_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
519 swkbd_config_common.guide_text.data(), swkbd_config_common.guide_text.size());
520
521 const u32 max_text_length =
522 swkbd_config_common.max_text_length > 0 &&
523 swkbd_config_common.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
524 ? swkbd_config_common.max_text_length
525 : DEFAULT_MAX_TEXT_LENGTH;
526
527 const u32 min_text_length = swkbd_config_common.min_text_length <= max_text_length
528 ? swkbd_config_common.min_text_length
529 : 0;
530
531 const s32 initial_cursor_position = [this] {
532 switch (swkbd_config_common.initial_cursor_position) {
533 case SwkbdInitialCursorPosition::Start:
534 default:
535 return 0;
536 case SwkbdInitialCursorPosition::End:
537 return static_cast<s32>(initial_text.size());
538 }
539 }();
540
541 const auto text_draw_type = [this, max_text_length] {
542 switch (swkbd_config_common.text_draw_type) {
543 case SwkbdTextDrawType::Line:
544 default:
545 return max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box;
546 case SwkbdTextDrawType::Box:
547 case SwkbdTextDrawType::DownloadCode:
548 return swkbd_config_common.text_draw_type;
549 }
550 }();
551
552 const auto enable_return_button = text_draw_type == SwkbdTextDrawType::Box
553 ? swkbd_config_common.enable_return_button
554 : false;
555
556 const auto disable_cancel_button = swkbd_applet_version >= SwkbdAppletVersion::Version393227
557 ? swkbd_config_new.disable_cancel_button
558 : false;
559
560 Core::Frontend::KeyboardInitializeParameters initialize_parameters{
561 .ok_text{ok_text},
562 .header_text{header_text},
563 .sub_text{sub_text},
564 .guide_text{guide_text},
565 .initial_text{initial_text},
566 .max_text_length{max_text_length},
567 .min_text_length{min_text_length},
568 .initial_cursor_position{initial_cursor_position},
569 .type{swkbd_config_common.type},
570 .password_mode{swkbd_config_common.password_mode},
571 .text_draw_type{text_draw_type},
572 .key_disable_flags{swkbd_config_common.key_disable_flags},
573 .use_blur_background{swkbd_config_common.use_blur_background},
574 .enable_backspace_button{true},
575 .enable_return_button{enable_return_button},
576 .disable_cancel_button{disable_cancel_button},
577 };
578
579 frontend.InitializeKeyboard(false, std::move(initialize_parameters),
580 [this](SwkbdResult result, std::u16string submitted_text) {
581 SubmitTextNormal(result, submitted_text);
582 },
583 {});
584 }
585}
586
587void SoftwareKeyboard::ShowNormalKeyboard() {
588 frontend.ShowNormalKeyboard();
589}
590
591void SoftwareKeyboard::ShowTextCheckDialog(SwkbdTextCheckResult text_check_result,
592 std::u16string text_check_message) {
593 frontend.ShowTextCheckDialog(text_check_result, text_check_message);
594}
595
596void SoftwareKeyboard::ShowInlineKeyboard() {
597 if (swkbd_state != SwkbdState::InitializedIsHidden) {
598 return;
599 }
600
601 ChangeState(SwkbdState::InitializedIsAppearing);
602
603 const auto& appear_arg = swkbd_calc_arg.appear_arg;
604
605 const u32 max_text_length =
606 appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
607 ? appear_arg.max_text_length
608 : DEFAULT_MAX_TEXT_LENGTH;
609
610 const u32 min_text_length =
611 appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0;
612
613 Core::Frontend::InlineAppearParameters appear_parameters{
614 .max_text_length{max_text_length},
615 .min_text_length{min_text_length},
616 .key_top_scale_x{swkbd_calc_arg.key_top_scale_x},
617 .key_top_scale_y{swkbd_calc_arg.key_top_scale_y},
618 .key_top_translate_x{swkbd_calc_arg.key_top_translate_x},
619 .key_top_translate_y{swkbd_calc_arg.key_top_translate_y},
620 .type{appear_arg.type},
621 .key_disable_flags{appear_arg.key_disable_flags},
622 .key_top_as_floating{swkbd_calc_arg.key_top_as_floating},
623 .enable_backspace_button{swkbd_calc_arg.enable_backspace_button},
624 .enable_return_button{appear_arg.enable_return_button},
625 .disable_cancel_button{appear_arg.disable_cancel_button},
626 };
627
628 frontend.ShowInlineKeyboard(std::move(appear_parameters));
629
630 ChangeState(SwkbdState::InitializedIsShown);
631}
632
633void SoftwareKeyboard::HideInlineKeyboard() {
634 if (swkbd_state != SwkbdState::InitializedIsShown) {
635 return;
636 }
637
638 ChangeState(SwkbdState::InitializedIsDisappearing);
639
640 frontend.HideInlineKeyboard();
641
642 ChangeState(SwkbdState::InitializedIsHidden);
643}
644
645void SoftwareKeyboard::InlineTextChanged() {
646 Core::Frontend::InlineTextParameters text_parameters{
647 .input_text{current_text},
648 .cursor_position{current_cursor_position},
649 };
650
651 frontend.InlineTextChanged(std::move(text_parameters));
652}
653
654void SoftwareKeyboard::ExitKeyboard() {
655 complete = true;
656 status = RESULT_SUCCESS;
657
658 frontend.ExitKeyboard();
659
660 broker.SignalStateChanged();
661}
662
663// Inline Software Keyboard Requests
664
665void SoftwareKeyboard::RequestFinalize(const std::vector<u8>& request_data) {
666 LOG_DEBUG(Service_AM, "Processing Request: Finalize");
667
668 ChangeState(SwkbdState::NotInitialized);
669
670 ExitKeyboard();
671}
672
673void SoftwareKeyboard::RequestSetUserWordInfo(const std::vector<u8>& request_data) {
674 LOG_WARNING(Service_AM, "SetUserWordInfo is not implemented.");
675}
676
677void SoftwareKeyboard::RequestSetCustomizeDic(const std::vector<u8>& request_data) {
678 LOG_WARNING(Service_AM, "SetCustomizeDic is not implemented.");
679}
680
681void SoftwareKeyboard::RequestCalc(const std::vector<u8>& request_data) {
682 LOG_DEBUG(Service_AM, "Processing Request: Calc");
683
684 ASSERT(request_data.size() == sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArg));
685
686 std::memcpy(&swkbd_calc_arg, request_data.data() + sizeof(SwkbdRequestCommand),
687 sizeof(SwkbdCalcArg));
688
689 if (swkbd_calc_arg.flags.set_input_text) {
690 current_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
691 swkbd_calc_arg.input_text.data(), swkbd_calc_arg.input_text.size());
692 }
693
694 if (swkbd_calc_arg.flags.set_cursor_position) {
695 current_cursor_position = swkbd_calc_arg.cursor_position;
696 }
697
698 if (swkbd_calc_arg.flags.set_utf8_mode) {
699 inline_use_utf8 = swkbd_calc_arg.utf8_mode;
700 }
701
702 if (swkbd_state <= SwkbdState::InitializedIsHidden &&
703 swkbd_calc_arg.flags.unset_customize_dic) {
704 ReplyUnsetCustomizeDic();
198 } 705 }
706
707 if (swkbd_state <= SwkbdState::InitializedIsHidden &&
708 swkbd_calc_arg.flags.unset_user_word_info) {
709 ReplyReleasedUserWordInfo();
710 }
711
712 if (swkbd_state == SwkbdState::NotInitialized && swkbd_calc_arg.flags.set_initialize_arg) {
713 InitializeFrontendKeyboard();
714
715 ChangeState(SwkbdState::InitializedIsHidden);
716
717 ReplyFinishedInitialize();
718 }
719
720 if (!swkbd_calc_arg.flags.set_initialize_arg &&
721 (swkbd_calc_arg.flags.set_input_text || swkbd_calc_arg.flags.set_cursor_position)) {
722 InlineTextChanged();
723 }
724
725 if (swkbd_state == SwkbdState::InitializedIsHidden && swkbd_calc_arg.flags.appear) {
726 ShowInlineKeyboard();
727 return;
728 }
729
730 if (swkbd_state == SwkbdState::InitializedIsShown && swkbd_calc_arg.flags.disappear) {
731 HideInlineKeyboard();
732 return;
733 }
734}
735
736void SoftwareKeyboard::RequestSetCustomizedDictionaries(const std::vector<u8>& request_data) {
737 LOG_WARNING(Service_AM, "SetCustomizedDictionaries is not implemented.");
738}
739
740void SoftwareKeyboard::RequestUnsetCustomizedDictionaries(const std::vector<u8>& request_data) {
741 LOG_WARNING(Service_AM, "(STUBBED) Processing Request: UnsetCustomizedDictionaries");
742
743 ReplyUnsetCustomizedDictionaries();
744}
745
746void SoftwareKeyboard::RequestSetChangedStringV2Flag(const std::vector<u8>& request_data) {
747 LOG_DEBUG(Service_AM, "Processing Request: SetChangedStringV2Flag");
748
749 ASSERT(request_data.size() == sizeof(SwkbdRequestCommand) + 1);
750
751 std::memcpy(&use_changed_string_v2, request_data.data() + sizeof(SwkbdRequestCommand), 1);
752}
753
754void SoftwareKeyboard::RequestSetMovedCursorV2Flag(const std::vector<u8>& request_data) {
755 LOG_DEBUG(Service_AM, "Processing Request: SetMovedCursorV2Flag");
756
757 ASSERT(request_data.size() == sizeof(SwkbdRequestCommand) + 1);
758
759 std::memcpy(&use_moved_cursor_v2, request_data.data() + sizeof(SwkbdRequestCommand), 1);
760}
761
762// Inline Software Keyboard Replies
763
764void SoftwareKeyboard::ReplyFinishedInitialize() {
765 LOG_DEBUG(Service_AM, "Sending Reply: FinishedInitialize");
766
767 std::vector<u8> reply(REPLY_BASE_SIZE + 1);
768
769 SetReplyBase(reply, swkbd_state, SwkbdReplyType::FinishedInitialize);
770
771 broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply)));
772}
773
774void SoftwareKeyboard::ReplyDefault() {
775 LOG_DEBUG(Service_AM, "Sending Reply: Default");
776
777 std::vector<u8> reply(REPLY_BASE_SIZE);
778
779 SetReplyBase(reply, swkbd_state, SwkbdReplyType::Default);
780
781 broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply)));
782}
783
784void SoftwareKeyboard::ReplyChangedString() {
785 LOG_DEBUG(Service_AM, "Sending Reply: ChangedString");
786
787 std::vector<u8> reply(REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdChangedStringArg));
788
789 SetReplyBase(reply, swkbd_state, SwkbdReplyType::ChangedString);
790
791 const SwkbdChangedStringArg changed_string_arg{
792 .text_length{static_cast<u32>(current_text.size())},
793 .dictionary_start_cursor_position{-1},
794 .dictionary_end_cursor_position{-1},
795 .cursor_position{current_cursor_position},
796 };
797
798 std::memcpy(reply.data() + REPLY_BASE_SIZE, current_text.data(),
799 current_text.size() * sizeof(char16_t));
800 std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &changed_string_arg,
801 sizeof(SwkbdChangedStringArg));
802
803 broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply)));
804}
805
806void SoftwareKeyboard::ReplyMovedCursor() {
807 LOG_DEBUG(Service_AM, "Sending Reply: MovedCursor");
808
809 std::vector<u8> reply(REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdMovedCursorArg));
810
811 SetReplyBase(reply, swkbd_state, SwkbdReplyType::MovedCursor);
812
813 const SwkbdMovedCursorArg moved_cursor_arg{
814 .text_length{static_cast<u32>(current_text.size())},
815 .cursor_position{current_cursor_position},
816 };
817
818 std::memcpy(reply.data() + REPLY_BASE_SIZE, current_text.data(),
819 current_text.size() * sizeof(char16_t));
820 std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &moved_cursor_arg,
821 sizeof(SwkbdMovedCursorArg));
822
823 broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply)));
824}
825
826void SoftwareKeyboard::ReplyMovedTab() {
827 LOG_DEBUG(Service_AM, "Sending Reply: MovedTab");
828
829 std::vector<u8> reply(REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdMovedTabArg));
830
831 SetReplyBase(reply, swkbd_state, SwkbdReplyType::MovedTab);
832
833 const SwkbdMovedTabArg moved_tab_arg{
834 .text_length{static_cast<u32>(current_text.size())},
835 .cursor_position{current_cursor_position},
836 };
837
838 std::memcpy(reply.data() + REPLY_BASE_SIZE, current_text.data(),
839 current_text.size() * sizeof(char16_t));
840 std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &moved_tab_arg,
841 sizeof(SwkbdMovedTabArg));
842
843 broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply)));
844}
845
846void SoftwareKeyboard::ReplyDecidedEnter() {
847 LOG_DEBUG(Service_AM, "Sending Reply: DecidedEnter");
848
849 std::vector<u8> reply(REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdDecidedEnterArg));
850
851 SetReplyBase(reply, swkbd_state, SwkbdReplyType::DecidedEnter);
852
853 const SwkbdDecidedEnterArg decided_enter_arg{
854 .text_length{static_cast<u32>(current_text.size())},
855 };
856
857 std::memcpy(reply.data() + REPLY_BASE_SIZE, current_text.data(),
858 current_text.size() * sizeof(char16_t));
859 std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &decided_enter_arg,
860 sizeof(SwkbdDecidedEnterArg));
861
862 broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply)));
863
864 HideInlineKeyboard();
865}
866
867void SoftwareKeyboard::ReplyDecidedCancel() {
868 LOG_DEBUG(Service_AM, "Sending Reply: DecidedCancel");
869
870 std::vector<u8> reply(REPLY_BASE_SIZE);
871
872 SetReplyBase(reply, swkbd_state, SwkbdReplyType::DecidedCancel);
873
874 broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply)));
875
876 HideInlineKeyboard();
877}
878
879void SoftwareKeyboard::ReplyChangedStringUtf8() {
880 LOG_DEBUG(Service_AM, "Sending Reply: ChangedStringUtf8");
881
882 std::vector<u8> reply(REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdChangedStringArg));
883
884 SetReplyBase(reply, swkbd_state, SwkbdReplyType::ChangedStringUtf8);
885
886 std::string utf8_current_text = Common::UTF16ToUTF8(current_text);
887
888 const SwkbdChangedStringArg changed_string_arg{
889 .text_length{static_cast<u32>(current_text.size())},
890 .dictionary_start_cursor_position{-1},
891 .dictionary_end_cursor_position{-1},
892 .cursor_position{current_cursor_position},
893 };
894
895 std::memcpy(reply.data() + REPLY_BASE_SIZE, utf8_current_text.data(), utf8_current_text.size());
896 std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE, &changed_string_arg,
897 sizeof(SwkbdChangedStringArg));
898
899 broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply)));
199} 900}
901
902void SoftwareKeyboard::ReplyMovedCursorUtf8() {
903 LOG_DEBUG(Service_AM, "Sending Reply: MovedCursorUtf8");
904
905 std::vector<u8> reply(REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdMovedCursorArg));
906
907 SetReplyBase(reply, swkbd_state, SwkbdReplyType::MovedCursorUtf8);
908
909 std::string utf8_current_text = Common::UTF16ToUTF8(current_text);
910
911 const SwkbdMovedCursorArg moved_cursor_arg{
912 .text_length{static_cast<u32>(current_text.size())},
913 .cursor_position{current_cursor_position},
914 };
915
916 std::memcpy(reply.data() + REPLY_BASE_SIZE, utf8_current_text.data(), utf8_current_text.size());
917 std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE, &moved_cursor_arg,
918 sizeof(SwkbdMovedCursorArg));
919
920 broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply)));
921}
922
923void SoftwareKeyboard::ReplyDecidedEnterUtf8() {
924 LOG_DEBUG(Service_AM, "Sending Reply: DecidedEnterUtf8");
925
926 std::vector<u8> reply(REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdDecidedEnterArg));
927
928 SetReplyBase(reply, swkbd_state, SwkbdReplyType::DecidedEnterUtf8);
929
930 std::string utf8_current_text = Common::UTF16ToUTF8(current_text);
931
932 const SwkbdDecidedEnterArg decided_enter_arg{
933 .text_length{static_cast<u32>(current_text.size())},
934 };
935
936 std::memcpy(reply.data() + REPLY_BASE_SIZE, utf8_current_text.data(), utf8_current_text.size());
937 std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE, &decided_enter_arg,
938 sizeof(SwkbdDecidedEnterArg));
939
940 broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply)));
941
942 HideInlineKeyboard();
943}
944
945void SoftwareKeyboard::ReplyUnsetCustomizeDic() {
946 LOG_DEBUG(Service_AM, "Sending Reply: UnsetCustomizeDic");
947
948 std::vector<u8> reply(REPLY_BASE_SIZE);
949
950 SetReplyBase(reply, swkbd_state, SwkbdReplyType::UnsetCustomizeDic);
951
952 broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply)));
953}
954
955void SoftwareKeyboard::ReplyReleasedUserWordInfo() {
956 LOG_DEBUG(Service_AM, "Sending Reply: ReleasedUserWordInfo");
957
958 std::vector<u8> reply(REPLY_BASE_SIZE);
959
960 SetReplyBase(reply, swkbd_state, SwkbdReplyType::ReleasedUserWordInfo);
961
962 broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply)));
963}
964
965void SoftwareKeyboard::ReplyUnsetCustomizedDictionaries() {
966 LOG_DEBUG(Service_AM, "Sending Reply: UnsetCustomizedDictionaries");
967
968 std::vector<u8> reply(REPLY_BASE_SIZE);
969
970 SetReplyBase(reply, swkbd_state, SwkbdReplyType::UnsetCustomizedDictionaries);
971
972 broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply)));
973}
974
975void SoftwareKeyboard::ReplyChangedStringV2() {
976 LOG_DEBUG(Service_AM, "Sending Reply: ChangedStringV2");
977
978 std::vector<u8> reply(REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdChangedStringArg) + 1);
979
980 SetReplyBase(reply, swkbd_state, SwkbdReplyType::ChangedStringV2);
981
982 const SwkbdChangedStringArg changed_string_arg{
983 .text_length{static_cast<u32>(current_text.size())},
984 .dictionary_start_cursor_position{-1},
985 .dictionary_end_cursor_position{-1},
986 .cursor_position{current_cursor_position},
987 };
988
989 constexpr u8 flag = 0;
990
991 std::memcpy(reply.data() + REPLY_BASE_SIZE, current_text.data(),
992 current_text.size() * sizeof(char16_t));
993 std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &changed_string_arg,
994 sizeof(SwkbdChangedStringArg));
995 std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdChangedStringArg),
996 &flag, 1);
997
998 broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply)));
999}
1000
1001void SoftwareKeyboard::ReplyMovedCursorV2() {
1002 LOG_DEBUG(Service_AM, "Sending Reply: MovedCursorV2");
1003
1004 std::vector<u8> reply(REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdMovedCursorArg) + 1);
1005
1006 SetReplyBase(reply, swkbd_state, SwkbdReplyType::MovedCursorV2);
1007
1008 const SwkbdMovedCursorArg moved_cursor_arg{
1009 .text_length{static_cast<u32>(current_text.size())},
1010 .cursor_position{current_cursor_position},
1011 };
1012
1013 constexpr u8 flag = 0;
1014
1015 std::memcpy(reply.data() + REPLY_BASE_SIZE, current_text.data(),
1016 current_text.size() * sizeof(char16_t));
1017 std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &moved_cursor_arg,
1018 sizeof(SwkbdMovedCursorArg));
1019 std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdMovedCursorArg),
1020 &flag, 1);
1021
1022 broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply)));
1023}
1024
1025void SoftwareKeyboard::ReplyChangedStringUtf8V2() {
1026 LOG_DEBUG(Service_AM, "Sending Reply: ChangedStringUtf8V2");
1027
1028 std::vector<u8> reply(REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdChangedStringArg) + 1);
1029
1030 SetReplyBase(reply, swkbd_state, SwkbdReplyType::ChangedStringUtf8V2);
1031
1032 std::string utf8_current_text = Common::UTF16ToUTF8(current_text);
1033
1034 const SwkbdChangedStringArg changed_string_arg{
1035 .text_length{static_cast<u32>(current_text.size())},
1036 .dictionary_start_cursor_position{-1},
1037 .dictionary_end_cursor_position{-1},
1038 .cursor_position{current_cursor_position},
1039 };
1040
1041 constexpr u8 flag = 0;
1042
1043 std::memcpy(reply.data() + REPLY_BASE_SIZE, utf8_current_text.data(), utf8_current_text.size());
1044 std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE, &changed_string_arg,
1045 sizeof(SwkbdChangedStringArg));
1046 std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdChangedStringArg),
1047 &flag, 1);
1048
1049 broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply)));
1050}
1051
1052void SoftwareKeyboard::ReplyMovedCursorUtf8V2() {
1053 LOG_DEBUG(Service_AM, "Sending Reply: MovedCursorUtf8V2");
1054
1055 std::vector<u8> reply(REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdMovedCursorArg) + 1);
1056
1057 SetReplyBase(reply, swkbd_state, SwkbdReplyType::MovedCursorUtf8V2);
1058
1059 std::string utf8_current_text = Common::UTF16ToUTF8(current_text);
1060
1061 const SwkbdMovedCursorArg moved_cursor_arg{
1062 .text_length{static_cast<u32>(current_text.size())},
1063 .cursor_position{current_cursor_position},
1064 };
1065
1066 constexpr u8 flag = 0;
1067
1068 std::memcpy(reply.data() + REPLY_BASE_SIZE, utf8_current_text.data(), utf8_current_text.size());
1069 std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE, &moved_cursor_arg,
1070 sizeof(SwkbdMovedCursorArg));
1071 std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdMovedCursorArg),
1072 &flag, 1);
1073
1074 broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply)));
1075}
1076
200} // namespace Service::AM::Applets 1077} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/software_keyboard.h b/src/core/hle/service/am/applets/software_keyboard.h
index 1d260fef8..7c67b7574 100644
--- a/src/core/hle/service/am/applets/software_keyboard.h
+++ b/src/core/hle/service/am/applets/software_keyboard.h
@@ -1,20 +1,14 @@
1// Copyright 2018 yuzu emulator team 1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
8#include <string>
9#include <vector>
10
11#include "common/common_funcs.h" 7#include "common/common_funcs.h"
12#include "common/common_types.h" 8#include "common/common_types.h"
13#include "common/swap.h" 9#include "core/hle/result.h"
14#include "core/hle/service/am/am.h"
15#include "core/hle/service/am/applets/applets.h" 10#include "core/hle/service/am/applets/applets.h"
16 11#include "core/hle/service/am/applets/software_keyboard_types.h"
17union ResultCode;
18 12
19namespace Core { 13namespace Core {
20class System; 14class System;
@@ -22,45 +16,10 @@ class System;
22 16
23namespace Service::AM::Applets { 17namespace Service::AM::Applets {
24 18
25enum class KeysetDisable : u32 {
26 Space = 0x02,
27 Address = 0x04,
28 Percent = 0x08,
29 Slashes = 0x10,
30 Numbers = 0x40,
31 DownloadCode = 0x80,
32};
33
34struct KeyboardConfig {
35 INSERT_PADDING_BYTES(4);
36 std::array<char16_t, 9> submit_text;
37 u16_le left_symbol_key;
38 u16_le right_symbol_key;
39 INSERT_PADDING_BYTES(1);
40 KeysetDisable keyset_disable_bitmask;
41 u32_le initial_cursor_position;
42 std::array<char16_t, 65> header_text;
43 std::array<char16_t, 129> sub_text;
44 std::array<char16_t, 257> guide_text;
45 u32_le length_limit;
46 INSERT_PADDING_BYTES(4);
47 u32_le is_password;
48 INSERT_PADDING_BYTES(5);
49 bool utf_8;
50 bool draw_background;
51 u32_le initial_string_offset;
52 u32_le initial_string_size;
53 u32_le user_dictionary_offset;
54 u32_le user_dictionary_size;
55 bool text_check;
56 u64_le text_check_callback;
57};
58static_assert(sizeof(KeyboardConfig) == 0x3E0, "KeyboardConfig has incorrect size.");
59
60class SoftwareKeyboard final : public Applet { 19class SoftwareKeyboard final : public Applet {
61public: 20public:
62 explicit SoftwareKeyboard(Core::System& system_, 21 explicit SoftwareKeyboard(Core::System& system_, LibraryAppletMode applet_mode_,
63 const Core::Frontend::SoftwareKeyboardApplet& frontend_); 22 Core::Frontend::SoftwareKeyboardApplet& frontend_);
64 ~SoftwareKeyboard() override; 23 ~SoftwareKeyboard() override;
65 24
66 void Initialize() override; 25 void Initialize() override;
@@ -70,17 +29,138 @@ public:
70 void ExecuteInteractive() override; 29 void ExecuteInteractive() override;
71 void Execute() override; 30 void Execute() override;
72 31
73 void WriteText(std::optional<std::u16string> text); 32 /**
33 * Submits the input text to the application.
34 * If text checking is enabled, the application will verify the input text.
35 * If use_utf8 is enabled, the input text will be converted to UTF-8 prior to being submitted.
36 * This should only be used by the normal software keyboard.
37 *
38 * @param result SwkbdResult enum
39 * @param submitted_text UTF-16 encoded string
40 */
41 void SubmitTextNormal(SwkbdResult result, std::u16string submitted_text);
42
43 /**
44 * Submits the input text to the application.
45 * If utf8_mode is enabled, the input text will be converted to UTF-8 prior to being submitted.
46 * This should only be used by the inline software keyboard.
47 *
48 * @param reply_type SwkbdReplyType enum
49 * @param submitted_text UTF-16 encoded string
50 * @param cursor_position The current position of the text cursor
51 */
52 void SubmitTextInline(SwkbdReplyType reply_type, std::u16string submitted_text,
53 s32 cursor_position);
74 54
75private: 55private:
76 const Core::Frontend::SoftwareKeyboardApplet& frontend; 56 /// Initializes the normal software keyboard.
57 void InitializeForeground();
77 58
78 KeyboardConfig config; 59 /// Initializes the inline software keyboard.
79 std::u16string initial_text; 60 void InitializeBackground(LibraryAppletMode applet_mode);
80 bool complete = false; 61
81 bool is_inline = false; 62 /// Processes the text check sent by the application.
82 std::vector<u8> final_data; 63 void ProcessTextCheck();
64
65 /// Processes the inline software keyboard request command sent by the application.
66 void ProcessInlineKeyboardRequest();
67
68 /// Submits the input text and exits the applet.
69 void SubmitNormalOutputAndExit(SwkbdResult result, std::u16string submitted_text);
70
71 /// Submits the input text for text checking.
72 void SubmitForTextCheck(std::u16string submitted_text);
73
74 /// Sends a reply to the application after processing a request command.
75 void SendReply(SwkbdReplyType reply_type);
76
77 /// Changes the inline keyboard state.
78 void ChangeState(SwkbdState state);
79
80 /**
81 * Signals the frontend to initialize the software keyboard with common parameters.
82 * This initializes either the normal software keyboard or the inline software keyboard
83 * depending on the state of is_background.
84 * Note that this does not cause the keyboard to appear.
85 * Use the respective Show*Keyboard() functions to cause the respective keyboards to appear.
86 */
87 void InitializeFrontendKeyboard();
88
89 /// Signals the frontend to show the normal software keyboard.
90 void ShowNormalKeyboard();
91
92 /// Signals the frontend to show the text check dialog.
93 void ShowTextCheckDialog(SwkbdTextCheckResult text_check_result,
94 std::u16string text_check_message);
95
96 /// Signals the frontend to show the inline software keyboard.
97 void ShowInlineKeyboard();
98
99 /// Signals the frontend to hide the inline software keyboard.
100 void HideInlineKeyboard();
101
102 /// Signals the frontend that the current inline keyboard text has changed.
103 void InlineTextChanged();
104
105 /// Signals both the frontend and application that the software keyboard is exiting.
106 void ExitKeyboard();
107
108 // Inline Software Keyboard Requests
109
110 void RequestFinalize(const std::vector<u8>& request_data);
111 void RequestSetUserWordInfo(const std::vector<u8>& request_data);
112 void RequestSetCustomizeDic(const std::vector<u8>& request_data);
113 void RequestCalc(const std::vector<u8>& request_data);
114 void RequestSetCustomizedDictionaries(const std::vector<u8>& request_data);
115 void RequestUnsetCustomizedDictionaries(const std::vector<u8>& request_data);
116 void RequestSetChangedStringV2Flag(const std::vector<u8>& request_data);
117 void RequestSetMovedCursorV2Flag(const std::vector<u8>& request_data);
118
119 // Inline Software Keyboard Replies
120
121 void ReplyFinishedInitialize();
122 void ReplyDefault();
123 void ReplyChangedString();
124 void ReplyMovedCursor();
125 void ReplyMovedTab();
126 void ReplyDecidedEnter();
127 void ReplyDecidedCancel();
128 void ReplyChangedStringUtf8();
129 void ReplyMovedCursorUtf8();
130 void ReplyDecidedEnterUtf8();
131 void ReplyUnsetCustomizeDic();
132 void ReplyReleasedUserWordInfo();
133 void ReplyUnsetCustomizedDictionaries();
134 void ReplyChangedStringV2();
135 void ReplyMovedCursorV2();
136 void ReplyChangedStringUtf8V2();
137 void ReplyMovedCursorUtf8V2();
138
139 Core::Frontend::SoftwareKeyboardApplet& frontend;
83 Core::System& system; 140 Core::System& system;
141
142 SwkbdAppletVersion swkbd_applet_version;
143
144 SwkbdConfigCommon swkbd_config_common;
145 SwkbdConfigOld swkbd_config_old;
146 SwkbdConfigOld2 swkbd_config_old2;
147 SwkbdConfigNew swkbd_config_new;
148 std::u16string initial_text;
149
150 SwkbdState swkbd_state{SwkbdState::NotInitialized};
151 SwkbdInitializeArg swkbd_initialize_arg;
152 SwkbdCalcArg swkbd_calc_arg;
153 bool use_changed_string_v2{false};
154 bool use_moved_cursor_v2{false};
155 bool inline_use_utf8{false};
156 s32 current_cursor_position{};
157
158 std::u16string current_text;
159
160 bool is_background{false};
161
162 bool complete{false};
163 ResultCode status{RESULT_SUCCESS};
84}; 164};
85 165
86} // namespace Service::AM::Applets 166} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/software_keyboard_types.h b/src/core/hle/service/am/applets/software_keyboard_types.h
new file mode 100644
index 000000000..21aa8e800
--- /dev/null
+++ b/src/core/hle/service/am/applets/software_keyboard_types.h
@@ -0,0 +1,295 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8
9#include "common/bit_field.h"
10#include "common/common_funcs.h"
11#include "common/common_types.h"
12#include "common/swap.h"
13
14namespace Service::AM::Applets {
15
16constexpr std::size_t MAX_OK_TEXT_LENGTH = 8;
17constexpr std::size_t MAX_HEADER_TEXT_LENGTH = 64;
18constexpr std::size_t MAX_SUB_TEXT_LENGTH = 128;
19constexpr std::size_t MAX_GUIDE_TEXT_LENGTH = 256;
20constexpr std::size_t STRING_BUFFER_SIZE = 0x7D4;
21
22enum class SwkbdAppletVersion : u32_le {
23 Version5 = 0x5, // 1.0.0
24 Version65542 = 0x10006, // 2.0.0 - 2.3.0
25 Version196615 = 0x30007, // 3.0.0 - 3.0.2
26 Version262152 = 0x40008, // 4.0.0 - 4.1.0
27 Version327689 = 0x50009, // 5.0.0 - 5.1.0
28 Version393227 = 0x6000B, // 6.0.0 - 7.0.1
29 Version524301 = 0x8000D, // 8.0.0+
30};
31
32enum class SwkbdType : u32 {
33 Normal,
34 NumberPad,
35 Qwerty,
36 Unknown3,
37 Latin,
38 SimplifiedChinese,
39 TraditionalChinese,
40 Korean,
41};
42
43enum class SwkbdInitialCursorPosition : u32 {
44 Start,
45 End,
46};
47
48enum class SwkbdPasswordMode : u32 {
49 Disabled,
50 Enabled,
51};
52
53enum class SwkbdTextDrawType : u32 {
54 Line,
55 Box,
56 DownloadCode,
57};
58
59enum class SwkbdResult : u32 {
60 Ok,
61 Cancel,
62};
63
64enum class SwkbdTextCheckResult : u32 {
65 Success,
66 Failure,
67 Confirm,
68 Silent,
69};
70
71enum class SwkbdState : u32 {
72 NotInitialized = 0x0,
73 InitializedIsHidden = 0x1,
74 InitializedIsAppearing = 0x2,
75 InitializedIsShown = 0x3,
76 InitializedIsDisappearing = 0x4,
77};
78
79enum class SwkbdRequestCommand : u32 {
80 Finalize = 0x4,
81 SetUserWordInfo = 0x6,
82 SetCustomizeDic = 0x7,
83 Calc = 0xA,
84 SetCustomizedDictionaries = 0xB,
85 UnsetCustomizedDictionaries = 0xC,
86 SetChangedStringV2Flag = 0xD,
87 SetMovedCursorV2Flag = 0xE,
88};
89
90enum class SwkbdReplyType : u32 {
91 FinishedInitialize = 0x0,
92 Default = 0x1,
93 ChangedString = 0x2,
94 MovedCursor = 0x3,
95 MovedTab = 0x4,
96 DecidedEnter = 0x5,
97 DecidedCancel = 0x6,
98 ChangedStringUtf8 = 0x7,
99 MovedCursorUtf8 = 0x8,
100 DecidedEnterUtf8 = 0x9,
101 UnsetCustomizeDic = 0xA,
102 ReleasedUserWordInfo = 0xB,
103 UnsetCustomizedDictionaries = 0xC,
104 ChangedStringV2 = 0xD,
105 MovedCursorV2 = 0xE,
106 ChangedStringUtf8V2 = 0xF,
107 MovedCursorUtf8V2 = 0x10,
108};
109
110struct SwkbdKeyDisableFlags {
111 union {
112 u32 raw{};
113
114 BitField<1, 1, u32> space;
115 BitField<2, 1, u32> at;
116 BitField<3, 1, u32> percent;
117 BitField<4, 1, u32> slash;
118 BitField<5, 1, u32> backslash;
119 BitField<6, 1, u32> numbers;
120 BitField<7, 1, u32> download_code;
121 BitField<8, 1, u32> username;
122 };
123};
124static_assert(sizeof(SwkbdKeyDisableFlags) == 0x4, "SwkbdKeyDisableFlags has incorrect size.");
125
126struct SwkbdConfigCommon {
127 SwkbdType type{};
128 std::array<char16_t, MAX_OK_TEXT_LENGTH + 1> ok_text{};
129 char16_t left_optional_symbol_key{};
130 char16_t right_optional_symbol_key{};
131 bool use_prediction{};
132 INSERT_PADDING_BYTES(1);
133 SwkbdKeyDisableFlags key_disable_flags{};
134 SwkbdInitialCursorPosition initial_cursor_position{};
135 std::array<char16_t, MAX_HEADER_TEXT_LENGTH + 1> header_text{};
136 std::array<char16_t, MAX_SUB_TEXT_LENGTH + 1> sub_text{};
137 std::array<char16_t, MAX_GUIDE_TEXT_LENGTH + 1> guide_text{};
138 u32 max_text_length{};
139 u32 min_text_length{};
140 SwkbdPasswordMode password_mode{};
141 SwkbdTextDrawType text_draw_type{};
142 bool enable_return_button{};
143 bool use_utf8{};
144 bool use_blur_background{};
145 INSERT_PADDING_BYTES(1);
146 u32 initial_string_offset{};
147 u32 initial_string_length{};
148 u32 user_dictionary_offset{};
149 u32 user_dictionary_entries{};
150 bool use_text_check{};
151 INSERT_PADDING_BYTES(3);
152};
153static_assert(sizeof(SwkbdConfigCommon) == 0x3D4, "SwkbdConfigCommon has incorrect size.");
154
155#pragma pack(push, 4)
156// SwkbdAppletVersion 0x5, 0x10006
157struct SwkbdConfigOld {
158 INSERT_PADDING_WORDS(1);
159 VAddr text_check_callback{};
160};
161static_assert(sizeof(SwkbdConfigOld) == 0x3E0 - sizeof(SwkbdConfigCommon),
162 "SwkbdConfigOld has incorrect size.");
163
164// SwkbdAppletVersion 0x30007, 0x40008, 0x50009
165struct SwkbdConfigOld2 {
166 INSERT_PADDING_WORDS(1);
167 VAddr text_check_callback{};
168 std::array<u32, 8> text_grouping{};
169};
170static_assert(sizeof(SwkbdConfigOld2) == 0x400 - sizeof(SwkbdConfigCommon),
171 "SwkbdConfigOld2 has incorrect size.");
172
173// SwkbdAppletVersion 0x6000B, 0x8000D
174struct SwkbdConfigNew {
175 std::array<u32, 8> text_grouping{};
176 std::array<u64, 24> customized_dictionary_set_entries{};
177 u8 total_customized_dictionary_set_entries{};
178 bool disable_cancel_button{};
179 INSERT_PADDING_BYTES(18);
180};
181static_assert(sizeof(SwkbdConfigNew) == 0x4C8 - sizeof(SwkbdConfigCommon),
182 "SwkbdConfigNew has incorrect size.");
183#pragma pack(pop)
184
185struct SwkbdTextCheck {
186 SwkbdTextCheckResult text_check_result{};
187 std::array<char16_t, STRING_BUFFER_SIZE / 2> text_check_message{};
188};
189static_assert(sizeof(SwkbdTextCheck) == 0x7D8, "SwkbdTextCheck has incorrect size.");
190
191struct SwkbdCalcArgFlags {
192 union {
193 u64 raw{};
194
195 BitField<0, 1, u64> set_initialize_arg;
196 BitField<1, 1, u64> set_volume;
197 BitField<2, 1, u64> appear;
198 BitField<3, 1, u64> set_input_text;
199 BitField<4, 1, u64> set_cursor_position;
200 BitField<5, 1, u64> set_utf8_mode;
201 BitField<6, 1, u64> unset_customize_dic;
202 BitField<7, 1, u64> disappear;
203 BitField<8, 1, u64> unknown;
204 BitField<9, 1, u64> set_key_top_translate_scale;
205 BitField<10, 1, u64> unset_user_word_info;
206 BitField<11, 1, u64> set_disable_hardware_keyboard;
207 };
208};
209static_assert(sizeof(SwkbdCalcArgFlags) == 0x8, "SwkbdCalcArgFlags has incorrect size.");
210
211struct SwkbdInitializeArg {
212 u32 unknown{};
213 bool library_applet_mode_flag{};
214 bool is_above_hos_500{};
215 INSERT_PADDING_BYTES(2);
216};
217static_assert(sizeof(SwkbdInitializeArg) == 0x8, "SwkbdInitializeArg has incorrect size.");
218
219struct SwkbdAppearArg {
220 SwkbdType type{};
221 std::array<char16_t, MAX_OK_TEXT_LENGTH + 1> ok_text{};
222 char16_t left_optional_symbol_key{};
223 char16_t right_optional_symbol_key{};
224 bool use_prediction{};
225 bool disable_cancel_button{};
226 SwkbdKeyDisableFlags key_disable_flags{};
227 u32 max_text_length{};
228 u32 min_text_length{};
229 bool enable_return_button{};
230 INSERT_PADDING_BYTES(3);
231 u32 flags{};
232 INSERT_PADDING_WORDS(6);
233};
234static_assert(sizeof(SwkbdAppearArg) == 0x48, "SwkbdAppearArg has incorrect size.");
235
236struct SwkbdCalcArg {
237 u32 unknown{};
238 u16 calc_arg_size{};
239 INSERT_PADDING_BYTES(2);
240 SwkbdCalcArgFlags flags{};
241 SwkbdInitializeArg initialize_arg{};
242 f32 volume{};
243 s32 cursor_position{};
244 SwkbdAppearArg appear_arg{};
245 std::array<char16_t, 0x1FA> input_text{};
246 bool utf8_mode{};
247 INSERT_PADDING_BYTES(1);
248 bool enable_backspace_button{};
249 INSERT_PADDING_BYTES(3);
250 bool key_top_as_floating{};
251 bool footer_scalable{};
252 bool alpha_enabled_in_input_mode{};
253 u8 input_mode_fade_type{};
254 bool disable_touch{};
255 bool disable_hardware_keyboard{};
256 INSERT_PADDING_BYTES(8);
257 f32 key_top_scale_x{};
258 f32 key_top_scale_y{};
259 f32 key_top_translate_x{};
260 f32 key_top_translate_y{};
261 f32 key_top_bg_alpha{};
262 f32 footer_bg_alpha{};
263 f32 balloon_scale{};
264 INSERT_PADDING_WORDS(4);
265 u8 se_group{};
266 INSERT_PADDING_BYTES(3);
267};
268static_assert(sizeof(SwkbdCalcArg) == 0x4A0, "SwkbdCalcArg has incorrect size.");
269
270struct SwkbdChangedStringArg {
271 u32 text_length{};
272 s32 dictionary_start_cursor_position{};
273 s32 dictionary_end_cursor_position{};
274 s32 cursor_position{};
275};
276static_assert(sizeof(SwkbdChangedStringArg) == 0x10, "SwkbdChangedStringArg has incorrect size.");
277
278struct SwkbdMovedCursorArg {
279 u32 text_length{};
280 s32 cursor_position{};
281};
282static_assert(sizeof(SwkbdMovedCursorArg) == 0x8, "SwkbdMovedCursorArg has incorrect size.");
283
284struct SwkbdMovedTabArg {
285 u32 text_length{};
286 s32 cursor_position{};
287};
288static_assert(sizeof(SwkbdMovedTabArg) == 0x8, "SwkbdMovedTabArg has incorrect size.");
289
290struct SwkbdDecidedEnterArg {
291 u32 text_length{};
292};
293static_assert(sizeof(SwkbdDecidedEnterArg) == 0x4, "SwkbdDecidedEnterArg has incorrect size.");
294
295} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/web_browser.cpp b/src/core/hle/service/am/applets/web_browser.cpp
index 2ab420789..2404921fd 100644
--- a/src/core/hle/service/am/applets/web_browser.cpp
+++ b/src/core/hle/service/am/applets/web_browser.cpp
@@ -208,8 +208,9 @@ void ExtractSharedFonts(Core::System& system) {
208 208
209} // namespace 209} // namespace
210 210
211WebBrowser::WebBrowser(Core::System& system_, const Core::Frontend::WebBrowserApplet& frontend_) 211WebBrowser::WebBrowser(Core::System& system_, LibraryAppletMode applet_mode_,
212 : Applet{system_.Kernel()}, frontend(frontend_), system{system_} {} 212 const Core::Frontend::WebBrowserApplet& frontend_)
213 : Applet{system_, applet_mode_}, frontend(frontend_), system{system_} {}
213 214
214WebBrowser::~WebBrowser() = default; 215WebBrowser::~WebBrowser() = default;
215 216
diff --git a/src/core/hle/service/am/applets/web_browser.h b/src/core/hle/service/am/applets/web_browser.h
index 04c274754..21fd910c2 100644
--- a/src/core/hle/service/am/applets/web_browser.h
+++ b/src/core/hle/service/am/applets/web_browser.h
@@ -25,7 +25,8 @@ namespace Service::AM::Applets {
25 25
26class WebBrowser final : public Applet { 26class WebBrowser final : public Applet {
27public: 27public:
28 WebBrowser(Core::System& system_, const Core::Frontend::WebBrowserApplet& frontend_); 28 WebBrowser(Core::System& system_, LibraryAppletMode applet_mode_,
29 const Core::Frontend::WebBrowserApplet& frontend_);
29 30
30 ~WebBrowser() override; 31 ~WebBrowser() override;
31 32
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
index c8031970b..4e58b9b80 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
@@ -32,11 +32,6 @@ NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>&
32 case 0x9: 32 case 0x9:
33 return MapBuffer(input, output); 33 return MapBuffer(input, output);
34 case 0xa: { 34 case 0xa: {
35 if (command.length == 0x1c) {
36 LOG_INFO(Service_NVDRV, "NVDEC video stream ended");
37 Tegra::ChCommandHeaderList cmdlist{{0xDEADB33F}};
38 system.GPU().PushCommandBuffer(cmdlist);
39 }
40 return UnmapBuffer(input, output); 35 return UnmapBuffer(input, output);
41 } 36 }
42 default: 37 default:
@@ -70,6 +65,9 @@ NvResult nvhost_nvdec::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>&
70} 65}
71 66
72void nvhost_nvdec::OnOpen(DeviceFD fd) {} 67void nvhost_nvdec::OnOpen(DeviceFD fd) {}
73void nvhost_nvdec::OnClose(DeviceFD fd) {} 68
69void nvhost_nvdec::OnClose(DeviceFD fd) {
70 system.GPU().ClearCommandBuffer();
71}
74 72
75} // namespace Service::Nvidia::Devices 73} // namespace Service::Nvidia::Devices
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index 9bdb282d2..7c42f1177 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -480,11 +480,7 @@ void GPU::PushCommandBuffer(Tegra::ChCommandHeaderList& entries) {
480 if (!use_nvdec) { 480 if (!use_nvdec) {
481 return; 481 return;
482 } 482 }
483 // This condition fires when a video stream ends, clear all intermediary data 483
484 if (entries[0].raw == 0xDEADB33F) {
485 cdma_pusher.reset();
486 return;
487 }
488 if (!cdma_pusher) { 484 if (!cdma_pusher) {
489 cdma_pusher = std::make_unique<Tegra::CDmaPusher>(*this); 485 cdma_pusher = std::make_unique<Tegra::CDmaPusher>(*this);
490 } 486 }
@@ -496,6 +492,12 @@ void GPU::PushCommandBuffer(Tegra::ChCommandHeaderList& entries) {
496 cdma_pusher->ProcessEntries(std::move(entries)); 492 cdma_pusher->ProcessEntries(std::move(entries));
497} 493}
498 494
495void GPU::ClearCommandBuffer() {
496 // This condition fires when a video stream ends, clear all intermediary data
497 cdma_pusher.reset();
498 LOG_INFO(Service_NVDRV, "NVDEC video stream ended");
499}
500
499void GPU::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { 501void GPU::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
500 gpu_thread.SwapBuffers(framebuffer); 502 gpu_thread.SwapBuffers(framebuffer);
501} 503}
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index ecab35d3b..b1960ea86 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -324,6 +324,9 @@ public:
324 /// Push GPU command buffer entries to be processed 324 /// Push GPU command buffer entries to be processed
325 void PushCommandBuffer(Tegra::ChCommandHeaderList& entries); 325 void PushCommandBuffer(Tegra::ChCommandHeaderList& entries);
326 326
327 /// Frees the CDMAPusher to free up resources
328 void ClearCommandBuffer();
329
327 /// Swap buffers (render frame) 330 /// Swap buffers (render frame)
328 void SwapBuffers(const Tegra::FramebufferConfig* framebuffer); 331 void SwapBuffers(const Tegra::FramebufferConfig* framebuffer);
329 332
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index b025ced1c..cc0790e07 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -18,6 +18,7 @@ add_executable(yuzu
18 applets/profile_select.h 18 applets/profile_select.h
19 applets/software_keyboard.cpp 19 applets/software_keyboard.cpp
20 applets/software_keyboard.h 20 applets/software_keyboard.h
21 applets/software_keyboard.ui
21 applets/web_browser.cpp 22 applets/web_browser.cpp
22 applets/web_browser.h 23 applets/web_browser.h
23 bootmanager.cpp 24 bootmanager.cpp
@@ -143,6 +144,9 @@ add_executable(yuzu
143 uisettings.h 144 uisettings.h
144 util/limitable_input_dialog.cpp 145 util/limitable_input_dialog.cpp
145 util/limitable_input_dialog.h 146 util/limitable_input_dialog.h
147 util/overlay_dialog.cpp
148 util/overlay_dialog.h
149 util/overlay_dialog.ui
146 util/sequence_dialog/sequence_dialog.cpp 150 util/sequence_dialog/sequence_dialog.cpp
147 util/sequence_dialog/sequence_dialog.h 151 util/sequence_dialog/sequence_dialog.h
148 util/url_request_interceptor.cpp 152 util/url_request_interceptor.cpp
diff --git a/src/yuzu/applets/error.cpp b/src/yuzu/applets/error.cpp
index 8ee03ddb3..085688cd4 100644
--- a/src/yuzu/applets/error.cpp
+++ b/src/yuzu/applets/error.cpp
@@ -19,11 +19,11 @@ QtErrorDisplay::~QtErrorDisplay() = default;
19void QtErrorDisplay::ShowError(ResultCode error, std::function<void()> finished) const { 19void QtErrorDisplay::ShowError(ResultCode error, std::function<void()> finished) const {
20 callback = std::move(finished); 20 callback = std::move(finished);
21 emit MainWindowDisplayError( 21 emit MainWindowDisplayError(
22 tr("An error has occurred.\nPlease try again or contact the developer of the " 22 tr("Error Code: %1-%2 (0x%3)")
23 "software.\n\nError Code: %1-%2 (0x%3)")
24 .arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0')) 23 .arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0'))
25 .arg(error.description, 4, 10, QChar::fromLatin1('0')) 24 .arg(error.description, 4, 10, QChar::fromLatin1('0'))
26 .arg(error.raw, 8, 16, QChar::fromLatin1('0'))); 25 .arg(error.raw, 8, 16, QChar::fromLatin1('0')),
26 tr("An error has occurred.\nPlease try again or contact the developer of the software."));
27} 27}
28 28
29void QtErrorDisplay::ShowErrorWithTimestamp(ResultCode error, std::chrono::seconds time, 29void QtErrorDisplay::ShowErrorWithTimestamp(ResultCode error, std::chrono::seconds time,
@@ -32,13 +32,14 @@ void QtErrorDisplay::ShowErrorWithTimestamp(ResultCode error, std::chrono::secon
32 32
33 const QDateTime date_time = QDateTime::fromSecsSinceEpoch(time.count()); 33 const QDateTime date_time = QDateTime::fromSecsSinceEpoch(time.count());
34 emit MainWindowDisplayError( 34 emit MainWindowDisplayError(
35 tr("An error occurred on %1 at %2.\nPlease try again or contact the " 35 tr("Error Code: %1-%2 (0x%3)")
36 "developer of the software.\n\nError Code: %3-%4 (0x%5)")
37 .arg(date_time.toString(QStringLiteral("dddd, MMMM d, yyyy")))
38 .arg(date_time.toString(QStringLiteral("h:mm:ss A")))
39 .arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0')) 36 .arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0'))
40 .arg(error.description, 4, 10, QChar::fromLatin1('0')) 37 .arg(error.description, 4, 10, QChar::fromLatin1('0'))
41 .arg(error.raw, 8, 16, QChar::fromLatin1('0'))); 38 .arg(error.raw, 8, 16, QChar::fromLatin1('0')),
39 tr("An error occurred on %1 at %2.\nPlease try again or contact the developer of the "
40 "software.")
41 .arg(date_time.toString(QStringLiteral("dddd, MMMM d, yyyy")))
42 .arg(date_time.toString(QStringLiteral("h:mm:ss A"))));
42} 43}
43 44
44void QtErrorDisplay::ShowCustomErrorText(ResultCode error, std::string dialog_text, 45void QtErrorDisplay::ShowCustomErrorText(ResultCode error, std::string dialog_text,
@@ -46,10 +47,11 @@ void QtErrorDisplay::ShowCustomErrorText(ResultCode error, std::string dialog_te
46 std::function<void()> finished) const { 47 std::function<void()> finished) const {
47 callback = std::move(finished); 48 callback = std::move(finished);
48 emit MainWindowDisplayError( 49 emit MainWindowDisplayError(
49 tr("An error has occurred.\nError Code: %1-%2 (0x%3)\n\n%4\n\n%5") 50 tr("Error Code: %1-%2 (0x%3)")
50 .arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0')) 51 .arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0'))
51 .arg(error.description, 4, 10, QChar::fromLatin1('0')) 52 .arg(error.description, 4, 10, QChar::fromLatin1('0'))
52 .arg(error.raw, 8, 16, QChar::fromLatin1('0')) 53 .arg(error.raw, 8, 16, QChar::fromLatin1('0')),
54 tr("An error has occurred.\n\n%1\n\n%2")
53 .arg(QString::fromStdString(dialog_text)) 55 .arg(QString::fromStdString(dialog_text))
54 .arg(QString::fromStdString(fullscreen_text))); 56 .arg(QString::fromStdString(fullscreen_text)));
55} 57}
diff --git a/src/yuzu/applets/error.h b/src/yuzu/applets/error.h
index b0932d895..8bd895a32 100644
--- a/src/yuzu/applets/error.h
+++ b/src/yuzu/applets/error.h
@@ -24,7 +24,7 @@ public:
24 std::function<void()> finished) const override; 24 std::function<void()> finished) const override;
25 25
26signals: 26signals:
27 void MainWindowDisplayError(QString error) const; 27 void MainWindowDisplayError(QString error_code, QString error_text) const;
28 28
29private: 29private:
30 void MainWindowFinishedError(); 30 void MainWindowFinishedError();
diff --git a/src/yuzu/applets/software_keyboard.cpp b/src/yuzu/applets/software_keyboard.cpp
index ab8cfd8ee..fd3368479 100644
--- a/src/yuzu/applets/software_keyboard.cpp
+++ b/src/yuzu/applets/software_keyboard.cpp
@@ -1,153 +1,1641 @@
1// Copyright 2018 yuzu Emulator Project 1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm> 5#include <QCursor>
6#include <mutex> 6#include <QKeyEvent>
7#include <QDialogButtonBox> 7#include <QScreen>
8#include <QFont> 8
9#include <QLabel> 9#include "common/logging/log.h"
10#include <QLineEdit> 10#include "common/settings.h"
11#include <QVBoxLayout> 11#include "common/string_util.h"
12#include "core/hle/lock.h" 12#include "core/core.h"
13#include "core/frontend/input_interpreter.h"
14#include "ui_software_keyboard.h"
13#include "yuzu/applets/software_keyboard.h" 15#include "yuzu/applets/software_keyboard.h"
14#include "yuzu/main.h" 16#include "yuzu/main.h"
17#include "yuzu/util/overlay_dialog.h"
18
19namespace {
20
21using namespace Service::AM::Applets;
22
23constexpr float BASE_HEADER_FONT_SIZE = 23.0f;
24constexpr float BASE_SUB_FONT_SIZE = 17.0f;
25constexpr float BASE_EDITOR_FONT_SIZE = 26.0f;
26constexpr float BASE_CHAR_BUTTON_FONT_SIZE = 28.0f;
27constexpr float BASE_LABEL_BUTTON_FONT_SIZE = 18.0f;
28constexpr float BASE_ICON_BUTTON_SIZE = 36.0f;
29[[maybe_unused]] constexpr float BASE_WIDTH = 1280.0f;
30constexpr float BASE_HEIGHT = 720.0f;
31
32} // Anonymous namespace
33
34QtSoftwareKeyboardDialog::QtSoftwareKeyboardDialog(
35 QWidget* parent, Core::System& system_, bool is_inline_,
36 Core::Frontend::KeyboardInitializeParameters initialize_parameters_)
37 : QDialog(parent), ui{std::make_unique<Ui::QtSoftwareKeyboardDialog>()}, system{system_},
38 is_inline{is_inline_}, initialize_parameters{std::move(initialize_parameters_)} {
39 ui->setupUi(this);
40
41 setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint | Qt::WindowTitleHint |
42 Qt::WindowSystemMenuHint | Qt::CustomizeWindowHint);
43 setWindowModality(Qt::WindowModal);
44 setAttribute(Qt::WA_DeleteOnClose);
45 setAttribute(Qt::WA_TranslucentBackground);
46
47 keyboard_buttons = {{
48 {{
49 {
50 ui->button_1,
51 ui->button_2,
52 ui->button_3,
53 ui->button_4,
54 ui->button_5,
55 ui->button_6,
56 ui->button_7,
57 ui->button_8,
58 ui->button_9,
59 ui->button_0,
60 ui->button_minus,
61 ui->button_backspace,
62 },
63 {
64 ui->button_q,
65 ui->button_w,
66 ui->button_e,
67 ui->button_r,
68 ui->button_t,
69 ui->button_y,
70 ui->button_u,
71 ui->button_i,
72 ui->button_o,
73 ui->button_p,
74 ui->button_slash,
75 ui->button_return,
76 },
77 {
78 ui->button_a,
79 ui->button_s,
80 ui->button_d,
81 ui->button_f,
82 ui->button_g,
83 ui->button_h,
84 ui->button_j,
85 ui->button_k,
86 ui->button_l,
87 ui->button_colon,
88 ui->button_apostrophe,
89 ui->button_return,
90 },
91 {
92 ui->button_z,
93 ui->button_x,
94 ui->button_c,
95 ui->button_v,
96 ui->button_b,
97 ui->button_n,
98 ui->button_m,
99 ui->button_comma,
100 ui->button_dot,
101 ui->button_question,
102 ui->button_exclamation,
103 ui->button_ok,
104 },
105 {
106 ui->button_shift,
107 ui->button_shift,
108 ui->button_space,
109 ui->button_space,
110 ui->button_space,
111 ui->button_space,
112 ui->button_space,
113 ui->button_space,
114 ui->button_space,
115 ui->button_space,
116 ui->button_space,
117 ui->button_ok,
118 },
119 }},
120 {{
121 {
122 ui->button_hash,
123 ui->button_left_bracket,
124 ui->button_right_bracket,
125 ui->button_dollar,
126 ui->button_percent,
127 ui->button_circumflex,
128 ui->button_ampersand,
129 ui->button_asterisk,
130 ui->button_left_parenthesis,
131 ui->button_right_parenthesis,
132 ui->button_underscore,
133 ui->button_backspace_shift,
134 },
135 {
136 ui->button_q_shift,
137 ui->button_w_shift,
138 ui->button_e_shift,
139 ui->button_r_shift,
140 ui->button_t_shift,
141 ui->button_y_shift,
142 ui->button_u_shift,
143 ui->button_i_shift,
144 ui->button_o_shift,
145 ui->button_p_shift,
146 ui->button_at,
147 ui->button_return_shift,
148 },
149 {
150 ui->button_a_shift,
151 ui->button_s_shift,
152 ui->button_d_shift,
153 ui->button_f_shift,
154 ui->button_g_shift,
155 ui->button_h_shift,
156 ui->button_j_shift,
157 ui->button_k_shift,
158 ui->button_l_shift,
159 ui->button_semicolon,
160 ui->button_quotation,
161 ui->button_return_shift,
162 },
163 {
164 ui->button_z_shift,
165 ui->button_x_shift,
166 ui->button_c_shift,
167 ui->button_v_shift,
168 ui->button_b_shift,
169 ui->button_n_shift,
170 ui->button_m_shift,
171 ui->button_less_than,
172 ui->button_greater_than,
173 ui->button_plus,
174 ui->button_equal,
175 ui->button_ok_shift,
176 },
177 {
178 ui->button_shift_shift,
179 ui->button_shift_shift,
180 ui->button_space_shift,
181 ui->button_space_shift,
182 ui->button_space_shift,
183 ui->button_space_shift,
184 ui->button_space_shift,
185 ui->button_space_shift,
186 ui->button_space_shift,
187 ui->button_space_shift,
188 ui->button_space_shift,
189 ui->button_ok_shift,
190 },
191 }},
192 }};
15 193
16QtSoftwareKeyboardValidator::QtSoftwareKeyboardValidator( 194 numberpad_buttons = {{
17 Core::Frontend::SoftwareKeyboardParameters parameters) 195 {
18 : parameters(std::move(parameters)) {} 196 ui->button_1_num,
197 ui->button_2_num,
198 ui->button_3_num,
199 ui->button_backspace_num,
200 },
201 {
202 ui->button_4_num,
203 ui->button_5_num,
204 ui->button_6_num,
205 ui->button_ok_num,
206 },
207 {
208 ui->button_7_num,
209 ui->button_8_num,
210 ui->button_9_num,
211 ui->button_ok_num,
212 },
213 {
214 nullptr,
215 ui->button_0_num,
216 nullptr,
217 ui->button_ok_num,
218 },
219 }};
19 220
20QValidator::State QtSoftwareKeyboardValidator::validate(QString& input, int& pos) const { 221 all_buttons = {
21 if (input.size() > static_cast<s64>(parameters.max_length)) { 222 ui->button_1,
22 return Invalid; 223 ui->button_2,
224 ui->button_3,
225 ui->button_4,
226 ui->button_5,
227 ui->button_6,
228 ui->button_7,
229 ui->button_8,
230 ui->button_9,
231 ui->button_0,
232 ui->button_minus,
233 ui->button_backspace,
234 ui->button_q,
235 ui->button_w,
236 ui->button_e,
237 ui->button_r,
238 ui->button_t,
239 ui->button_y,
240 ui->button_u,
241 ui->button_i,
242 ui->button_o,
243 ui->button_p,
244 ui->button_slash,
245 ui->button_return,
246 ui->button_a,
247 ui->button_s,
248 ui->button_d,
249 ui->button_f,
250 ui->button_g,
251 ui->button_h,
252 ui->button_j,
253 ui->button_k,
254 ui->button_l,
255 ui->button_colon,
256 ui->button_apostrophe,
257 ui->button_z,
258 ui->button_x,
259 ui->button_c,
260 ui->button_v,
261 ui->button_b,
262 ui->button_n,
263 ui->button_m,
264 ui->button_comma,
265 ui->button_dot,
266 ui->button_question,
267 ui->button_exclamation,
268 ui->button_ok,
269 ui->button_shift,
270 ui->button_space,
271 ui->button_hash,
272 ui->button_left_bracket,
273 ui->button_right_bracket,
274 ui->button_dollar,
275 ui->button_percent,
276 ui->button_circumflex,
277 ui->button_ampersand,
278 ui->button_asterisk,
279 ui->button_left_parenthesis,
280 ui->button_right_parenthesis,
281 ui->button_underscore,
282 ui->button_backspace_shift,
283 ui->button_q_shift,
284 ui->button_w_shift,
285 ui->button_e_shift,
286 ui->button_r_shift,
287 ui->button_t_shift,
288 ui->button_y_shift,
289 ui->button_u_shift,
290 ui->button_i_shift,
291 ui->button_o_shift,
292 ui->button_p_shift,
293 ui->button_at,
294 ui->button_return_shift,
295 ui->button_a_shift,
296 ui->button_s_shift,
297 ui->button_d_shift,
298 ui->button_f_shift,
299 ui->button_g_shift,
300 ui->button_h_shift,
301 ui->button_j_shift,
302 ui->button_k_shift,
303 ui->button_l_shift,
304 ui->button_semicolon,
305 ui->button_quotation,
306 ui->button_z_shift,
307 ui->button_x_shift,
308 ui->button_c_shift,
309 ui->button_v_shift,
310 ui->button_b_shift,
311 ui->button_n_shift,
312 ui->button_m_shift,
313 ui->button_less_than,
314 ui->button_greater_than,
315 ui->button_plus,
316 ui->button_equal,
317 ui->button_ok_shift,
318 ui->button_shift_shift,
319 ui->button_space_shift,
320 ui->button_1_num,
321 ui->button_2_num,
322 ui->button_3_num,
323 ui->button_backspace_num,
324 ui->button_4_num,
325 ui->button_5_num,
326 ui->button_6_num,
327 ui->button_ok_num,
328 ui->button_7_num,
329 ui->button_8_num,
330 ui->button_9_num,
331 ui->button_0_num,
332 };
333
334 SetupMouseHover();
335
336 if (!initialize_parameters.ok_text.empty()) {
337 ui->button_ok->setText(QString::fromStdU16String(initialize_parameters.ok_text));
23 } 338 }
24 if (parameters.disable_space && input.contains(QLatin1Char{' '})) { 339
25 return Invalid; 340 ui->label_header->setText(QString::fromStdU16String(initialize_parameters.header_text));
341 ui->label_sub->setText(QString::fromStdU16String(initialize_parameters.sub_text));
342
343 current_text = initialize_parameters.initial_text;
344 cursor_position = initialize_parameters.initial_cursor_position;
345
346 SetTextDrawType();
347
348 for (auto* button : all_buttons) {
349 connect(button, &QPushButton::clicked, this, [this, button](bool) {
350 if (is_inline) {
351 InlineKeyboardButtonClicked(button);
352 } else {
353 NormalKeyboardButtonClicked(button);
354 }
355 });
26 } 356 }
27 if (parameters.disable_address && input.contains(QLatin1Char{'@'})) { 357
28 return Invalid; 358 // TODO (Morph): Remove this when InputInterpreter no longer relies on the HID backend
359 if (system.IsPoweredOn()) {
360 input_interpreter = std::make_unique<InputInterpreter>(system);
29 } 361 }
30 if (parameters.disable_percent && input.contains(QLatin1Char{'%'})) { 362}
31 return Invalid; 363
364QtSoftwareKeyboardDialog::~QtSoftwareKeyboardDialog() {
365 StopInputThread();
366}
367
368void QtSoftwareKeyboardDialog::ShowNormalKeyboard(QPoint pos, QSize size) {
369 if (isVisible()) {
370 return;
32 } 371 }
33 if (parameters.disable_slash && 372
34 (input.contains(QLatin1Char{'/'}) || input.contains(QLatin1Char{'\\'}))) { 373 MoveAndResizeWindow(pos, size);
35 return Invalid; 374
375 SetKeyboardType();
376 SetPasswordMode();
377 SetControllerImage();
378 DisableKeyboardButtons();
379 SetBackspaceOkEnabled();
380
381 open();
382}
383
384void QtSoftwareKeyboardDialog::ShowTextCheckDialog(
385 Service::AM::Applets::SwkbdTextCheckResult text_check_result,
386 std::u16string text_check_message) {
387 switch (text_check_result) {
388 case SwkbdTextCheckResult::Success:
389 case SwkbdTextCheckResult::Silent:
390 default:
391 break;
392 case SwkbdTextCheckResult::Failure: {
393 StopInputThread();
394
395 OverlayDialog dialog(this, system, QString{}, QString::fromStdU16String(text_check_message),
396 QString{}, tr("OK"), Qt::AlignCenter);
397 dialog.exec();
398
399 StartInputThread();
400 break;
36 } 401 }
37 if (parameters.disable_number && 402 case SwkbdTextCheckResult::Confirm: {
38 std::any_of(input.begin(), input.end(), [](QChar c) { return c.isDigit(); })) { 403 StopInputThread();
39 return Invalid; 404
405 OverlayDialog dialog(this, system, QString{}, QString::fromStdU16String(text_check_message),
406 tr("Cancel"), tr("OK"), Qt::AlignCenter);
407 if (dialog.exec() == QDialog::Accepted) {
408 emit SubmitNormalText(SwkbdResult::Ok, current_text);
409 break;
410 }
411
412 StartInputThread();
413 break;
40 } 414 }
415 }
416}
417
418void QtSoftwareKeyboardDialog::ShowInlineKeyboard(
419 Core::Frontend::InlineAppearParameters appear_parameters, QPoint pos, QSize size) {
420 MoveAndResizeWindow(pos, size);
421
422 ui->topOSK->setStyleSheet(QStringLiteral("background: rgba(0, 0, 0, 0);"));
423
424 ui->headerOSK->hide();
425 ui->subOSK->hide();
426 ui->inputOSK->hide();
427 ui->charactersOSK->hide();
428 ui->inputBoxOSK->hide();
429 ui->charactersBoxOSK->hide();
430
431 initialize_parameters.max_text_length = appear_parameters.max_text_length;
432 initialize_parameters.min_text_length = appear_parameters.min_text_length;
433 initialize_parameters.type = appear_parameters.type;
434 initialize_parameters.key_disable_flags = appear_parameters.key_disable_flags;
435 initialize_parameters.enable_backspace_button = appear_parameters.enable_backspace_button;
436 initialize_parameters.enable_return_button = appear_parameters.enable_return_button;
437 initialize_parameters.disable_cancel_button = initialize_parameters.disable_cancel_button;
41 438
42 if (parameters.disable_download_code && std::any_of(input.begin(), input.end(), [](QChar c) { 439 SetKeyboardType();
43 return c == QLatin1Char{'O'} || c == QLatin1Char{'I'}; 440 SetControllerImage();
44 })) { 441 DisableKeyboardButtons();
45 return Invalid; 442 SetBackspaceOkEnabled();
443
444 open();
445}
446
447void QtSoftwareKeyboardDialog::HideInlineKeyboard() {
448 StopInputThread();
449 QDialog::hide();
450}
451
452void QtSoftwareKeyboardDialog::InlineTextChanged(
453 Core::Frontend::InlineTextParameters text_parameters) {
454 current_text = text_parameters.input_text;
455 cursor_position = text_parameters.cursor_position;
456
457 SetBackspaceOkEnabled();
458}
459
460void QtSoftwareKeyboardDialog::ExitKeyboard() {
461 StopInputThread();
462 QDialog::done(QDialog::Accepted);
463}
464
465void QtSoftwareKeyboardDialog::open() {
466 QDialog::open();
467
468 row = 0;
469 column = 0;
470
471 const auto* const curr_button =
472 keyboard_buttons[static_cast<int>(bottom_osk_index)][row][column];
473
474 // This is a workaround for setFocus() randomly not showing focus in the UI
475 QCursor::setPos(curr_button->mapToGlobal(curr_button->rect().center()));
476
477 StartInputThread();
478}
479
480void QtSoftwareKeyboardDialog::reject() {
481 // Pressing the ESC key in a dialog calls QDialog::reject().
482 // We will override this behavior to the "Cancel" action on the software keyboard.
483 if (is_inline) {
484 emit SubmitInlineText(SwkbdReplyType::DecidedCancel, current_text, cursor_position);
485 } else {
486 emit SubmitNormalText(SwkbdResult::Cancel, current_text);
46 } 487 }
488}
489
490void QtSoftwareKeyboardDialog::keyPressEvent(QKeyEvent* event) {
491 if (!is_inline) {
492 QDialog::keyPressEvent(event);
493 return;
494 }
495
496 const auto entered_key = event->key();
47 497
48 return Acceptable; 498 switch (entered_key) {
499 case Qt::Key_Escape:
500 QDialog::keyPressEvent(event);
501 return;
502 case Qt::Key_Backspace:
503 switch (bottom_osk_index) {
504 case BottomOSKIndex::LowerCase:
505 ui->button_backspace->click();
506 break;
507 case BottomOSKIndex::UpperCase:
508 ui->button_backspace_shift->click();
509 break;
510 case BottomOSKIndex::NumberPad:
511 ui->button_backspace_num->click();
512 break;
513 default:
514 break;
515 }
516 return;
517 case Qt::Key_Return:
518 switch (bottom_osk_index) {
519 case BottomOSKIndex::LowerCase:
520 ui->button_ok->click();
521 break;
522 case BottomOSKIndex::UpperCase:
523 ui->button_ok_shift->click();
524 break;
525 case BottomOSKIndex::NumberPad:
526 ui->button_ok_num->click();
527 break;
528 default:
529 break;
530 }
531 return;
532 case Qt::Key_Left:
533 MoveTextCursorDirection(Direction::Left);
534 return;
535 case Qt::Key_Right:
536 MoveTextCursorDirection(Direction::Right);
537 return;
538 default:
539 break;
540 }
541
542 const auto entered_text = event->text();
543
544 if (entered_text.isEmpty()) {
545 return;
546 }
547
548 InlineTextInsertString(entered_text.toStdU16String());
49} 549}
50 550
51QtSoftwareKeyboardDialog::QtSoftwareKeyboardDialog( 551void QtSoftwareKeyboardDialog::MoveAndResizeWindow(QPoint pos, QSize size) {
52 QWidget* parent, Core::Frontend::SoftwareKeyboardParameters parameters_) 552 QDialog::move(pos);
53 : QDialog(parent), parameters(std::move(parameters_)) { 553 QDialog::resize(size);
54 layout = new QVBoxLayout; 554
55 555 // High DPI
56 header_label = new QLabel(QString::fromStdU16String(parameters.header_text)); 556 const float dpi_scale = qApp->screenAt(pos)->logicalDotsPerInch() / 96.0f;
57 header_label->setFont({header_label->font().family(), 11, QFont::Bold}); 557
58 if (header_label->text().isEmpty()) 558 RescaleKeyboardElements(size.width(), size.height(), dpi_scale);
59 header_label->setText(tr("Enter text:")); 559}
60 560
61 sub_label = new QLabel(QString::fromStdU16String(parameters.sub_text)); 561void QtSoftwareKeyboardDialog::RescaleKeyboardElements(float width, float height, float dpi_scale) {
62 sub_label->setFont({sub_label->font().family(), sub_label->font().pointSize(), 562 const auto header_font_size = BASE_HEADER_FONT_SIZE * (height / BASE_HEIGHT) / dpi_scale;
63 sub_label->font().weight(), true}); 563 const auto sub_font_size = BASE_SUB_FONT_SIZE * (height / BASE_HEIGHT) / dpi_scale;
64 sub_label->setHidden(parameters.sub_text.empty()); 564 const auto editor_font_size = BASE_EDITOR_FONT_SIZE * (height / BASE_HEIGHT) / dpi_scale;
65 565 const auto char_button_font_size =
66 guide_label = new QLabel(QString::fromStdU16String(parameters.guide_text)); 566 BASE_CHAR_BUTTON_FONT_SIZE * (height / BASE_HEIGHT) / dpi_scale;
67 guide_label->setHidden(parameters.guide_text.empty()); 567 const auto label_button_font_size =
68 568 BASE_LABEL_BUTTON_FONT_SIZE * (height / BASE_HEIGHT) / dpi_scale;
69 length_label = new QLabel(QStringLiteral("0/%1").arg(parameters.max_length)); 569
70 length_label->setAlignment(Qt::AlignRight); 570 QFont header_font(QStringLiteral("MS Shell Dlg 2"), header_font_size, QFont::Normal);
71 length_label->setFont({length_label->font().family(), 8}); 571 QFont sub_font(QStringLiteral("MS Shell Dlg 2"), sub_font_size, QFont::Normal);
72 572 QFont editor_font(QStringLiteral("MS Shell Dlg 2"), editor_font_size, QFont::Normal);
73 line_edit = new QLineEdit; 573 QFont char_button_font(QStringLiteral("MS Shell Dlg 2"), char_button_font_size, QFont::Normal);
74 line_edit->setValidator(new QtSoftwareKeyboardValidator(parameters)); 574 QFont label_button_font(QStringLiteral("MS Shell Dlg 2"), label_button_font_size,
75 line_edit->setMaxLength(static_cast<int>(parameters.max_length)); 575 QFont::Normal);
76 line_edit->setText(QString::fromStdU16String(parameters.initial_text)); 576
77 line_edit->setCursorPosition( 577 ui->label_header->setFont(header_font);
78 parameters.cursor_at_beginning ? 0 : static_cast<int>(parameters.initial_text.size())); 578 ui->label_sub->setFont(sub_font);
79 line_edit->setEchoMode(parameters.password ? QLineEdit::Password : QLineEdit::Normal); 579 ui->line_edit_osk->setFont(editor_font);
80 580 ui->text_edit_osk->setFont(editor_font);
81 connect(line_edit, &QLineEdit::textChanged, this, [this](const QString& text) { 581 ui->label_characters->setFont(sub_font);
82 length_label->setText(QStringLiteral("%1/%2").arg(text.size()).arg(parameters.max_length)); 582 ui->label_characters_box->setFont(sub_font);
83 }); 583
84 584 ui->label_shift->setFont(label_button_font);
85 buttons = new QDialogButtonBox(QDialogButtonBox::Cancel); 585 ui->label_shift_shift->setFont(label_button_font);
86 if (parameters.submit_text.empty()) { 586 ui->label_cancel->setFont(label_button_font);
87 buttons->addButton(QDialogButtonBox::Ok); 587 ui->label_cancel_shift->setFont(label_button_font);
588 ui->label_cancel_num->setFont(label_button_font);
589 ui->label_enter->setFont(label_button_font);
590 ui->label_enter_shift->setFont(label_button_font);
591 ui->label_enter_num->setFont(label_button_font);
592
593 for (auto* button : all_buttons) {
594 if (button == ui->button_return || button == ui->button_return_shift) {
595 button->setFont(label_button_font);
596 continue;
597 }
598
599 if (button == ui->button_space || button == ui->button_space_shift) {
600 button->setFont(label_button_font);
601 continue;
602 }
603
604 if (button == ui->button_shift || button == ui->button_shift_shift) {
605 button->setFont(label_button_font);
606 button->setIconSize(QSize(BASE_ICON_BUTTON_SIZE, BASE_ICON_BUTTON_SIZE) *
607 (height / BASE_HEIGHT));
608 continue;
609 }
610
611 if (button == ui->button_backspace || button == ui->button_backspace_shift ||
612 button == ui->button_backspace_num) {
613 button->setFont(label_button_font);
614 button->setIconSize(QSize(BASE_ICON_BUTTON_SIZE, BASE_ICON_BUTTON_SIZE) *
615 (height / BASE_HEIGHT));
616 continue;
617 }
618
619 if (button == ui->button_ok || button == ui->button_ok_shift ||
620 button == ui->button_ok_num) {
621 button->setFont(label_button_font);
622 continue;
623 }
624
625 button->setFont(char_button_font);
626 }
627}
628
629void QtSoftwareKeyboardDialog::SetKeyboardType() {
630 switch (initialize_parameters.type) {
631 case SwkbdType::Normal:
632 case SwkbdType::Qwerty:
633 case SwkbdType::Unknown3:
634 case SwkbdType::Latin:
635 case SwkbdType::SimplifiedChinese:
636 case SwkbdType::TraditionalChinese:
637 case SwkbdType::Korean:
638 default: {
639 bottom_osk_index = BottomOSKIndex::LowerCase;
640 ui->bottomOSK->setCurrentIndex(static_cast<int>(bottom_osk_index));
641
642 ui->verticalLayout_2->setStretch(0, 320);
643 ui->verticalLayout_2->setStretch(1, 400);
644
645 ui->gridLineOSK->setRowStretch(5, 94);
646 ui->gridBoxOSK->setRowStretch(2, 81);
647 break;
648 }
649 case SwkbdType::NumberPad: {
650 bottom_osk_index = BottomOSKIndex::NumberPad;
651 ui->bottomOSK->setCurrentIndex(static_cast<int>(bottom_osk_index));
652
653 ui->verticalLayout_2->setStretch(0, 370);
654 ui->verticalLayout_2->setStretch(1, 350);
655
656 ui->gridLineOSK->setRowStretch(5, 144);
657 ui->gridBoxOSK->setRowStretch(2, 131);
658 break;
659 }
660 }
661}
662
663void QtSoftwareKeyboardDialog::SetPasswordMode() {
664 switch (initialize_parameters.password_mode) {
665 case SwkbdPasswordMode::Disabled:
666 default:
667 ui->line_edit_osk->setEchoMode(QLineEdit::Normal);
668 break;
669 case SwkbdPasswordMode::Enabled:
670 ui->line_edit_osk->setEchoMode(QLineEdit::Password);
671 break;
672 }
673}
674
675void QtSoftwareKeyboardDialog::SetTextDrawType() {
676 switch (initialize_parameters.text_draw_type) {
677 case SwkbdTextDrawType::Line:
678 case SwkbdTextDrawType::DownloadCode: {
679 ui->topOSK->setCurrentIndex(0);
680
681 if (initialize_parameters.max_text_length <= 10) {
682 ui->gridLineOSK->setColumnStretch(0, 390);
683 ui->gridLineOSK->setColumnStretch(1, 500);
684 ui->gridLineOSK->setColumnStretch(2, 390);
685 } else {
686 ui->gridLineOSK->setColumnStretch(0, 130);
687 ui->gridLineOSK->setColumnStretch(1, 1020);
688 ui->gridLineOSK->setColumnStretch(2, 130);
689 }
690
691 if (is_inline) {
692 return;
693 }
694
695 connect(ui->line_edit_osk, &QLineEdit::textChanged, [this](const QString& changed_string) {
696 const auto is_valid = ValidateInputText(changed_string);
697
698 const auto text_length = static_cast<u32>(changed_string.length());
699
700 ui->label_characters->setText(QStringLiteral("%1/%2")
701 .arg(text_length)
702 .arg(initialize_parameters.max_text_length));
703
704 ui->button_ok->setEnabled(is_valid);
705 ui->button_ok_shift->setEnabled(is_valid);
706 ui->button_ok_num->setEnabled(is_valid);
707
708 ui->line_edit_osk->setFocus();
709 });
710
711 connect(ui->line_edit_osk, &QLineEdit::cursorPositionChanged,
712 [this](int old_cursor_position, int new_cursor_position) {
713 ui->button_backspace->setEnabled(
714 initialize_parameters.enable_backspace_button && new_cursor_position > 0);
715 ui->button_backspace_shift->setEnabled(
716 initialize_parameters.enable_backspace_button && new_cursor_position > 0);
717 ui->button_backspace_num->setEnabled(
718 initialize_parameters.enable_backspace_button && new_cursor_position > 0);
719
720 ui->line_edit_osk->setFocus();
721 });
722
723 connect(ui->line_edit_osk, &QLineEdit::returnPressed, [this] {
724 switch (bottom_osk_index) {
725 case BottomOSKIndex::LowerCase:
726 ui->button_ok->click();
727 break;
728 case BottomOSKIndex::UpperCase:
729 ui->button_ok_shift->click();
730 break;
731 case BottomOSKIndex::NumberPad:
732 ui->button_ok_num->click();
733 break;
734 default:
735 break;
736 }
737 });
738
739 ui->line_edit_osk->setPlaceholderText(
740 QString::fromStdU16String(initialize_parameters.guide_text));
741 ui->line_edit_osk->setText(QString::fromStdU16String(initialize_parameters.initial_text));
742 ui->line_edit_osk->setMaxLength(initialize_parameters.max_text_length);
743 ui->line_edit_osk->setCursorPosition(initialize_parameters.initial_cursor_position);
744
745 ui->label_characters->setText(QStringLiteral("%1/%2")
746 .arg(initialize_parameters.initial_text.size())
747 .arg(initialize_parameters.max_text_length));
748 break;
749 }
750 case SwkbdTextDrawType::Box:
751 default: {
752 ui->topOSK->setCurrentIndex(1);
753
754 if (is_inline) {
755 return;
756 }
757
758 connect(ui->text_edit_osk, &QTextEdit::textChanged, [this] {
759 if (static_cast<u32>(ui->text_edit_osk->toPlainText().length()) >
760 initialize_parameters.max_text_length) {
761 auto text_cursor = ui->text_edit_osk->textCursor();
762 ui->text_edit_osk->setTextCursor(text_cursor);
763 text_cursor.deletePreviousChar();
764 }
765
766 const auto is_valid = ValidateInputText(ui->text_edit_osk->toPlainText());
767
768 const auto text_length = static_cast<u32>(ui->text_edit_osk->toPlainText().length());
769
770 ui->label_characters_box->setText(QStringLiteral("%1/%2")
771 .arg(text_length)
772 .arg(initialize_parameters.max_text_length));
773
774 ui->button_ok->setEnabled(is_valid);
775 ui->button_ok_shift->setEnabled(is_valid);
776 ui->button_ok_num->setEnabled(is_valid);
777
778 ui->text_edit_osk->setFocus();
779 });
780
781 connect(ui->text_edit_osk, &QTextEdit::cursorPositionChanged, [this] {
782 const auto new_cursor_position = ui->text_edit_osk->textCursor().position();
783
784 ui->button_backspace->setEnabled(initialize_parameters.enable_backspace_button &&
785 new_cursor_position > 0);
786 ui->button_backspace_shift->setEnabled(initialize_parameters.enable_backspace_button &&
787 new_cursor_position > 0);
788 ui->button_backspace_num->setEnabled(initialize_parameters.enable_backspace_button &&
789 new_cursor_position > 0);
790
791 ui->text_edit_osk->setFocus();
792 });
793
794 ui->text_edit_osk->setPlaceholderText(
795 QString::fromStdU16String(initialize_parameters.guide_text));
796 ui->text_edit_osk->setText(QString::fromStdU16String(initialize_parameters.initial_text));
797 ui->text_edit_osk->moveCursor(initialize_parameters.initial_cursor_position == 0
798 ? QTextCursor::Start
799 : QTextCursor::End);
800
801 ui->label_characters_box->setText(QStringLiteral("%1/%2")
802 .arg(initialize_parameters.initial_text.size())
803 .arg(initialize_parameters.max_text_length));
804 break;
805 }
806 }
807}
808
809void QtSoftwareKeyboardDialog::SetControllerImage() {
810 const auto controller_type = Settings::values.players.GetValue()[8].connected
811 ? Settings::values.players.GetValue()[8].controller_type
812 : Settings::values.players.GetValue()[0].controller_type;
813
814 const QString theme = [] {
815 if (QIcon::themeName().contains(QStringLiteral("dark")) ||
816 QIcon::themeName().contains(QStringLiteral("midnight"))) {
817 return QStringLiteral("_dark");
818 } else {
819 return QString{};
820 }
821 }();
822
823 switch (controller_type) {
824 case Settings::ControllerType::ProController:
825 case Settings::ControllerType::GameCube:
826 ui->icon_controller->setStyleSheet(
827 QStringLiteral("image: url(:/overlay/controller_pro%1.png);").arg(theme));
828 ui->icon_controller_shift->setStyleSheet(
829 QStringLiteral("image: url(:/overlay/controller_pro%1.png);").arg(theme));
830 ui->icon_controller_num->setStyleSheet(
831 QStringLiteral("image: url(:/overlay/controller_pro%1.png);").arg(theme));
832 break;
833 case Settings::ControllerType::DualJoyconDetached:
834 ui->icon_controller->setStyleSheet(
835 QStringLiteral("image: url(:/overlay/controller_dual_joycon%1.png);").arg(theme));
836 ui->icon_controller_shift->setStyleSheet(
837 QStringLiteral("image: url(:/overlay/controller_dual_joycon%1.png);").arg(theme));
838 ui->icon_controller_num->setStyleSheet(
839 QStringLiteral("image: url(:/overlay/controller_dual_joycon%1.png);").arg(theme));
840 break;
841 case Settings::ControllerType::LeftJoycon:
842 ui->icon_controller->setStyleSheet(
843 QStringLiteral("image: url(:/overlay/controller_single_joycon_left%1.png);")
844 .arg(theme));
845 ui->icon_controller_shift->setStyleSheet(
846 QStringLiteral("image: url(:/overlay/controller_single_joycon_left%1.png);")
847 .arg(theme));
848 ui->icon_controller_num->setStyleSheet(
849 QStringLiteral("image: url(:/overlay/controller_single_joycon_left%1.png);")
850 .arg(theme));
851 break;
852 case Settings::ControllerType::RightJoycon:
853 ui->icon_controller->setStyleSheet(
854 QStringLiteral("image: url(:/overlay/controller_single_joycon_right%1.png);")
855 .arg(theme));
856 ui->icon_controller_shift->setStyleSheet(
857 QStringLiteral("image: url(:/overlay/controller_single_joycon_right%1.png);")
858 .arg(theme));
859 ui->icon_controller_num->setStyleSheet(
860 QStringLiteral("image: url(:/overlay/controller_single_joycon_right%1.png);")
861 .arg(theme));
862 break;
863 case Settings::ControllerType::Handheld:
864 ui->icon_controller->setStyleSheet(
865 QStringLiteral("image: url(:/overlay/controller_handheld%1.png);").arg(theme));
866 ui->icon_controller_shift->setStyleSheet(
867 QStringLiteral("image: url(:/overlay/controller_handheld%1.png);").arg(theme));
868 ui->icon_controller_num->setStyleSheet(
869 QStringLiteral("image: url(:/overlay/controller_handheld%1.png);").arg(theme));
870 break;
871 default:
872 break;
873 }
874}
875
876void QtSoftwareKeyboardDialog::DisableKeyboardButtons() {
877 switch (bottom_osk_index) {
878 case BottomOSKIndex::LowerCase:
879 case BottomOSKIndex::UpperCase:
880 default: {
881 for (const auto& keys : keyboard_buttons) {
882 for (const auto& rows : keys) {
883 for (auto* button : rows) {
884 if (!button) {
885 continue;
886 }
887
888 button->setEnabled(true);
889 }
890 }
891 }
892
893 const auto& key_disable_flags = initialize_parameters.key_disable_flags;
894
895 ui->button_space->setDisabled(key_disable_flags.space);
896 ui->button_space_shift->setDisabled(key_disable_flags.space);
897
898 ui->button_at->setDisabled(key_disable_flags.at || key_disable_flags.username);
899
900 ui->button_percent->setDisabled(key_disable_flags.percent || key_disable_flags.username);
901
902 ui->button_slash->setDisabled(key_disable_flags.slash);
903
904 ui->button_1->setDisabled(key_disable_flags.numbers);
905 ui->button_2->setDisabled(key_disable_flags.numbers);
906 ui->button_3->setDisabled(key_disable_flags.numbers);
907 ui->button_4->setDisabled(key_disable_flags.numbers);
908 ui->button_5->setDisabled(key_disable_flags.numbers);
909 ui->button_6->setDisabled(key_disable_flags.numbers);
910 ui->button_7->setDisabled(key_disable_flags.numbers);
911 ui->button_8->setDisabled(key_disable_flags.numbers);
912 ui->button_9->setDisabled(key_disable_flags.numbers);
913 ui->button_0->setDisabled(key_disable_flags.numbers);
914
915 ui->button_return->setEnabled(initialize_parameters.enable_return_button);
916 ui->button_return_shift->setEnabled(initialize_parameters.enable_return_button);
917 break;
918 }
919 case BottomOSKIndex::NumberPad: {
920 for (const auto& rows : numberpad_buttons) {
921 for (auto* button : rows) {
922 if (!button) {
923 continue;
924 }
925
926 button->setEnabled(true);
927 }
928 }
929 break;
930 }
931 }
932}
933
934void QtSoftwareKeyboardDialog::SetBackspaceOkEnabled() {
935 if (is_inline) {
936 ui->button_ok->setEnabled(current_text.size() >= initialize_parameters.min_text_length);
937 ui->button_ok_shift->setEnabled(current_text.size() >=
938 initialize_parameters.min_text_length);
939 ui->button_ok_num->setEnabled(current_text.size() >= initialize_parameters.min_text_length);
940
941 ui->button_backspace->setEnabled(initialize_parameters.enable_backspace_button &&
942 cursor_position > 0);
943 ui->button_backspace_shift->setEnabled(initialize_parameters.enable_backspace_button &&
944 cursor_position > 0);
945 ui->button_backspace_num->setEnabled(initialize_parameters.enable_backspace_button &&
946 cursor_position > 0);
88 } else { 947 } else {
89 buttons->addButton(QString::fromStdU16String(parameters.submit_text), 948 const auto text_length = [this] {
90 QDialogButtonBox::AcceptRole); 949 if (ui->topOSK->currentIndex() == 1) {
950 return static_cast<u32>(ui->text_edit_osk->toPlainText().length());
951 } else {
952 return static_cast<u32>(ui->line_edit_osk->text().length());
953 }
954 }();
955
956 const auto normal_cursor_position = [this] {
957 if (ui->topOSK->currentIndex() == 1) {
958 return ui->text_edit_osk->textCursor().position();
959 } else {
960 return ui->line_edit_osk->cursorPosition();
961 }
962 }();
963
964 ui->button_ok->setEnabled(text_length >= initialize_parameters.min_text_length);
965 ui->button_ok_shift->setEnabled(text_length >= initialize_parameters.min_text_length);
966 ui->button_ok_num->setEnabled(text_length >= initialize_parameters.min_text_length);
967
968 ui->button_backspace->setEnabled(initialize_parameters.enable_backspace_button &&
969 normal_cursor_position > 0);
970 ui->button_backspace_shift->setEnabled(initialize_parameters.enable_backspace_button &&
971 normal_cursor_position > 0);
972 ui->button_backspace_num->setEnabled(initialize_parameters.enable_backspace_button &&
973 normal_cursor_position > 0);
91 } 974 }
92 connect(buttons, &QDialogButtonBox::accepted, this, &QtSoftwareKeyboardDialog::accept);
93 connect(buttons, &QDialogButtonBox::rejected, this, &QtSoftwareKeyboardDialog::reject);
94 layout->addWidget(header_label);
95 layout->addWidget(sub_label);
96 layout->addWidget(guide_label);
97 layout->addWidget(length_label);
98 layout->addWidget(line_edit);
99 layout->addWidget(buttons);
100 setLayout(layout);
101 setWindowTitle(tr("Software Keyboard"));
102} 975}
103 976
104QtSoftwareKeyboardDialog::~QtSoftwareKeyboardDialog() = default; 977bool QtSoftwareKeyboardDialog::ValidateInputText(const QString& input_text) {
978 const auto& key_disable_flags = initialize_parameters.key_disable_flags;
979
980 const auto input_text_length = static_cast<u32>(input_text.length());
981
982 if (input_text_length < initialize_parameters.min_text_length ||
983 input_text_length > initialize_parameters.max_text_length) {
984 return false;
985 }
986
987 if (key_disable_flags.space && input_text.contains(QLatin1Char{' '})) {
988 return false;
989 }
990
991 if ((key_disable_flags.at || key_disable_flags.username) &&
992 input_text.contains(QLatin1Char{'@'})) {
993 return false;
994 }
995
996 if ((key_disable_flags.percent || key_disable_flags.username) &&
997 input_text.contains(QLatin1Char{'%'})) {
998 return false;
999 }
1000
1001 if (key_disable_flags.slash && input_text.contains(QLatin1Char{'/'})) {
1002 return false;
1003 }
1004
1005 if ((key_disable_flags.backslash || key_disable_flags.username) &&
1006 input_text.contains(QLatin1Char('\\'))) {
1007 return false;
1008 }
105 1009
106void QtSoftwareKeyboardDialog::accept() { 1010 if (key_disable_flags.numbers &&
107 text = line_edit->text().toStdU16String(); 1011 std::any_of(input_text.begin(), input_text.end(), [](QChar c) { return c.isDigit(); })) {
108 QDialog::accept(); 1012 return false;
1013 }
1014
1015 if (bottom_osk_index == BottomOSKIndex::NumberPad &&
1016 std::any_of(input_text.begin(), input_text.end(), [](QChar c) { return !c.isDigit(); })) {
1017 return false;
1018 }
1019
1020 return true;
109} 1021}
110 1022
111void QtSoftwareKeyboardDialog::reject() { 1023void QtSoftwareKeyboardDialog::ChangeBottomOSKIndex() {
112 text.clear(); 1024 switch (bottom_osk_index) {
113 QDialog::reject(); 1025 case BottomOSKIndex::LowerCase:
1026 bottom_osk_index = BottomOSKIndex::UpperCase;
1027 ui->bottomOSK->setCurrentIndex(static_cast<int>(bottom_osk_index));
1028
1029 ui->button_shift_shift->setStyleSheet(
1030 QStringLiteral("background-image: url(:/overlay/osk_button_shift_lock_off.png);"
1031 "\nbackground-position: left top;"
1032 "\nbackground-repeat: no-repeat;"
1033 "\nbackground-origin: content;"));
1034
1035 ui->button_shift_shift->setIconSize(ui->button_shift->iconSize());
1036 ui->button_backspace_shift->setIconSize(ui->button_backspace->iconSize());
1037 break;
1038 case BottomOSKIndex::UpperCase:
1039 if (caps_lock_enabled) {
1040 caps_lock_enabled = false;
1041
1042 ui->button_shift_shift->setStyleSheet(
1043 QStringLiteral("background-image: url(:/overlay/osk_button_shift_lock_off.png);"
1044 "\nbackground-position: left top;"
1045 "\nbackground-repeat: no-repeat;"
1046 "\nbackground-origin: content;"));
1047
1048 ui->button_shift_shift->setIconSize(ui->button_shift->iconSize());
1049 ui->button_backspace_shift->setIconSize(ui->button_backspace->iconSize());
1050
1051 ui->label_shift_shift->setText(QStringLiteral("Caps Lock"));
1052
1053 bottom_osk_index = BottomOSKIndex::LowerCase;
1054 ui->bottomOSK->setCurrentIndex(static_cast<int>(bottom_osk_index));
1055 } else {
1056 caps_lock_enabled = true;
1057
1058 ui->button_shift_shift->setStyleSheet(
1059 QStringLiteral("background-image: url(:/overlay/osk_button_shift_lock_on.png);"
1060 "\nbackground-position: left top;"
1061 "\nbackground-repeat: no-repeat;"
1062 "\nbackground-origin: content;"));
1063
1064 ui->button_shift_shift->setIconSize(ui->button_shift->iconSize());
1065 ui->button_backspace_shift->setIconSize(ui->button_backspace->iconSize());
1066
1067 ui->label_shift_shift->setText(QStringLiteral("Caps Lock Off"));
1068 }
1069 break;
1070 case BottomOSKIndex::NumberPad:
1071 default:
1072 break;
1073 }
114} 1074}
115 1075
116std::u16string QtSoftwareKeyboardDialog::GetText() const { 1076void QtSoftwareKeyboardDialog::NormalKeyboardButtonClicked(QPushButton* button) {
117 return text; 1077 if (button == ui->button_ampersand) {
1078 if (ui->topOSK->currentIndex() == 1) {
1079 ui->text_edit_osk->insertPlainText(QStringLiteral("&"));
1080 } else {
1081 ui->line_edit_osk->insert(QStringLiteral("&"));
1082 }
1083 return;
1084 }
1085
1086 if (button == ui->button_return || button == ui->button_return_shift) {
1087 if (ui->topOSK->currentIndex() == 1) {
1088 ui->text_edit_osk->insertPlainText(QStringLiteral("\n"));
1089 } else {
1090 ui->line_edit_osk->insert(QStringLiteral("\n"));
1091 }
1092 return;
1093 }
1094
1095 if (button == ui->button_space || button == ui->button_space_shift) {
1096 if (ui->topOSK->currentIndex() == 1) {
1097 ui->text_edit_osk->insertPlainText(QStringLiteral(" "));
1098 } else {
1099 ui->line_edit_osk->insert(QStringLiteral(" "));
1100 }
1101 return;
1102 }
1103
1104 if (button == ui->button_shift || button == ui->button_shift_shift) {
1105 ChangeBottomOSKIndex();
1106 return;
1107 }
1108
1109 if (button == ui->button_backspace || button == ui->button_backspace_shift ||
1110 button == ui->button_backspace_num) {
1111 if (ui->topOSK->currentIndex() == 1) {
1112 auto text_cursor = ui->text_edit_osk->textCursor();
1113 ui->text_edit_osk->setTextCursor(text_cursor);
1114 text_cursor.deletePreviousChar();
1115 } else {
1116 ui->line_edit_osk->backspace();
1117 }
1118 return;
1119 }
1120
1121 if (button == ui->button_ok || button == ui->button_ok_shift || button == ui->button_ok_num) {
1122 if (ui->topOSK->currentIndex() == 1) {
1123 emit SubmitNormalText(SwkbdResult::Ok,
1124 ui->text_edit_osk->toPlainText().toStdU16String());
1125 } else {
1126 emit SubmitNormalText(SwkbdResult::Ok, ui->line_edit_osk->text().toStdU16String());
1127 }
1128 return;
1129 }
1130
1131 if (ui->topOSK->currentIndex() == 1) {
1132 ui->text_edit_osk->insertPlainText(button->text());
1133 } else {
1134 ui->line_edit_osk->insert(button->text());
1135 }
1136
1137 // Revert the keyboard to lowercase if the shift key is active.
1138 if (bottom_osk_index == BottomOSKIndex::UpperCase && !caps_lock_enabled) {
1139 // This is set to true since ChangeBottomOSKIndex will change bottom_osk_index to LowerCase
1140 // if bottom_osk_index is UpperCase and caps_lock_enabled is true.
1141 caps_lock_enabled = true;
1142 ChangeBottomOSKIndex();
1143 }
1144}
1145
1146void QtSoftwareKeyboardDialog::InlineKeyboardButtonClicked(QPushButton* button) {
1147 if (!button->isEnabled()) {
1148 return;
1149 }
1150
1151 if (button == ui->button_ampersand) {
1152 InlineTextInsertString(u"&");
1153 return;
1154 }
1155
1156 if (button == ui->button_return || button == ui->button_return_shift) {
1157 InlineTextInsertString(u"\n");
1158 return;
1159 }
1160
1161 if (button == ui->button_space || button == ui->button_space_shift) {
1162 InlineTextInsertString(u" ");
1163 return;
1164 }
1165
1166 if (button == ui->button_shift || button == ui->button_shift_shift) {
1167 ChangeBottomOSKIndex();
1168 return;
1169 }
1170
1171 if (button == ui->button_backspace || button == ui->button_backspace_shift ||
1172 button == ui->button_backspace_num) {
1173 if (cursor_position <= 0 || current_text.empty()) {
1174 cursor_position = 0;
1175 return;
1176 }
1177
1178 --cursor_position;
1179
1180 current_text.erase(cursor_position, 1);
1181
1182 SetBackspaceOkEnabled();
1183
1184 emit SubmitInlineText(SwkbdReplyType::ChangedString, current_text, cursor_position);
1185 return;
1186 }
1187
1188 if (button == ui->button_ok || button == ui->button_ok_shift || button == ui->button_ok_num) {
1189 emit SubmitInlineText(SwkbdReplyType::DecidedEnter, current_text, cursor_position);
1190 return;
1191 }
1192
1193 InlineTextInsertString(button->text().toStdU16String());
1194
1195 // Revert the keyboard to lowercase if the shift key is active.
1196 if (bottom_osk_index == BottomOSKIndex::UpperCase && !caps_lock_enabled) {
1197 // This is set to true since ChangeBottomOSKIndex will change bottom_osk_index to LowerCase
1198 // if bottom_osk_index is UpperCase and caps_lock_enabled is true.
1199 caps_lock_enabled = true;
1200 ChangeBottomOSKIndex();
1201 }
1202}
1203
1204void QtSoftwareKeyboardDialog::InlineTextInsertString(std::u16string_view string) {
1205 if ((current_text.size() + string.size()) > initialize_parameters.max_text_length) {
1206 return;
1207 }
1208
1209 current_text.insert(cursor_position, string);
1210
1211 cursor_position += static_cast<s32>(string.size());
1212
1213 SetBackspaceOkEnabled();
1214
1215 emit SubmitInlineText(SwkbdReplyType::ChangedString, current_text, cursor_position);
1216}
1217
1218void QtSoftwareKeyboardDialog::SetupMouseHover() {
1219 // setFocus() has a bug where continuously changing focus will cause the focus UI to
1220 // mysteriously disappear. A workaround we have found is using the mouse to hover over
1221 // the buttons to act in place of the button focus. As a result, we will have to set
1222 // a blank cursor when hovering over all the buttons and set a no focus policy so the
1223 // buttons do not stay in focus in addition to the mouse hover.
1224 for (auto* button : all_buttons) {
1225 button->setCursor(QCursor(Qt::BlankCursor));
1226 button->setFocusPolicy(Qt::NoFocus);
1227 }
1228}
1229
1230template <HIDButton... T>
1231void QtSoftwareKeyboardDialog::HandleButtonPressedOnce() {
1232 const auto f = [this](HIDButton button) {
1233 if (input_interpreter->IsButtonPressedOnce(button)) {
1234 TranslateButtonPress(button);
1235 }
1236 };
1237
1238 (f(T), ...);
1239}
1240
1241template <HIDButton... T>
1242void QtSoftwareKeyboardDialog::HandleButtonHold() {
1243 const auto f = [this](HIDButton button) {
1244 if (input_interpreter->IsButtonHeld(button)) {
1245 TranslateButtonPress(button);
1246 }
1247 };
1248
1249 (f(T), ...);
1250}
1251
1252void QtSoftwareKeyboardDialog::TranslateButtonPress(HIDButton button) {
1253 switch (button) {
1254 case HIDButton::A:
1255 switch (bottom_osk_index) {
1256 case BottomOSKIndex::LowerCase:
1257 case BottomOSKIndex::UpperCase:
1258 keyboard_buttons[static_cast<std::size_t>(bottom_osk_index)][row][column]->click();
1259 break;
1260 case BottomOSKIndex::NumberPad:
1261 numberpad_buttons[row][column]->click();
1262 break;
1263 default:
1264 break;
1265 }
1266 break;
1267 case HIDButton::B:
1268 switch (bottom_osk_index) {
1269 case BottomOSKIndex::LowerCase:
1270 ui->button_backspace->click();
1271 break;
1272 case BottomOSKIndex::UpperCase:
1273 ui->button_backspace_shift->click();
1274 break;
1275 case BottomOSKIndex::NumberPad:
1276 ui->button_backspace_num->click();
1277 break;
1278 default:
1279 break;
1280 }
1281 break;
1282 case HIDButton::X:
1283 if (is_inline) {
1284 emit SubmitInlineText(SwkbdReplyType::DecidedCancel, current_text, cursor_position);
1285 } else {
1286 if (ui->topOSK->currentIndex() == 1) {
1287 emit SubmitNormalText(SwkbdResult::Cancel,
1288 ui->text_edit_osk->toPlainText().toStdU16String());
1289 } else {
1290 emit SubmitNormalText(SwkbdResult::Cancel,
1291 ui->line_edit_osk->text().toStdU16String());
1292 }
1293 }
1294 break;
1295 case HIDButton::Y:
1296 switch (bottom_osk_index) {
1297 case BottomOSKIndex::LowerCase:
1298 ui->button_space->click();
1299 break;
1300 case BottomOSKIndex::UpperCase:
1301 ui->button_space_shift->click();
1302 break;
1303 case BottomOSKIndex::NumberPad:
1304 default:
1305 break;
1306 }
1307 break;
1308 case HIDButton::LStick:
1309 case HIDButton::RStick:
1310 switch (bottom_osk_index) {
1311 case BottomOSKIndex::LowerCase:
1312 ui->button_shift->click();
1313 break;
1314 case BottomOSKIndex::UpperCase:
1315 ui->button_shift_shift->click();
1316 break;
1317 case BottomOSKIndex::NumberPad:
1318 default:
1319 break;
1320 }
1321 break;
1322 case HIDButton::L:
1323 MoveTextCursorDirection(Direction::Left);
1324 break;
1325 case HIDButton::R:
1326 MoveTextCursorDirection(Direction::Right);
1327 break;
1328 case HIDButton::Plus:
1329 switch (bottom_osk_index) {
1330 case BottomOSKIndex::LowerCase:
1331 ui->button_ok->click();
1332 break;
1333 case BottomOSKIndex::UpperCase:
1334 ui->button_ok_shift->click();
1335 break;
1336 case BottomOSKIndex::NumberPad:
1337 ui->button_ok_num->click();
1338 break;
1339 default:
1340 break;
1341 }
1342 break;
1343 case HIDButton::DLeft:
1344 case HIDButton::LStickLeft:
1345 case HIDButton::RStickLeft:
1346 MoveButtonDirection(Direction::Left);
1347 break;
1348 case HIDButton::DUp:
1349 case HIDButton::LStickUp:
1350 case HIDButton::RStickUp:
1351 MoveButtonDirection(Direction::Up);
1352 break;
1353 case HIDButton::DRight:
1354 case HIDButton::LStickRight:
1355 case HIDButton::RStickRight:
1356 MoveButtonDirection(Direction::Right);
1357 break;
1358 case HIDButton::DDown:
1359 case HIDButton::LStickDown:
1360 case HIDButton::RStickDown:
1361 MoveButtonDirection(Direction::Down);
1362 break;
1363 default:
1364 break;
1365 }
1366}
1367
1368void QtSoftwareKeyboardDialog::MoveButtonDirection(Direction direction) {
1369 // Changes the row or column index depending on the direction.
1370 auto move_direction = [this, direction](std::size_t max_rows, std::size_t max_columns) {
1371 switch (direction) {
1372 case Direction::Left:
1373 column = (column + max_columns - 1) % max_columns;
1374 break;
1375 case Direction::Up:
1376 row = (row + max_rows - 1) % max_rows;
1377 break;
1378 case Direction::Right:
1379 column = (column + 1) % max_columns;
1380 break;
1381 case Direction::Down:
1382 row = (row + 1) % max_rows;
1383 break;
1384 default:
1385 break;
1386 }
1387 };
1388
1389 switch (bottom_osk_index) {
1390 case BottomOSKIndex::LowerCase:
1391 case BottomOSKIndex::UpperCase: {
1392 const auto index = static_cast<std::size_t>(bottom_osk_index);
1393
1394 const auto* const prev_button = keyboard_buttons[index][row][column];
1395 move_direction(NUM_ROWS_NORMAL, NUM_COLUMNS_NORMAL);
1396 auto* curr_button = keyboard_buttons[index][row][column];
1397
1398 while (!curr_button || !curr_button->isEnabled() || curr_button == prev_button) {
1399 move_direction(NUM_ROWS_NORMAL, NUM_COLUMNS_NORMAL);
1400 curr_button = keyboard_buttons[index][row][column];
1401 }
1402
1403 // This is a workaround for setFocus() randomly not showing focus in the UI
1404 QCursor::setPos(curr_button->mapToGlobal(curr_button->rect().center()));
1405 break;
1406 }
1407 case BottomOSKIndex::NumberPad: {
1408 const auto* const prev_button = numberpad_buttons[row][column];
1409 move_direction(NUM_ROWS_NUMPAD, NUM_COLUMNS_NUMPAD);
1410 auto* curr_button = numberpad_buttons[row][column];
1411
1412 while (!curr_button || !curr_button->isEnabled() || curr_button == prev_button) {
1413 move_direction(NUM_ROWS_NUMPAD, NUM_COLUMNS_NUMPAD);
1414 curr_button = numberpad_buttons[row][column];
1415 }
1416
1417 // This is a workaround for setFocus() randomly not showing focus in the UI
1418 QCursor::setPos(curr_button->mapToGlobal(curr_button->rect().center()));
1419 break;
1420 }
1421 default:
1422 break;
1423 }
1424}
1425
1426void QtSoftwareKeyboardDialog::MoveTextCursorDirection(Direction direction) {
1427 switch (direction) {
1428 case Direction::Left:
1429 if (is_inline) {
1430 if (cursor_position <= 0) {
1431 cursor_position = 0;
1432 } else {
1433 --cursor_position;
1434 emit SubmitInlineText(SwkbdReplyType::MovedCursor, current_text, cursor_position);
1435 }
1436 } else {
1437 if (ui->topOSK->currentIndex() == 1) {
1438 ui->text_edit_osk->moveCursor(QTextCursor::Left);
1439 } else {
1440 ui->line_edit_osk->setCursorPosition(ui->line_edit_osk->cursorPosition() - 1);
1441 }
1442 }
1443 break;
1444 case Direction::Right:
1445 if (is_inline) {
1446 if (cursor_position >= static_cast<s32>(current_text.size())) {
1447 cursor_position = static_cast<s32>(current_text.size());
1448 } else {
1449 ++cursor_position;
1450 emit SubmitInlineText(SwkbdReplyType::MovedCursor, current_text, cursor_position);
1451 }
1452 } else {
1453 if (ui->topOSK->currentIndex() == 1) {
1454 ui->text_edit_osk->moveCursor(QTextCursor::Right);
1455 } else {
1456 ui->line_edit_osk->setCursorPosition(ui->line_edit_osk->cursorPosition() + 1);
1457 }
1458 }
1459 break;
1460 default:
1461 break;
1462 }
1463}
1464
1465void QtSoftwareKeyboardDialog::StartInputThread() {
1466 if (input_thread_running) {
1467 return;
1468 }
1469
1470 input_thread_running = true;
1471
1472 input_thread = std::thread(&QtSoftwareKeyboardDialog::InputThread, this);
1473}
1474
1475void QtSoftwareKeyboardDialog::StopInputThread() {
1476 input_thread_running = false;
1477
1478 if (input_thread.joinable()) {
1479 input_thread.join();
1480 }
1481
1482 if (input_interpreter) {
1483 input_interpreter->ResetButtonStates();
1484 }
1485}
1486
1487void QtSoftwareKeyboardDialog::InputThread() {
1488 while (input_thread_running) {
1489 input_interpreter->PollInput();
1490
1491 HandleButtonPressedOnce<HIDButton::A, HIDButton::B, HIDButton::X, HIDButton::Y,
1492 HIDButton::LStick, HIDButton::RStick, HIDButton::L, HIDButton::R,
1493 HIDButton::Plus, HIDButton::DLeft, HIDButton::DUp,
1494 HIDButton::DRight, HIDButton::DDown, HIDButton::LStickLeft,
1495 HIDButton::LStickUp, HIDButton::LStickRight, HIDButton::LStickDown,
1496 HIDButton::RStickLeft, HIDButton::RStickUp, HIDButton::RStickRight,
1497 HIDButton::RStickDown>();
1498
1499 HandleButtonHold<HIDButton::B, HIDButton::L, HIDButton::R, HIDButton::DLeft, HIDButton::DUp,
1500 HIDButton::DRight, HIDButton::DDown, HIDButton::LStickLeft,
1501 HIDButton::LStickUp, HIDButton::LStickRight, HIDButton::LStickDown,
1502 HIDButton::RStickLeft, HIDButton::RStickUp, HIDButton::RStickRight,
1503 HIDButton::RStickDown>();
1504
1505 std::this_thread::sleep_for(std::chrono::milliseconds(50));
1506 }
118} 1507}
119 1508
120QtSoftwareKeyboard::QtSoftwareKeyboard(GMainWindow& main_window) { 1509QtSoftwareKeyboard::QtSoftwareKeyboard(GMainWindow& main_window) {
121 connect(this, &QtSoftwareKeyboard::MainWindowGetText, &main_window, 1510 connect(this, &QtSoftwareKeyboard::MainWindowInitializeKeyboard, &main_window,
122 &GMainWindow::SoftwareKeyboardGetText, Qt::QueuedConnection); 1511 &GMainWindow::SoftwareKeyboardInitialize, Qt::QueuedConnection);
123 connect(this, &QtSoftwareKeyboard::MainWindowTextCheckDialog, &main_window, 1512 connect(this, &QtSoftwareKeyboard::MainWindowShowNormalKeyboard, &main_window,
124 &GMainWindow::SoftwareKeyboardInvokeCheckDialog, Qt::BlockingQueuedConnection); 1513 &GMainWindow::SoftwareKeyboardShowNormal, Qt::QueuedConnection);
125 connect(&main_window, &GMainWindow::SoftwareKeyboardFinishedText, this, 1514 connect(this, &QtSoftwareKeyboard::MainWindowShowTextCheckDialog, &main_window,
126 &QtSoftwareKeyboard::MainWindowFinishedText, Qt::QueuedConnection); 1515 &GMainWindow::SoftwareKeyboardShowTextCheck, Qt::QueuedConnection);
1516 connect(this, &QtSoftwareKeyboard::MainWindowShowInlineKeyboard, &main_window,
1517 &GMainWindow::SoftwareKeyboardShowInline, Qt::QueuedConnection);
1518 connect(this, &QtSoftwareKeyboard::MainWindowHideInlineKeyboard, &main_window,
1519 &GMainWindow::SoftwareKeyboardHideInline, Qt::QueuedConnection);
1520 connect(this, &QtSoftwareKeyboard::MainWindowInlineTextChanged, &main_window,
1521 &GMainWindow::SoftwareKeyboardInlineTextChanged, Qt::QueuedConnection);
1522 connect(this, &QtSoftwareKeyboard::MainWindowExitKeyboard, &main_window,
1523 &GMainWindow::SoftwareKeyboardExit, Qt::QueuedConnection);
1524 connect(&main_window, &GMainWindow::SoftwareKeyboardSubmitNormalText, this,
1525 &QtSoftwareKeyboard::SubmitNormalText, Qt::QueuedConnection);
1526 connect(&main_window, &GMainWindow::SoftwareKeyboardSubmitInlineText, this,
1527 &QtSoftwareKeyboard::SubmitInlineText, Qt::QueuedConnection);
127} 1528}
128 1529
129QtSoftwareKeyboard::~QtSoftwareKeyboard() = default; 1530QtSoftwareKeyboard::~QtSoftwareKeyboard() = default;
130 1531
131void QtSoftwareKeyboard::RequestText(std::function<void(std::optional<std::u16string>)> out, 1532void QtSoftwareKeyboard::InitializeKeyboard(
132 Core::Frontend::SoftwareKeyboardParameters parameters) const { 1533 bool is_inline, Core::Frontend::KeyboardInitializeParameters initialize_parameters,
133 text_output = std::move(out); 1534 std::function<void(Service::AM::Applets::SwkbdResult, std::u16string)> submit_normal_callback_,
134 emit MainWindowGetText(parameters); 1535 std::function<void(Service::AM::Applets::SwkbdReplyType, std::u16string, s32)>
1536 submit_inline_callback_) {
1537 if (is_inline) {
1538 submit_inline_callback = std::move(submit_inline_callback_);
1539 } else {
1540 submit_normal_callback = std::move(submit_normal_callback_);
1541 }
1542
1543 LOG_INFO(Service_AM,
1544 "\nKeyboardInitializeParameters:"
1545 "\nok_text={}"
1546 "\nheader_text={}"
1547 "\nsub_text={}"
1548 "\nguide_text={}"
1549 "\ninitial_text={}"
1550 "\nmax_text_length={}"
1551 "\nmin_text_length={}"
1552 "\ninitial_cursor_position={}"
1553 "\ntype={}"
1554 "\npassword_mode={}"
1555 "\ntext_draw_type={}"
1556 "\nkey_disable_flags={}"
1557 "\nuse_blur_background={}"
1558 "\nenable_backspace_button={}"
1559 "\nenable_return_button={}"
1560 "\ndisable_cancel_button={}",
1561 Common::UTF16ToUTF8(initialize_parameters.ok_text),
1562 Common::UTF16ToUTF8(initialize_parameters.header_text),
1563 Common::UTF16ToUTF8(initialize_parameters.sub_text),
1564 Common::UTF16ToUTF8(initialize_parameters.guide_text),
1565 Common::UTF16ToUTF8(initialize_parameters.initial_text),
1566 initialize_parameters.max_text_length, initialize_parameters.min_text_length,
1567 initialize_parameters.initial_cursor_position, initialize_parameters.type,
1568 initialize_parameters.password_mode, initialize_parameters.text_draw_type,
1569 initialize_parameters.key_disable_flags.raw, initialize_parameters.use_blur_background,
1570 initialize_parameters.enable_backspace_button,
1571 initialize_parameters.enable_return_button,
1572 initialize_parameters.disable_cancel_button);
1573
1574 emit MainWindowInitializeKeyboard(is_inline, std::move(initialize_parameters));
1575}
1576
1577void QtSoftwareKeyboard::ShowNormalKeyboard() const {
1578 emit MainWindowShowNormalKeyboard();
1579}
1580
1581void QtSoftwareKeyboard::ShowTextCheckDialog(
1582 Service::AM::Applets::SwkbdTextCheckResult text_check_result,
1583 std::u16string text_check_message) const {
1584 emit MainWindowShowTextCheckDialog(text_check_result, text_check_message);
1585}
1586
1587void QtSoftwareKeyboard::ShowInlineKeyboard(
1588 Core::Frontend::InlineAppearParameters appear_parameters) const {
1589 LOG_INFO(Service_AM,
1590 "\nInlineAppearParameters:"
1591 "\nmax_text_length={}"
1592 "\nmin_text_length={}"
1593 "\nkey_top_scale_x={}"
1594 "\nkey_top_scale_y={}"
1595 "\nkey_top_translate_x={}"
1596 "\nkey_top_translate_y={}"
1597 "\ntype={}"
1598 "\nkey_disable_flags={}"
1599 "\nkey_top_as_floating={}"
1600 "\nenable_backspace_button={}"
1601 "\nenable_return_button={}"
1602 "\ndisable_cancel_button={}",
1603 appear_parameters.max_text_length, appear_parameters.min_text_length,
1604 appear_parameters.key_top_scale_x, appear_parameters.key_top_scale_y,
1605 appear_parameters.key_top_translate_x, appear_parameters.key_top_translate_y,
1606 appear_parameters.type, appear_parameters.key_disable_flags.raw,
1607 appear_parameters.key_top_as_floating, appear_parameters.enable_backspace_button,
1608 appear_parameters.enable_return_button, appear_parameters.disable_cancel_button);
1609
1610 emit MainWindowShowInlineKeyboard(std::move(appear_parameters));
1611}
1612
1613void QtSoftwareKeyboard::HideInlineKeyboard() const {
1614 emit MainWindowHideInlineKeyboard();
1615}
1616
1617void QtSoftwareKeyboard::InlineTextChanged(
1618 Core::Frontend::InlineTextParameters text_parameters) const {
1619 LOG_INFO(Service_AM,
1620 "\nInlineTextParameters:"
1621 "\ninput_text={}"
1622 "\ncursor_position={}",
1623 Common::UTF16ToUTF8(text_parameters.input_text), text_parameters.cursor_position);
1624
1625 emit MainWindowInlineTextChanged(std::move(text_parameters));
135} 1626}
136 1627
137void QtSoftwareKeyboard::SendTextCheckDialog(std::u16string error_message, 1628void QtSoftwareKeyboard::ExitKeyboard() const {
138 std::function<void()> finished_check_) const { 1629 emit MainWindowExitKeyboard();
139 finished_check = std::move(finished_check_);
140 emit MainWindowTextCheckDialog(error_message);
141} 1630}
142 1631
143void QtSoftwareKeyboard::MainWindowFinishedText(std::optional<std::u16string> text) { 1632void QtSoftwareKeyboard::SubmitNormalText(Service::AM::Applets::SwkbdResult result,
144 // Acquire the HLE mutex 1633 std::u16string submitted_text) const {
145 std::lock_guard lock{HLE::g_hle_lock}; 1634 submit_normal_callback(result, submitted_text);
146 text_output(std::move(text));
147} 1635}
148 1636
149void QtSoftwareKeyboard::MainWindowFinishedCheckDialog() { 1637void QtSoftwareKeyboard::SubmitInlineText(Service::AM::Applets::SwkbdReplyType reply_type,
150 // Acquire the HLE mutex 1638 std::u16string submitted_text,
151 std::lock_guard lock{HLE::g_hle_lock}; 1639 s32 cursor_position) const {
152 finished_check(); 1640 submit_inline_callback(reply_type, submitted_text, cursor_position);
153} 1641}
diff --git a/src/yuzu/applets/software_keyboard.h b/src/yuzu/applets/software_keyboard.h
index 9e1094cce..1a03c098c 100644
--- a/src/yuzu/applets/software_keyboard.h
+++ b/src/yuzu/applets/software_keyboard.h
@@ -1,54 +1,228 @@
1// Copyright 2018 yuzu Emulator Project 1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
8#include <atomic>
9#include <memory>
10#include <thread>
11
7#include <QDialog> 12#include <QDialog>
8#include <QValidator> 13#include <QValidator>
14
9#include "core/frontend/applets/software_keyboard.h" 15#include "core/frontend/applets/software_keyboard.h"
10 16
11class GMainWindow; 17enum class HIDButton : u8;
12class QDialogButtonBox;
13class QLabel;
14class QLineEdit;
15class QVBoxLayout;
16class QtSoftwareKeyboard;
17 18
18class QtSoftwareKeyboardValidator final : public QValidator { 19class InputInterpreter;
19public:
20 explicit QtSoftwareKeyboardValidator(Core::Frontend::SoftwareKeyboardParameters parameters);
21 State validate(QString& input, int& pos) const override;
22 20
23private: 21namespace Core {
24 Core::Frontend::SoftwareKeyboardParameters parameters; 22class System;
25}; 23}
24
25namespace Ui {
26class QtSoftwareKeyboardDialog;
27}
28
29class GMainWindow;
26 30
27class QtSoftwareKeyboardDialog final : public QDialog { 31class QtSoftwareKeyboardDialog final : public QDialog {
28 Q_OBJECT 32 Q_OBJECT
29 33
30public: 34public:
31 QtSoftwareKeyboardDialog(QWidget* parent, 35 QtSoftwareKeyboardDialog(QWidget* parent, Core::System& system_, bool is_inline_,
32 Core::Frontend::SoftwareKeyboardParameters parameters); 36 Core::Frontend::KeyboardInitializeParameters initialize_parameters_);
33 ~QtSoftwareKeyboardDialog() override; 37 ~QtSoftwareKeyboardDialog() override;
34 38
35 void accept() override; 39 void ShowNormalKeyboard(QPoint pos, QSize size);
40
41 void ShowTextCheckDialog(Service::AM::Applets::SwkbdTextCheckResult text_check_result,
42 std::u16string text_check_message);
43
44 void ShowInlineKeyboard(Core::Frontend::InlineAppearParameters appear_parameters, QPoint pos,
45 QSize size);
46
47 void HideInlineKeyboard();
48
49 void InlineTextChanged(Core::Frontend::InlineTextParameters text_parameters);
50
51 void ExitKeyboard();
52
53signals:
54 void SubmitNormalText(Service::AM::Applets::SwkbdResult result,
55 std::u16string submitted_text) const;
56
57 void SubmitInlineText(Service::AM::Applets::SwkbdReplyType reply_type,
58 std::u16string submitted_text, s32 cursor_position) const;
59
60public slots:
61 void open() override;
36 void reject() override; 62 void reject() override;
37 63
38 std::u16string GetText() const; 64protected:
65 /// We override the keyPressEvent for inputting text into the inline software keyboard.
66 void keyPressEvent(QKeyEvent* event) override;
39 67
40private: 68private:
41 std::u16string text; 69 enum class Direction {
70 Left,
71 Up,
72 Right,
73 Down,
74 };
75
76 enum class BottomOSKIndex {
77 LowerCase,
78 UpperCase,
79 NumberPad,
80 };
81
82 /**
83 * Moves and resizes the window to a specified position and size.
84 *
85 * @param pos Top-left window position
86 * @param size Window size
87 */
88 void MoveAndResizeWindow(QPoint pos, QSize size);
89
90 /**
91 * Rescales all keyboard elements to account for High DPI displays.
92 *
93 * @param width Window width
94 * @param height Window height
95 * @param dpi_scale Display scaling factor
96 */
97 void RescaleKeyboardElements(float width, float height, float dpi_scale);
98
99 /// Sets the keyboard type based on initialize_parameters.
100 void SetKeyboardType();
101
102 /// Sets the password mode based on initialize_parameters.
103 void SetPasswordMode();
104
105 /// Sets the text draw type based on initialize_parameters.
106 void SetTextDrawType();
107
108 /// Sets the controller image at the bottom left of the software keyboard.
109 void SetControllerImage();
110
111 /// Disables buttons based on initialize_parameters.
112 void DisableKeyboardButtons();
113
114 /// Changes whether the backspace or/and ok buttons should be enabled or disabled.
115 void SetBackspaceOkEnabled();
116
117 /**
118 * Validates the input text sent in based on the parameters in initialize_parameters.
119 *
120 * @param input_text Input text
121 *
122 * @returns True if the input text is valid, false otherwise.
123 */
124 bool ValidateInputText(const QString& input_text);
125
126 /// Switches between LowerCase and UpperCase (Shift and Caps Lock)
127 void ChangeBottomOSKIndex();
128
129 /// Processes a keyboard button click from the UI as normal keyboard input.
130 void NormalKeyboardButtonClicked(QPushButton* button);
131
132 /// Processes a keyboard button click from the UI as inline keyboard input.
133 void InlineKeyboardButtonClicked(QPushButton* button);
134
135 /**
136 * Inserts a string of arbitrary length into the current_text at the current cursor position.
137 * This is only used for the inline software keyboard.
138 */
139 void InlineTextInsertString(std::u16string_view string);
42 140
43 QDialogButtonBox* buttons; 141 /// Setup the mouse hover workaround for "focusing" buttons. This should only be called once.
44 QLabel* header_label; 142 void SetupMouseHover();
45 QLabel* sub_label;
46 QLabel* guide_label;
47 QLabel* length_label;
48 QLineEdit* line_edit;
49 QVBoxLayout* layout;
50 143
51 Core::Frontend::SoftwareKeyboardParameters parameters; 144 /**
145 * Handles button presses and converts them into keyboard input.
146 *
147 * @tparam HIDButton The list of buttons that can be converted into keyboard input.
148 */
149 template <HIDButton... T>
150 void HandleButtonPressedOnce();
151
152 /**
153 * Handles button holds and converts them into keyboard input.
154 *
155 * @tparam HIDButton The list of buttons that can be converted into keyboard input.
156 */
157 template <HIDButton... T>
158 void HandleButtonHold();
159
160 /**
161 * Translates a button press to focus or click a keyboard button.
162 *
163 * @param button The button press to process.
164 */
165 void TranslateButtonPress(HIDButton button);
166
167 /**
168 * Moves the focus of a button in a certain direction.
169 *
170 * @param direction The direction to move.
171 */
172 void MoveButtonDirection(Direction direction);
173
174 /**
175 * Moves the text cursor in a certain direction.
176 *
177 * @param direction The direction to move.
178 */
179 void MoveTextCursorDirection(Direction direction);
180
181 void StartInputThread();
182 void StopInputThread();
183
184 /// The thread where input is being polled and processed.
185 void InputThread();
186
187 std::unique_ptr<Ui::QtSoftwareKeyboardDialog> ui;
188
189 Core::System& system;
190
191 // True if it is the inline software keyboard.
192 bool is_inline;
193
194 // Common software keyboard initialize parameters.
195 Core::Frontend::KeyboardInitializeParameters initialize_parameters;
196
197 // Used only by the inline software keyboard since the QLineEdit or QTextEdit is hidden.
198 std::u16string current_text;
199 s32 cursor_position{0};
200
201 static constexpr std::size_t NUM_ROWS_NORMAL = 5;
202 static constexpr std::size_t NUM_COLUMNS_NORMAL = 12;
203 static constexpr std::size_t NUM_ROWS_NUMPAD = 4;
204 static constexpr std::size_t NUM_COLUMNS_NUMPAD = 4;
205
206 // Stores the normal keyboard layout.
207 std::array<std::array<std::array<QPushButton*, NUM_COLUMNS_NORMAL>, NUM_ROWS_NORMAL>, 2>
208 keyboard_buttons;
209 // Stores the numberpad keyboard layout.
210 std::array<std::array<QPushButton*, NUM_COLUMNS_NUMPAD>, NUM_ROWS_NUMPAD> numberpad_buttons;
211
212 // Contains a set of all buttons used in keyboard_buttons and numberpad_buttons.
213 std::array<QPushButton*, 110> all_buttons;
214
215 std::size_t row{0};
216 std::size_t column{0};
217
218 BottomOSKIndex bottom_osk_index{BottomOSKIndex::LowerCase};
219 std::atomic<bool> caps_lock_enabled{false};
220
221 std::unique_ptr<InputInterpreter> input_interpreter;
222
223 std::thread input_thread;
224
225 std::atomic<bool> input_thread_running{};
52}; 226};
53 227
54class QtSoftwareKeyboard final : public QObject, public Core::Frontend::SoftwareKeyboardApplet { 228class QtSoftwareKeyboard final : public QObject, public Core::Frontend::SoftwareKeyboardApplet {
@@ -58,19 +232,54 @@ public:
58 explicit QtSoftwareKeyboard(GMainWindow& parent); 232 explicit QtSoftwareKeyboard(GMainWindow& parent);
59 ~QtSoftwareKeyboard() override; 233 ~QtSoftwareKeyboard() override;
60 234
61 void RequestText(std::function<void(std::optional<std::u16string>)> out, 235 void InitializeKeyboard(
62 Core::Frontend::SoftwareKeyboardParameters parameters) const override; 236 bool is_inline, Core::Frontend::KeyboardInitializeParameters initialize_parameters,
63 void SendTextCheckDialog(std::u16string error_message, 237 std::function<void(Service::AM::Applets::SwkbdResult, std::u16string)>
64 std::function<void()> finished_check_) const override; 238 submit_normal_callback_,
239 std::function<void(Service::AM::Applets::SwkbdReplyType, std::u16string, s32)>
240 submit_inline_callback_) override;
241
242 void ShowNormalKeyboard() const override;
243
244 void ShowTextCheckDialog(Service::AM::Applets::SwkbdTextCheckResult text_check_result,
245 std::u16string text_check_message) const override;
246
247 void ShowInlineKeyboard(
248 Core::Frontend::InlineAppearParameters appear_parameters) const override;
249
250 void HideInlineKeyboard() const override;
251
252 void InlineTextChanged(Core::Frontend::InlineTextParameters text_parameters) const override;
253
254 void ExitKeyboard() const override;
65 255
66signals: 256signals:
67 void MainWindowGetText(Core::Frontend::SoftwareKeyboardParameters parameters) const; 257 void MainWindowInitializeKeyboard(
68 void MainWindowTextCheckDialog(std::u16string error_message) const; 258 bool is_inline, Core::Frontend::KeyboardInitializeParameters initialize_parameters) const;
259
260 void MainWindowShowNormalKeyboard() const;
261
262 void MainWindowShowTextCheckDialog(Service::AM::Applets::SwkbdTextCheckResult text_check_result,
263 std::u16string text_check_message) const;
264
265 void MainWindowShowInlineKeyboard(
266 Core::Frontend::InlineAppearParameters appear_parameters) const;
267
268 void MainWindowHideInlineKeyboard() const;
269
270 void MainWindowInlineTextChanged(Core::Frontend::InlineTextParameters text_parameters) const;
271
272 void MainWindowExitKeyboard() const;
69 273
70private: 274private:
71 void MainWindowFinishedText(std::optional<std::u16string> text); 275 void SubmitNormalText(Service::AM::Applets::SwkbdResult result,
72 void MainWindowFinishedCheckDialog(); 276 std::u16string submitted_text) const;
277
278 void SubmitInlineText(Service::AM::Applets::SwkbdReplyType reply_type,
279 std::u16string submitted_text, s32 cursor_position) const;
73 280
74 mutable std::function<void(std::optional<std::u16string>)> text_output; 281 mutable std::function<void(Service::AM::Applets::SwkbdResult, std::u16string)>
75 mutable std::function<void()> finished_check; 282 submit_normal_callback;
283 mutable std::function<void(Service::AM::Applets::SwkbdReplyType, std::u16string, s32)>
284 submit_inline_callback;
76}; 285};
diff --git a/src/yuzu/applets/software_keyboard.ui b/src/yuzu/applets/software_keyboard.ui
new file mode 100644
index 000000000..b0a1fcde9
--- /dev/null
+++ b/src/yuzu/applets/software_keyboard.ui
@@ -0,0 +1,3503 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>QtSoftwareKeyboardDialog</class>
4 <widget class="QDialog" name="QtSoftwareKeyboardDialog">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>1280</width>
10 <height>720</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>Software Keyboard</string>
15 </property>
16 <property name="styleSheet">
17 <string notr="true"/>
18 </property>
19 <layout class="QVBoxLayout" name="verticalLayout">
20 <property name="spacing">
21 <number>0</number>
22 </property>
23 <property name="leftMargin">
24 <number>0</number>
25 </property>
26 <property name="topMargin">
27 <number>0</number>
28 </property>
29 <property name="rightMargin">
30 <number>0</number>
31 </property>
32 <property name="bottomMargin">
33 <number>0</number>
34 </property>
35 <item>
36 <widget class="QWidget" name="mainOSK" native="true">
37 <layout class="QVBoxLayout" name="verticalLayout_2" stretch="320,400">
38 <property name="spacing">
39 <number>0</number>
40 </property>
41 <property name="leftMargin">
42 <number>0</number>
43 </property>
44 <property name="topMargin">
45 <number>0</number>
46 </property>
47 <property name="rightMargin">
48 <number>0</number>
49 </property>
50 <property name="bottomMargin">
51 <number>0</number>
52 </property>
53 <item>
54 <widget class="QStackedWidget" name="topOSK">
55 <property name="currentIndex">
56 <number>0</number>
57 </property>
58 <widget class="QWidget" name="lineOSK">
59 <property name="minimumSize">
60 <size>
61 <width>0</width>
62 <height>100</height>
63 </size>
64 </property>
65 <layout class="QVBoxLayout" name="lineOSKVerticalLayout">
66 <property name="spacing">
67 <number>0</number>
68 </property>
69 <property name="leftMargin">
70 <number>0</number>
71 </property>
72 <property name="topMargin">
73 <number>0</number>
74 </property>
75 <property name="rightMargin">
76 <number>0</number>
77 </property>
78 <property name="bottomMargin">
79 <number>0</number>
80 </property>
81 <item>
82 <layout class="QGridLayout" name="gridLineOSK" rowstretch="40,50,23,48,65,94" columnstretch="130,1020,130">
83 <property name="topMargin">
84 <number>0</number>
85 </property>
86 <property name="spacing">
87 <number>0</number>
88 </property>
89 <item row="4" column="2">
90 <spacer name="horizontalSpacer_3">
91 <property name="orientation">
92 <enum>Qt::Horizontal</enum>
93 </property>
94 <property name="sizeHint" stdset="0">
95 <size>
96 <width>40</width>
97 <height>20</height>
98 </size>
99 </property>
100 </spacer>
101 </item>
102 <item row="4" column="0">
103 <spacer name="horizontalSpacer_4">
104 <property name="orientation">
105 <enum>Qt::Horizontal</enum>
106 </property>
107 <property name="sizeHint" stdset="0">
108 <size>
109 <width>40</width>
110 <height>20</height>
111 </size>
112 </property>
113 </spacer>
114 </item>
115 <item row="5" column="1">
116 <widget class="QWidget" name="charactersOSK" native="true">
117 <layout class="QVBoxLayout" name="verticalLayout_5">
118 <property name="spacing">
119 <number>0</number>
120 </property>
121 <property name="leftMargin">
122 <number>0</number>
123 </property>
124 <property name="topMargin">
125 <number>0</number>
126 </property>
127 <property name="rightMargin">
128 <number>0</number>
129 </property>
130 <property name="bottomMargin">
131 <number>0</number>
132 </property>
133 <item alignment="Qt::AlignRight|Qt::AlignTop">
134 <widget class="QLabel" name="label_characters">
135 <property name="font">
136 <font>
137 <pointsize>17</pointsize>
138 </font>
139 </property>
140 <property name="text">
141 <string notr="true">0/32</string>
142 </property>
143 <property name="alignment">
144 <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
145 </property>
146 </widget>
147 </item>
148 </layout>
149 </widget>
150 </item>
151 <item row="3" column="1">
152 <spacer name="verticalSpacer_2">
153 <property name="orientation">
154 <enum>Qt::Vertical</enum>
155 </property>
156 <property name="sizeHint" stdset="0">
157 <size>
158 <width>20</width>
159 <height>40</height>
160 </size>
161 </property>
162 </spacer>
163 </item>
164 <item row="5" column="0">
165 <spacer name="verticalSpacer_3">
166 <property name="orientation">
167 <enum>Qt::Vertical</enum>
168 </property>
169 <property name="sizeHint" stdset="0">
170 <size>
171 <width>20</width>
172 <height>40</height>
173 </size>
174 </property>
175 </spacer>
176 </item>
177 <item row="4" column="1">
178 <widget class="QWidget" name="inputOSK" native="true">
179 <layout class="QVBoxLayout" name="verticalLayout_3">
180 <item>
181 <widget class="QLineEdit" name="line_edit_osk">
182 <property name="font">
183 <font>
184 <pointsize>26</pointsize>
185 <weight>50</weight>
186 <bold>false</bold>
187 </font>
188 </property>
189 <property name="focusPolicy">
190 <enum>Qt::StrongFocus</enum>
191 </property>
192 <property name="text">
193 <string/>
194 </property>
195 <property name="maxLength">
196 <number>32</number>
197 </property>
198 <property name="placeholderText">
199 <string>Enter Text</string>
200 </property>
201 </widget>
202 </item>
203 </layout>
204 </widget>
205 </item>
206 <item row="0" column="1">
207 <spacer name="verticalSpacer_4">
208 <property name="orientation">
209 <enum>Qt::Vertical</enum>
210 </property>
211 <property name="sizeHint" stdset="0">
212 <size>
213 <width>20</width>
214 <height>40</height>
215 </size>
216 </property>
217 </spacer>
218 </item>
219 <item row="1" column="0" colspan="3">
220 <widget class="QWidget" name="headerOSK" native="true">
221 <layout class="QHBoxLayout" name="horizontalLayout_4" stretch="130,1020,130">
222 <property name="spacing">
223 <number>0</number>
224 </property>
225 <property name="leftMargin">
226 <number>0</number>
227 </property>
228 <property name="topMargin">
229 <number>0</number>
230 </property>
231 <property name="rightMargin">
232 <number>0</number>
233 </property>
234 <property name="bottomMargin">
235 <number>0</number>
236 </property>
237 <item>
238 <spacer name="horizontalSpacer_18">
239 <property name="orientation">
240 <enum>Qt::Horizontal</enum>
241 </property>
242 <property name="sizeHint" stdset="0">
243 <size>
244 <width>127</width>
245 <height>20</height>
246 </size>
247 </property>
248 </spacer>
249 </item>
250 <item>
251 <widget class="QLabel" name="label_header">
252 <property name="font">
253 <font>
254 <pointsize>23</pointsize>
255 </font>
256 </property>
257 <property name="text">
258 <string/>
259 </property>
260 </widget>
261 </item>
262 <item>
263 <spacer name="horizontalSpacer_19">
264 <property name="orientation">
265 <enum>Qt::Horizontal</enum>
266 </property>
267 <property name="sizeHint" stdset="0">
268 <size>
269 <width>127</width>
270 <height>20</height>
271 </size>
272 </property>
273 </spacer>
274 </item>
275 </layout>
276 </widget>
277 </item>
278 <item row="2" column="0" colspan="3">
279 <widget class="QWidget" name="subOSK" native="true">
280 <layout class="QHBoxLayout" name="horizontalLayout_3" stretch="130,1020,130">
281 <property name="spacing">
282 <number>0</number>
283 </property>
284 <property name="leftMargin">
285 <number>0</number>
286 </property>
287 <property name="topMargin">
288 <number>0</number>
289 </property>
290 <property name="rightMargin">
291 <number>0</number>
292 </property>
293 <property name="bottomMargin">
294 <number>0</number>
295 </property>
296 <item>
297 <spacer name="horizontalSpacer_16">
298 <property name="orientation">
299 <enum>Qt::Horizontal</enum>
300 </property>
301 <property name="sizeHint" stdset="0">
302 <size>
303 <width>127</width>
304 <height>20</height>
305 </size>
306 </property>
307 </spacer>
308 </item>
309 <item>
310 <widget class="QLabel" name="label_sub">
311 <property name="font">
312 <font>
313 <pointsize>17</pointsize>
314 </font>
315 </property>
316 <property name="text">
317 <string/>
318 </property>
319 </widget>
320 </item>
321 <item>
322 <spacer name="horizontalSpacer_17">
323 <property name="orientation">
324 <enum>Qt::Horizontal</enum>
325 </property>
326 <property name="sizeHint" stdset="0">
327 <size>
328 <width>127</width>
329 <height>20</height>
330 </size>
331 </property>
332 </spacer>
333 </item>
334 </layout>
335 </widget>
336 </item>
337 </layout>
338 </item>
339 </layout>
340 </widget>
341 <widget class="QWidget" name="boxOSK">
342 <layout class="QVBoxLayout" name="boxOSKVerticalLayout">
343 <property name="spacing">
344 <number>0</number>
345 </property>
346 <property name="leftMargin">
347 <number>0</number>
348 </property>
349 <property name="topMargin">
350 <number>0</number>
351 </property>
352 <property name="rightMargin">
353 <number>0</number>
354 </property>
355 <property name="bottomMargin">
356 <number>0</number>
357 </property>
358 <item>
359 <layout class="QGridLayout" name="gridBoxOSK" rowstretch="61,178,81" columnstretch="120,1040,120">
360 <property name="leftMargin">
361 <number>0</number>
362 </property>
363 <property name="topMargin">
364 <number>0</number>
365 </property>
366 <property name="rightMargin">
367 <number>0</number>
368 </property>
369 <property name="bottomMargin">
370 <number>0</number>
371 </property>
372 <property name="spacing">
373 <number>0</number>
374 </property>
375 <item row="0" column="1">
376 <spacer name="verticalSpacer_5">
377 <property name="orientation">
378 <enum>Qt::Vertical</enum>
379 </property>
380 <property name="sizeHint" stdset="0">
381 <size>
382 <width>20</width>
383 <height>40</height>
384 </size>
385 </property>
386 </spacer>
387 </item>
388 <item row="1" column="0">
389 <spacer name="horizontalSpacer_20">
390 <property name="orientation">
391 <enum>Qt::Horizontal</enum>
392 </property>
393 <property name="sizeHint" stdset="0">
394 <size>
395 <width>40</width>
396 <height>20</height>
397 </size>
398 </property>
399 </spacer>
400 </item>
401 <item row="1" column="2">
402 <spacer name="horizontalSpacer_21">
403 <property name="orientation">
404 <enum>Qt::Horizontal</enum>
405 </property>
406 <property name="sizeHint" stdset="0">
407 <size>
408 <width>40</width>
409 <height>20</height>
410 </size>
411 </property>
412 </spacer>
413 </item>
414 <item row="2" column="1" alignment="Qt::AlignRight|Qt::AlignTop">
415 <widget class="QWidget" name="charactersBoxOSK" native="true">
416 <layout class="QVBoxLayout" name="verticalLayout_4">
417 <property name="spacing">
418 <number>0</number>
419 </property>
420 <property name="leftMargin">
421 <number>0</number>
422 </property>
423 <property name="topMargin">
424 <number>0</number>
425 </property>
426 <property name="rightMargin">
427 <number>0</number>
428 </property>
429 <property name="bottomMargin">
430 <number>0</number>
431 </property>
432 <item>
433 <widget class="QLabel" name="label_characters_box">
434 <property name="enabled">
435 <bool>true</bool>
436 </property>
437 <property name="font">
438 <font>
439 <pointsize>17</pointsize>
440 </font>
441 </property>
442 <property name="text">
443 <string notr="true">0/500</string>
444 </property>
445 </widget>
446 </item>
447 </layout>
448 </widget>
449 </item>
450 <item row="1" column="1">
451 <widget class="QWidget" name="inputBoxOSK" native="true">
452 <layout class="QVBoxLayout" name="verticalLayout_6">
453 <property name="spacing">
454 <number>0</number>
455 </property>
456 <property name="leftMargin">
457 <number>14</number>
458 </property>
459 <property name="topMargin">
460 <number>9</number>
461 </property>
462 <property name="rightMargin">
463 <number>14</number>
464 </property>
465 <property name="bottomMargin">
466 <number>9</number>
467 </property>
468 <item>
469 <widget class="QTextEdit" name="text_edit_osk">
470 <property name="font">
471 <font>
472 <pointsize>26</pointsize>
473 </font>
474 </property>
475 <property name="focusPolicy">
476 <enum>Qt::StrongFocus</enum>
477 </property>
478 <property name="html">
479 <string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
480&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
481p, li { white-space: pre-wrap; }
482&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:26pt; font-weight:400; font-style:normal;&quot;&gt;
483&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
484 </property>
485 </widget>
486 </item>
487 </layout>
488 </widget>
489 </item>
490 </layout>
491 </item>
492 </layout>
493 </widget>
494 </widget>
495 </item>
496 <item>
497 <widget class="QStackedWidget" name="bottomOSK">
498 <property name="currentIndex">
499 <number>0</number>
500 </property>
501 <widget class="QWidget" name="normalOSK">
502 <layout class="QVBoxLayout" name="normalPageVerticalLayout">
503 <property name="leftMargin">
504 <number>0</number>
505 </property>
506 <property name="topMargin">
507 <number>0</number>
508 </property>
509 <property name="rightMargin">
510 <number>0</number>
511 </property>
512 <property name="bottomMargin">
513 <number>0</number>
514 </property>
515 <item>
516 <layout class="QGridLayout" name="kbOSKnormal" rowstretch="15,63,63,63,63,63,70" columnstretch="54,96,96,96,96,96,96,96,96,96,96,96,116,54">
517 <property name="spacing">
518 <number>0</number>
519 </property>
520 <item row="6" column="1" colspan="12">
521 <widget class="QWidget" name="legendOSK" native="true">
522 <layout class="QHBoxLayout" name="horizontalLayout_2" stretch="70,525,25,12,22,41,25,12,22,41,25,12,47,37,29,12,69,37,29,12,56,8">
523 <property name="spacing">
524 <number>0</number>
525 </property>
526 <property name="leftMargin">
527 <number>2</number>
528 </property>
529 <property name="topMargin">
530 <number>0</number>
531 </property>
532 <property name="rightMargin">
533 <number>0</number>
534 </property>
535 <property name="bottomMargin">
536 <number>0</number>
537 </property>
538 <item>
539 <widget class="QWidget" name="icon_controller" native="true">
540 <property name="styleSheet">
541 <string notr="true"/>
542 </property>
543 </widget>
544 </item>
545 <item>
546 <spacer name="horizontalSpacer_5">
547 <property name="orientation">
548 <enum>Qt::Horizontal</enum>
549 </property>
550 <property name="sizeHint" stdset="0">
551 <size>
552 <width>0</width>
553 <height>20</height>
554 </size>
555 </property>
556 </spacer>
557 </item>
558 <item>
559 <widget class="QWidget" name="button_L" native="true"/>
560 </item>
561 <item>
562 <spacer name="horizontalSpacer_14">
563 <property name="orientation">
564 <enum>Qt::Horizontal</enum>
565 </property>
566 <property name="sizeHint" stdset="0">
567 <size>
568 <width>0</width>
569 <height>20</height>
570 </size>
571 </property>
572 </spacer>
573 </item>
574 <item>
575 <widget class="QWidget" name="arrow_left" native="true"/>
576 </item>
577 <item>
578 <spacer name="horizontalSpacer_13">
579 <property name="orientation">
580 <enum>Qt::Horizontal</enum>
581 </property>
582 <property name="sizeHint" stdset="0">
583 <size>
584 <width>0</width>
585 <height>20</height>
586 </size>
587 </property>
588 </spacer>
589 </item>
590 <item>
591 <widget class="QWidget" name="button_R" native="true"/>
592 </item>
593 <item>
594 <spacer name="horizontalSpacer_12">
595 <property name="orientation">
596 <enum>Qt::Horizontal</enum>
597 </property>
598 <property name="sizeHint" stdset="0">
599 <size>
600 <width>0</width>
601 <height>20</height>
602 </size>
603 </property>
604 </spacer>
605 </item>
606 <item>
607 <widget class="QWidget" name="arrow_right" native="true"/>
608 </item>
609 <item>
610 <spacer name="horizontalSpacer_11">
611 <property name="orientation">
612 <enum>Qt::Horizontal</enum>
613 </property>
614 <property name="sizeHint" stdset="0">
615 <size>
616 <width>0</width>
617 <height>20</height>
618 </size>
619 </property>
620 </spacer>
621 </item>
622 <item>
623 <widget class="QWidget" name="button_press_stick" native="true"/>
624 </item>
625 <item>
626 <spacer name="horizontalSpacer_10">
627 <property name="orientation">
628 <enum>Qt::Horizontal</enum>
629 </property>
630 <property name="sizeHint" stdset="0">
631 <size>
632 <width>0</width>
633 <height>20</height>
634 </size>
635 </property>
636 </spacer>
637 </item>
638 <item>
639 <widget class="QLabel" name="label_shift">
640 <property name="font">
641 <font>
642 <pointsize>18</pointsize>
643 </font>
644 </property>
645 <property name="text">
646 <string notr="true">Shift</string>
647 </property>
648 </widget>
649 </item>
650 <item>
651 <spacer name="horizontalSpacer_15">
652 <property name="orientation">
653 <enum>Qt::Horizontal</enum>
654 </property>
655 <property name="sizeHint" stdset="0">
656 <size>
657 <width>0</width>
658 <height>20</height>
659 </size>
660 </property>
661 </spacer>
662 </item>
663 <item>
664 <widget class="QWidget" name="button_X" native="true"/>
665 </item>
666 <item>
667 <spacer name="horizontalSpacer_9">
668 <property name="orientation">
669 <enum>Qt::Horizontal</enum>
670 </property>
671 <property name="sizeHint" stdset="0">
672 <size>
673 <width>0</width>
674 <height>20</height>
675 </size>
676 </property>
677 </spacer>
678 </item>
679 <item>
680 <widget class="QLabel" name="label_cancel">
681 <property name="font">
682 <font>
683 <pointsize>18</pointsize>
684 </font>
685 </property>
686 <property name="text">
687 <string notr="true">Cancel</string>
688 </property>
689 </widget>
690 </item>
691 <item>
692 <spacer name="horizontalSpacer_8">
693 <property name="orientation">
694 <enum>Qt::Horizontal</enum>
695 </property>
696 <property name="sizeHint" stdset="0">
697 <size>
698 <width>0</width>
699 <height>20</height>
700 </size>
701 </property>
702 </spacer>
703 </item>
704 <item>
705 <widget class="QWidget" name="button_A" native="true"/>
706 </item>
707 <item>
708 <spacer name="horizontalSpacer_7">
709 <property name="orientation">
710 <enum>Qt::Horizontal</enum>
711 </property>
712 <property name="sizeHint" stdset="0">
713 <size>
714 <width>0</width>
715 <height>20</height>
716 </size>
717 </property>
718 </spacer>
719 </item>
720 <item>
721 <widget class="QLabel" name="label_enter">
722 <property name="font">
723 <font>
724 <pointsize>18</pointsize>
725 </font>
726 </property>
727 <property name="text">
728 <string notr="true">Enter</string>
729 </property>
730 </widget>
731 </item>
732 <item>
733 <spacer name="horizontalSpacer_6">
734 <property name="orientation">
735 <enum>Qt::Horizontal</enum>
736 </property>
737 <property name="sizeHint" stdset="0">
738 <size>
739 <width>0</width>
740 <height>20</height>
741 </size>
742 </property>
743 </spacer>
744 </item>
745 </layout>
746 </widget>
747 </item>
748 <item row="1" column="11">
749 <widget class="QPushButton" name="button_minus">
750 <property name="sizePolicy">
751 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
752 <horstretch>1</horstretch>
753 <verstretch>1</verstretch>
754 </sizepolicy>
755 </property>
756 <property name="font">
757 <font>
758 <pointsize>28</pointsize>
759 </font>
760 </property>
761 <property name="text">
762 <string notr="true">-</string>
763 </property>
764 </widget>
765 </item>
766 <item row="3" column="11">
767 <widget class="QPushButton" name="button_apostrophe">
768 <property name="sizePolicy">
769 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
770 <horstretch>1</horstretch>
771 <verstretch>1</verstretch>
772 </sizepolicy>
773 </property>
774 <property name="font">
775 <font>
776 <pointsize>28</pointsize>
777 </font>
778 </property>
779 <property name="text">
780 <string notr="true">'</string>
781 </property>
782 </widget>
783 </item>
784 <item row="2" column="11">
785 <widget class="QPushButton" name="button_slash">
786 <property name="sizePolicy">
787 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
788 <horstretch>1</horstretch>
789 <verstretch>1</verstretch>
790 </sizepolicy>
791 </property>
792 <property name="font">
793 <font>
794 <pointsize>28</pointsize>
795 </font>
796 </property>
797 <property name="text">
798 <string notr="true">/</string>
799 </property>
800 </widget>
801 </item>
802 <item row="4" column="11">
803 <widget class="QPushButton" name="button_exclamation">
804 <property name="sizePolicy">
805 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
806 <horstretch>1</horstretch>
807 <verstretch>1</verstretch>
808 </sizepolicy>
809 </property>
810 <property name="font">
811 <font>
812 <pointsize>28</pointsize>
813 </font>
814 </property>
815 <property name="text">
816 <string notr="true">!</string>
817 </property>
818 </widget>
819 </item>
820 <item row="1" column="7">
821 <widget class="QPushButton" name="button_7">
822 <property name="sizePolicy">
823 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
824 <horstretch>1</horstretch>
825 <verstretch>1</verstretch>
826 </sizepolicy>
827 </property>
828 <property name="font">
829 <font>
830 <pointsize>28</pointsize>
831 </font>
832 </property>
833 <property name="text">
834 <string notr="true">7</string>
835 </property>
836 </widget>
837 </item>
838 <item row="1" column="8">
839 <widget class="QPushButton" name="button_8">
840 <property name="sizePolicy">
841 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
842 <horstretch>1</horstretch>
843 <verstretch>1</verstretch>
844 </sizepolicy>
845 </property>
846 <property name="font">
847 <font>
848 <pointsize>28</pointsize>
849 </font>
850 </property>
851 <property name="text">
852 <string notr="true">8</string>
853 </property>
854 </widget>
855 </item>
856 <item row="1" column="10">
857 <widget class="QPushButton" name="button_0">
858 <property name="sizePolicy">
859 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
860 <horstretch>1</horstretch>
861 <verstretch>1</verstretch>
862 </sizepolicy>
863 </property>
864 <property name="font">
865 <font>
866 <pointsize>28</pointsize>
867 </font>
868 </property>
869 <property name="text">
870 <string notr="true">0</string>
871 </property>
872 </widget>
873 </item>
874 <item row="1" column="9">
875 <widget class="QPushButton" name="button_9">
876 <property name="sizePolicy">
877 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
878 <horstretch>1</horstretch>
879 <verstretch>1</verstretch>
880 </sizepolicy>
881 </property>
882 <property name="font">
883 <font>
884 <pointsize>28</pointsize>
885 </font>
886 </property>
887 <property name="text">
888 <string notr="true">9</string>
889 </property>
890 </widget>
891 </item>
892 <item row="2" column="2">
893 <widget class="QPushButton" name="button_w">
894 <property name="sizePolicy">
895 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
896 <horstretch>1</horstretch>
897 <verstretch>1</verstretch>
898 </sizepolicy>
899 </property>
900 <property name="font">
901 <font>
902 <pointsize>28</pointsize>
903 </font>
904 </property>
905 <property name="text">
906 <string notr="true">w</string>
907 </property>
908 </widget>
909 </item>
910 <item row="2" column="4">
911 <widget class="QPushButton" name="button_r">
912 <property name="sizePolicy">
913 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
914 <horstretch>1</horstretch>
915 <verstretch>1</verstretch>
916 </sizepolicy>
917 </property>
918 <property name="font">
919 <font>
920 <pointsize>28</pointsize>
921 </font>
922 </property>
923 <property name="text">
924 <string notr="true">r</string>
925 </property>
926 </widget>
927 </item>
928 <item row="2" column="3">
929 <widget class="QPushButton" name="button_e">
930 <property name="sizePolicy">
931 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
932 <horstretch>1</horstretch>
933 <verstretch>1</verstretch>
934 </sizepolicy>
935 </property>
936 <property name="font">
937 <font>
938 <pointsize>28</pointsize>
939 </font>
940 </property>
941 <property name="text">
942 <string notr="true">e</string>
943 </property>
944 </widget>
945 </item>
946 <item row="2" column="1">
947 <widget class="QPushButton" name="button_q">
948 <property name="sizePolicy">
949 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
950 <horstretch>1</horstretch>
951 <verstretch>1</verstretch>
952 </sizepolicy>
953 </property>
954 <property name="font">
955 <font>
956 <pointsize>28</pointsize>
957 </font>
958 </property>
959 <property name="text">
960 <string notr="true">q</string>
961 </property>
962 </widget>
963 </item>
964 <item row="2" column="7">
965 <widget class="QPushButton" name="button_u">
966 <property name="sizePolicy">
967 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
968 <horstretch>1</horstretch>
969 <verstretch>1</verstretch>
970 </sizepolicy>
971 </property>
972 <property name="font">
973 <font>
974 <pointsize>28</pointsize>
975 </font>
976 </property>
977 <property name="text">
978 <string notr="true">u</string>
979 </property>
980 </widget>
981 </item>
982 <item row="2" column="6">
983 <widget class="QPushButton" name="button_y">
984 <property name="sizePolicy">
985 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
986 <horstretch>1</horstretch>
987 <verstretch>1</verstretch>
988 </sizepolicy>
989 </property>
990 <property name="font">
991 <font>
992 <pointsize>28</pointsize>
993 </font>
994 </property>
995 <property name="text">
996 <string notr="true">y</string>
997 </property>
998 </widget>
999 </item>
1000 <item row="2" column="5">
1001 <widget class="QPushButton" name="button_t">
1002 <property name="sizePolicy">
1003 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
1004 <horstretch>1</horstretch>
1005 <verstretch>1</verstretch>
1006 </sizepolicy>
1007 </property>
1008 <property name="font">
1009 <font>
1010 <pointsize>28</pointsize>
1011 </font>
1012 </property>
1013 <property name="text">
1014 <string notr="true">t</string>
1015 </property>
1016 </widget>
1017 </item>
1018 <item row="2" column="9">
1019 <widget class="QPushButton" name="button_o">
1020 <property name="sizePolicy">
1021 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
1022 <horstretch>1</horstretch>
1023 <verstretch>1</verstretch>
1024 </sizepolicy>
1025 </property>
1026 <property name="font">
1027 <font>
1028 <pointsize>28</pointsize>
1029 </font>
1030 </property>
1031 <property name="text">
1032 <string notr="true">o</string>
1033 </property>
1034 </widget>
1035 </item>
1036 <item row="2" column="10">
1037 <widget class="QPushButton" name="button_p">
1038 <property name="sizePolicy">
1039 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
1040 <horstretch>1</horstretch>
1041 <verstretch>1</verstretch>
1042 </sizepolicy>
1043 </property>
1044 <property name="font">
1045 <font>
1046 <pointsize>28</pointsize>
1047 </font>
1048 </property>
1049 <property name="text">
1050 <string notr="true">p</string>
1051 </property>
1052 </widget>
1053 </item>
1054 <item row="2" column="8">
1055 <widget class="QPushButton" name="button_i">
1056 <property name="sizePolicy">
1057 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
1058 <horstretch>1</horstretch>
1059 <verstretch>1</verstretch>
1060 </sizepolicy>
1061 </property>
1062 <property name="font">
1063 <font>
1064 <pointsize>28</pointsize>
1065 </font>
1066 </property>
1067 <property name="text">
1068 <string notr="true">i</string>
1069 </property>
1070 </widget>
1071 </item>
1072 <item row="3" column="1">
1073 <widget class="QPushButton" name="button_a">
1074 <property name="sizePolicy">
1075 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
1076 <horstretch>1</horstretch>
1077 <verstretch>1</verstretch>
1078 </sizepolicy>
1079 </property>
1080 <property name="font">
1081 <font>
1082 <pointsize>28</pointsize>
1083 </font>
1084 </property>
1085 <property name="text">
1086 <string notr="true">a</string>
1087 </property>
1088 </widget>
1089 </item>
1090 <item row="3" column="2">
1091 <widget class="QPushButton" name="button_s">
1092 <property name="sizePolicy">
1093 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
1094 <horstretch>1</horstretch>
1095 <verstretch>1</verstretch>
1096 </sizepolicy>
1097 </property>
1098 <property name="font">
1099 <font>
1100 <pointsize>28</pointsize>
1101 </font>
1102 </property>
1103 <property name="text">
1104 <string notr="true">s</string>
1105 </property>
1106 </widget>
1107 </item>
1108 <item row="3" column="3">
1109 <widget class="QPushButton" name="button_d">
1110 <property name="sizePolicy">
1111 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
1112 <horstretch>1</horstretch>
1113 <verstretch>1</verstretch>
1114 </sizepolicy>
1115 </property>
1116 <property name="font">
1117 <font>
1118 <pointsize>28</pointsize>
1119 </font>
1120 </property>
1121 <property name="text">
1122 <string notr="true">d</string>
1123 </property>
1124 </widget>
1125 </item>
1126 <item row="3" column="4">
1127 <widget class="QPushButton" name="button_f">
1128 <property name="sizePolicy">
1129 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
1130 <horstretch>1</horstretch>
1131 <verstretch>1</verstretch>
1132 </sizepolicy>
1133 </property>
1134 <property name="font">
1135 <font>
1136 <pointsize>28</pointsize>
1137 </font>
1138 </property>
1139 <property name="text">
1140 <string notr="true">f</string>
1141 </property>
1142 </widget>
1143 </item>
1144 <item row="3" column="6">
1145 <widget class="QPushButton" name="button_h">
1146 <property name="sizePolicy">
1147 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
1148 <horstretch>1</horstretch>
1149 <verstretch>1</verstretch>
1150 </sizepolicy>
1151 </property>
1152 <property name="font">
1153 <font>
1154 <pointsize>28</pointsize>
1155 </font>
1156 </property>
1157 <property name="text">
1158 <string notr="true">h</string>
1159 </property>
1160 </widget>
1161 </item>
1162 <item row="3" column="7">
1163 <widget class="QPushButton" name="button_j">
1164 <property name="sizePolicy">
1165 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
1166 <horstretch>1</horstretch>
1167 <verstretch>1</verstretch>
1168 </sizepolicy>
1169 </property>
1170 <property name="font">
1171 <font>
1172 <pointsize>28</pointsize>
1173 </font>
1174 </property>
1175 <property name="text">
1176 <string notr="true">j</string>
1177 </property>
1178 </widget>
1179 </item>
1180 <item row="3" column="5">
1181 <widget class="QPushButton" name="button_g">
1182 <property name="sizePolicy">
1183 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
1184 <horstretch>1</horstretch>
1185 <verstretch>1</verstretch>
1186 </sizepolicy>
1187 </property>
1188 <property name="font">
1189 <font>
1190 <pointsize>28</pointsize>
1191 </font>
1192 </property>
1193 <property name="text">
1194 <string notr="true">g</string>
1195 </property>
1196 </widget>
1197 </item>
1198 <item row="3" column="8">
1199 <widget class="QPushButton" name="button_k">
1200 <property name="sizePolicy">
1201 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
1202 <horstretch>1</horstretch>
1203 <verstretch>1</verstretch>
1204 </sizepolicy>
1205 </property>
1206 <property name="font">
1207 <font>
1208 <pointsize>28</pointsize>
1209 </font>
1210 </property>
1211 <property name="text">
1212 <string notr="true">k</string>
1213 </property>
1214 </widget>
1215 </item>
1216 <item row="3" column="9">
1217 <widget class="QPushButton" name="button_l">
1218 <property name="sizePolicy">
1219 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
1220 <horstretch>1</horstretch>
1221 <verstretch>1</verstretch>
1222 </sizepolicy>
1223 </property>
1224 <property name="font">
1225 <font>
1226 <pointsize>28</pointsize>
1227 </font>
1228 </property>
1229 <property name="text">
1230 <string notr="true">l</string>
1231 </property>
1232 </widget>
1233 </item>
1234 <item row="3" column="10">
1235 <widget class="QPushButton" name="button_colon">
1236 <property name="sizePolicy">
1237 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
1238 <horstretch>1</horstretch>
1239 <verstretch>1</verstretch>
1240 </sizepolicy>
1241 </property>
1242 <property name="font">
1243 <font>
1244 <pointsize>28</pointsize>
1245 </font>
1246 </property>
1247 <property name="text">
1248 <string notr="true">:</string>
1249 </property>
1250 </widget>
1251 </item>
1252 <item row="2" column="12" rowspan="2">
1253 <widget class="QPushButton" name="button_return">
1254 <property name="sizePolicy">
1255 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
1256 <horstretch>1</horstretch>
1257 <verstretch>1</verstretch>
1258 </sizepolicy>
1259 </property>
1260 <property name="font">
1261 <font>
1262 <pointsize>18</pointsize>
1263 </font>
1264 </property>
1265 <property name="text">
1266 <string notr="true">Return</string>
1267 </property>
1268 </widget>
1269 </item>
1270 <item row="4" column="12" rowspan="2">
1271 <widget class="QPushButton" name="button_ok">
1272 <property name="sizePolicy">
1273 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
1274 <horstretch>1</horstretch>
1275 <verstretch>1</verstretch>
1276 </sizepolicy>
1277 </property>
1278 <property name="font">
1279 <font>
1280 <pointsize>18</pointsize>
1281 </font>
1282 </property>
1283 <property name="text">
1284 <string notr="true">OK</string>
1285 </property>
1286 </widget>
1287 </item>
1288 <item row="4" column="1">
1289 <widget class="QPushButton" name="button_z">
1290 <property name="sizePolicy">
1291 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
1292 <horstretch>1</horstretch>
1293 <verstretch>1</verstretch>
1294 </sizepolicy>
1295 </property>
1296 <property name="font">
1297 <font>
1298 <pointsize>28</pointsize>
1299 </font>
1300 </property>
1301 <property name="text">
1302 <string notr="true">z</string>
1303 </property>
1304 </widget>
1305 </item>
1306 <item row="4" column="3">
1307 <widget class="QPushButton" name="button_c">
1308 <property name="sizePolicy">
1309 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
1310 <horstretch>1</horstretch>
1311 <verstretch>1</verstretch>
1312 </sizepolicy>
1313 </property>
1314 <property name="font">
1315 <font>
1316 <pointsize>28</pointsize>
1317 </font>
1318 </property>
1319 <property name="text">
1320 <string notr="true">c</string>
1321 </property>
1322 </widget>
1323 </item>
1324 <item row="4" column="2">
1325 <widget class="QPushButton" name="button_x">
1326 <property name="sizePolicy">
1327 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
1328 <horstretch>1</horstretch>
1329 <verstretch>1</verstretch>
1330 </sizepolicy>
1331 </property>
1332 <property name="font">
1333 <font>
1334 <pointsize>28</pointsize>
1335 </font>
1336 </property>
1337 <property name="text">
1338 <string notr="true">x</string>
1339 </property>
1340 </widget>
1341 </item>
1342 <item row="4" column="4">
1343 <widget class="QPushButton" name="button_v">
1344 <property name="sizePolicy">
1345 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
1346 <horstretch>1</horstretch>
1347 <verstretch>1</verstretch>
1348 </sizepolicy>
1349 </property>
1350 <property name="font">
1351 <font>
1352 <pointsize>28</pointsize>
1353 </font>
1354 </property>
1355 <property name="text">
1356 <string notr="true">v</string>
1357 </property>
1358 </widget>
1359 </item>
1360 <item row="4" column="7">
1361 <widget class="QPushButton" name="button_m">
1362 <property name="sizePolicy">
1363 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
1364 <horstretch>1</horstretch>
1365 <verstretch>1</verstretch>
1366 </sizepolicy>
1367 </property>
1368 <property name="font">
1369 <font>
1370 <pointsize>28</pointsize>
1371 </font>
1372 </property>
1373 <property name="text">
1374 <string notr="true">m</string>
1375 </property>
1376 </widget>
1377 </item>
1378 <item row="4" column="8">
1379 <widget class="QPushButton" name="button_comma">
1380 <property name="sizePolicy">
1381 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
1382 <horstretch>1</horstretch>
1383 <verstretch>1</verstretch>
1384 </sizepolicy>
1385 </property>
1386 <property name="font">
1387 <font>
1388 <pointsize>28</pointsize>
1389 </font>
1390 </property>
1391 <property name="text">
1392 <string notr="true">,</string>
1393 </property>
1394 </widget>
1395 </item>
1396 <item row="4" column="6">
1397 <widget class="QPushButton" name="button_n">
1398 <property name="sizePolicy">
1399 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
1400 <horstretch>1</horstretch>
1401 <verstretch>1</verstretch>
1402 </sizepolicy>
1403 </property>
1404 <property name="font">
1405 <font>
1406 <pointsize>28</pointsize>
1407 </font>
1408 </property>
1409 <property name="text">
1410 <string notr="true">n</string>
1411 </property>
1412 </widget>
1413 </item>
1414 <item row="4" column="5">
1415 <widget class="QPushButton" name="button_b">
1416 <property name="sizePolicy">
1417 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
1418 <horstretch>1</horstretch>
1419 <verstretch>1</verstretch>
1420 </sizepolicy>
1421 </property>
1422 <property name="font">
1423 <font>
1424 <pointsize>28</pointsize>
1425 </font>
1426 </property>
1427 <property name="text">
1428 <string notr="true">b</string>
1429 </property>
1430 </widget>
1431 </item>
1432 <item row="5" column="1" colspan="2">
1433 <widget class="QPushButton" name="button_shift">
1434 <property name="sizePolicy">
1435 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
1436 <horstretch>1</horstretch>
1437 <verstretch>1</verstretch>
1438 </sizepolicy>
1439 </property>
1440 <property name="font">
1441 <font>
1442 <pointsize>18</pointsize>
1443 </font>
1444 </property>
1445 <property name="text">
1446 <string notr="true"/>
1447 </property>
1448 <property name="checkable">
1449 <bool>true</bool>
1450 </property>
1451 <property name="checked">
1452 <bool>false</bool>
1453 </property>
1454 </widget>
1455 </item>
1456 <item row="4" column="10">
1457 <widget class="QPushButton" name="button_question">
1458 <property name="sizePolicy">
1459 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
1460 <horstretch>1</horstretch>
1461 <verstretch>1</verstretch>
1462 </sizepolicy>
1463 </property>
1464 <property name="font">
1465 <font>
1466 <pointsize>28</pointsize>
1467 </font>
1468 </property>
1469 <property name="text">
1470 <string notr="true">?</string>
1471 </property>
1472 </widget>
1473 </item>
1474 <item row="4" column="9">
1475 <widget class="QPushButton" name="button_dot">
1476 <property name="sizePolicy">
1477 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
1478 <horstretch>1</horstretch>
1479 <verstretch>1</verstretch>
1480 </sizepolicy>
1481 </property>
1482 <property name="font">
1483 <font>
1484 <pointsize>28</pointsize>
1485 </font>
1486 </property>
1487 <property name="text">
1488 <string notr="true">.</string>
1489 </property>
1490 </widget>
1491 </item>
1492 <item row="1" column="1">
1493 <widget class="QPushButton" name="button_1">
1494 <property name="sizePolicy">
1495 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
1496 <horstretch>1</horstretch>
1497 <verstretch>1</verstretch>
1498 </sizepolicy>
1499 </property>
1500 <property name="font">
1501 <font>
1502 <pointsize>28</pointsize>
1503 </font>
1504 </property>
1505 <property name="text">
1506 <string notr="true">1</string>
1507 </property>
1508 </widget>
1509 </item>
1510 <item row="1" column="3">
1511 <widget class="QPushButton" name="button_3">
1512 <property name="sizePolicy">
1513 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
1514 <horstretch>1</horstretch>
1515 <verstretch>1</verstretch>
1516 </sizepolicy>
1517 </property>
1518 <property name="font">
1519 <font>
1520 <pointsize>28</pointsize>
1521 </font>
1522 </property>
1523 <property name="text">
1524 <string notr="true">3</string>
1525 </property>
1526 </widget>
1527 </item>
1528 <item row="1" column="4">
1529 <widget class="QPushButton" name="button_4">
1530 <property name="sizePolicy">
1531 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
1532 <horstretch>1</horstretch>
1533 <verstretch>1</verstretch>
1534 </sizepolicy>
1535 </property>
1536 <property name="font">
1537 <font>
1538 <pointsize>28</pointsize>
1539 </font>
1540 </property>
1541 <property name="text">
1542 <string notr="true">4</string>
1543 </property>
1544 </widget>
1545 </item>
1546 <item row="1" column="2">
1547 <widget class="QPushButton" name="button_2">
1548 <property name="sizePolicy">
1549 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
1550 <horstretch>1</horstretch>
1551 <verstretch>1</verstretch>
1552 </sizepolicy>
1553 </property>
1554 <property name="font">
1555 <font>
1556 <pointsize>28</pointsize>
1557 </font>
1558 </property>
1559 <property name="text">
1560 <string notr="true">2</string>
1561 </property>
1562 </widget>
1563 </item>
1564 <item row="1" column="6">
1565 <widget class="QPushButton" name="button_6">
1566 <property name="sizePolicy">
1567 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
1568 <horstretch>1</horstretch>
1569 <verstretch>1</verstretch>
1570 </sizepolicy>
1571 </property>
1572 <property name="font">
1573 <font>
1574 <pointsize>28</pointsize>
1575 </font>
1576 </property>
1577 <property name="text">
1578 <string notr="true">6</string>
1579 </property>
1580 </widget>
1581 </item>
1582 <item row="1" column="5">
1583 <widget class="QPushButton" name="button_5">
1584 <property name="sizePolicy">
1585 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
1586 <horstretch>1</horstretch>
1587 <verstretch>1</verstretch>
1588 </sizepolicy>
1589 </property>
1590 <property name="font">
1591 <font>
1592 <pointsize>28</pointsize>
1593 </font>
1594 </property>
1595 <property name="text">
1596 <string notr="true">5</string>
1597 </property>
1598 </widget>
1599 </item>
1600 <item row="5" column="3" colspan="9">
1601 <widget class="QPushButton" name="button_space">
1602 <property name="sizePolicy">
1603 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
1604 <horstretch>1</horstretch>
1605 <verstretch>1</verstretch>
1606 </sizepolicy>
1607 </property>
1608 <property name="font">
1609 <font>
1610 <pointsize>18</pointsize>
1611 </font>
1612 </property>
1613 <property name="text">
1614 <string notr="true">Space</string>
1615 </property>
1616 </widget>
1617 </item>
1618 <item row="1" column="12">
1619 <widget class="QPushButton" name="button_backspace">
1620 <property name="sizePolicy">
1621 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
1622 <horstretch>1</horstretch>
1623 <verstretch>1</verstretch>
1624 </sizepolicy>
1625 </property>
1626 <property name="font">
1627 <font>
1628 <pointsize>18</pointsize>
1629 </font>
1630 </property>
1631 <property name="text">
1632 <string notr="true"/>
1633 </property>
1634 </widget>
1635 </item>
1636 <item row="1" column="0">
1637 <spacer name="horizontalSpacer">
1638 <property name="orientation">
1639 <enum>Qt::Horizontal</enum>
1640 </property>
1641 <property name="sizeHint" stdset="0">
1642 <size>
1643 <width>40</width>
1644 <height>20</height>
1645 </size>
1646 </property>
1647 </spacer>
1648 </item>
1649 <item row="0" column="1">
1650 <spacer name="verticalSpacer">
1651 <property name="orientation">
1652 <enum>Qt::Vertical</enum>
1653 </property>
1654 <property name="sizeHint" stdset="0">
1655 <size>
1656 <width>20</width>
1657 <height>0</height>
1658 </size>
1659 </property>
1660 </spacer>
1661 </item>
1662 <item row="1" column="13">
1663 <spacer name="horizontalSpacer_2">
1664 <property name="orientation">
1665 <enum>Qt::Horizontal</enum>
1666 </property>
1667 <property name="sizeHint" stdset="0">
1668 <size>
1669 <width>40</width>
1670 <height>20</height>
1671 </size>
1672 </property>
1673 </spacer>
1674 </item>
1675 </layout>
1676 </item>
1677 </layout>
1678 </widget>
1679 <widget class="QWidget" name="shiftOSK">
1680 <layout class="QVBoxLayout" name="shiftPageVerticalLayout">
1681 <property name="leftMargin">
1682 <number>0</number>
1683 </property>
1684 <property name="topMargin">
1685 <number>0</number>
1686 </property>
1687 <property name="rightMargin">
1688 <number>0</number>
1689 </property>
1690 <property name="bottomMargin">
1691 <number>0</number>
1692 </property>
1693 <item>
1694 <layout class="QGridLayout" name="kbOSKshift" rowstretch="15,63,63,63,63,63,70" columnstretch="54,96,96,96,96,96,96,96,96,96,96,96,116,54">
1695 <property name="spacing">
1696 <number>0</number>
1697 </property>
1698 <item row="6" column="1" colspan="12">
1699 <widget class="QWidget" name="legendOSKshift" native="true">
1700 <layout class="QHBoxLayout" name="horizontalLayout_5" stretch="70,464,25,12,22,41,25,12,22,41,25,12,95,37,29,12,69,37,29,12,56,8">
1701 <property name="spacing">
1702 <number>0</number>
1703 </property>
1704 <property name="leftMargin">
1705 <number>2</number>
1706 </property>
1707 <property name="topMargin">
1708 <number>0</number>
1709 </property>
1710 <property name="rightMargin">
1711 <number>0</number>
1712 </property>
1713 <property name="bottomMargin">
1714 <number>0</number>
1715 </property>
1716 <item>
1717 <widget class="QWidget" name="icon_controller_shift" native="true">
1718 <property name="styleSheet">
1719 <string notr="true"/>
1720 </property>
1721 </widget>
1722 </item>
1723 <item>
1724 <spacer name="horizontalSpacer_22">
1725 <property name="orientation">
1726 <enum>Qt::Horizontal</enum>
1727 </property>
1728 <property name="sizeHint" stdset="0">
1729 <size>
1730 <width>0</width>
1731 <height>20</height>
1732 </size>
1733 </property>
1734 </spacer>
1735 </item>
1736 <item>
1737 <widget class="QWidget" name="button_L_shift" native="true"/>
1738 </item>
1739 <item>
1740 <spacer name="horizontalSpacer_23">
1741 <property name="orientation">
1742 <enum>Qt::Horizontal</enum>
1743 </property>
1744 <property name="sizeHint" stdset="0">
1745 <size>
1746 <width>0</width>
1747 <height>20</height>
1748 </size>
1749 </property>
1750 </spacer>
1751 </item>
1752 <item>
1753 <widget class="QWidget" name="arrow_left_shift" native="true"/>
1754 </item>
1755 <item>
1756 <spacer name="horizontalSpacer_24">
1757 <property name="orientation">
1758 <enum>Qt::Horizontal</enum>
1759 </property>
1760 <property name="sizeHint" stdset="0">
1761 <size>
1762 <width>0</width>
1763 <height>20</height>
1764 </size>
1765 </property>
1766 </spacer>
1767 </item>
1768 <item>
1769 <widget class="QWidget" name="button_R_shift" native="true"/>
1770 </item>
1771 <item>
1772 <spacer name="horizontalSpacer_25">
1773 <property name="orientation">
1774 <enum>Qt::Horizontal</enum>
1775 </property>
1776 <property name="sizeHint" stdset="0">
1777 <size>
1778 <width>0</width>
1779 <height>20</height>
1780 </size>
1781 </property>
1782 </spacer>
1783 </item>
1784 <item>
1785 <widget class="QWidget" name="arrow_right_shift" native="true"/>
1786 </item>
1787 <item>
1788 <spacer name="horizontalSpacer_26">
1789 <property name="orientation">
1790 <enum>Qt::Horizontal</enum>
1791 </property>
1792 <property name="sizeHint" stdset="0">
1793 <size>
1794 <width>0</width>
1795 <height>20</height>
1796 </size>
1797 </property>
1798 </spacer>
1799 </item>
1800 <item>
1801 <widget class="QWidget" name="button_press_stick_shift" native="true"/>
1802 </item>
1803 <item>
1804 <spacer name="horizontalSpacer_27">
1805 <property name="orientation">
1806 <enum>Qt::Horizontal</enum>
1807 </property>
1808 <property name="sizeHint" stdset="0">
1809 <size>
1810 <width>0</width>
1811 <height>20</height>
1812 </size>
1813 </property>
1814 </spacer>
1815 </item>
1816 <item>
1817 <widget class="QLabel" name="label_shift_shift">
1818 <property name="font">
1819 <font>
1820 <pointsize>18</pointsize>
1821 </font>
1822 </property>
1823 <property name="text">
1824 <string notr="true">Caps Lock</string>
1825 </property>
1826 </widget>
1827 </item>
1828 <item>
1829 <spacer name="horizontalSpacer_28">
1830 <property name="orientation">
1831 <enum>Qt::Horizontal</enum>
1832 </property>
1833 <property name="sizeHint" stdset="0">
1834 <size>
1835 <width>0</width>
1836 <height>20</height>
1837 </size>
1838 </property>
1839 </spacer>
1840 </item>
1841 <item>
1842 <widget class="QWidget" name="button_X_shift" native="true"/>
1843 </item>
1844 <item>
1845 <spacer name="horizontalSpacer_29">
1846 <property name="orientation">
1847 <enum>Qt::Horizontal</enum>
1848 </property>
1849 <property name="sizeHint" stdset="0">
1850 <size>
1851 <width>0</width>
1852 <height>20</height>
1853 </size>
1854 </property>
1855 </spacer>
1856 </item>
1857 <item>
1858 <widget class="QLabel" name="label_cancel_shift">
1859 <property name="font">
1860 <font>
1861 <pointsize>18</pointsize>
1862 </font>
1863 </property>
1864 <property name="text">
1865 <string notr="true">Cancel</string>
1866 </property>
1867 </widget>
1868 </item>
1869 <item>
1870 <spacer name="horizontalSpacer_30">
1871 <property name="orientation">
1872 <enum>Qt::Horizontal</enum>
1873 </property>
1874 <property name="sizeHint" stdset="0">
1875 <size>
1876 <width>0</width>
1877 <height>20</height>
1878 </size>
1879 </property>
1880 </spacer>
1881 </item>
1882 <item>
1883 <widget class="QWidget" name="button_A_shift" native="true"/>
1884 </item>
1885 <item>
1886 <spacer name="horizontalSpacer_31">
1887 <property name="orientation">
1888 <enum>Qt::Horizontal</enum>
1889 </property>
1890 <property name="sizeHint" stdset="0">
1891 <size>
1892 <width>0</width>
1893 <height>20</height>
1894 </size>
1895 </property>
1896 </spacer>
1897 </item>
1898 <item>
1899 <widget class="QLabel" name="label_enter_shift">
1900 <property name="font">
1901 <font>
1902 <pointsize>18</pointsize>
1903 </font>
1904 </property>
1905 <property name="text">
1906 <string notr="true">Enter</string>
1907 </property>
1908 </widget>
1909 </item>
1910 <item>
1911 <spacer name="horizontalSpacer_32">
1912 <property name="orientation">
1913 <enum>Qt::Horizontal</enum>
1914 </property>
1915 <property name="sizeHint" stdset="0">
1916 <size>
1917 <width>0</width>
1918 <height>20</height>
1919 </size>
1920 </property>
1921 </spacer>
1922 </item>
1923 </layout>
1924 </widget>
1925 </item>
1926 <item row="1" column="11">
1927 <widget class="QPushButton" name="button_underscore">
1928 <property name="sizePolicy">
1929 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
1930 <horstretch>1</horstretch>
1931 <verstretch>1</verstretch>
1932 </sizepolicy>
1933 </property>
1934 <property name="font">
1935 <font>
1936 <pointsize>28</pointsize>
1937 </font>
1938 </property>
1939 <property name="text">
1940 <string notr="true">_</string>
1941 </property>
1942 </widget>
1943 </item>
1944 <item row="3" column="11">
1945 <widget class="QPushButton" name="button_quotation">
1946 <property name="sizePolicy">
1947 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
1948 <horstretch>1</horstretch>
1949 <verstretch>1</verstretch>
1950 </sizepolicy>
1951 </property>
1952 <property name="font">
1953 <font>
1954 <pointsize>28</pointsize>
1955 </font>
1956 </property>
1957 <property name="text">
1958 <string notr="true">&quot;</string>
1959 </property>
1960 </widget>
1961 </item>
1962 <item row="2" column="11">
1963 <widget class="QPushButton" name="button_at">
1964 <property name="sizePolicy">
1965 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
1966 <horstretch>1</horstretch>
1967 <verstretch>1</verstretch>
1968 </sizepolicy>
1969 </property>
1970 <property name="font">
1971 <font>
1972 <pointsize>28</pointsize>
1973 </font>
1974 </property>
1975 <property name="text">
1976 <string notr="true">@</string>
1977 </property>
1978 </widget>
1979 </item>
1980 <item row="4" column="11">
1981 <widget class="QPushButton" name="button_equal">
1982 <property name="sizePolicy">
1983 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
1984 <horstretch>1</horstretch>
1985 <verstretch>1</verstretch>
1986 </sizepolicy>
1987 </property>
1988 <property name="font">
1989 <font>
1990 <pointsize>28</pointsize>
1991 </font>
1992 </property>
1993 <property name="text">
1994 <string notr="true">=</string>
1995 </property>
1996 </widget>
1997 </item>
1998 <item row="1" column="7">
1999 <widget class="QPushButton" name="button_ampersand">
2000 <property name="sizePolicy">
2001 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
2002 <horstretch>1</horstretch>
2003 <verstretch>1</verstretch>
2004 </sizepolicy>
2005 </property>
2006 <property name="font">
2007 <font>
2008 <pointsize>28</pointsize>
2009 </font>
2010 </property>
2011 <property name="text">
2012 <string notr="true">&amp;&amp;</string>
2013 </property>
2014 </widget>
2015 </item>
2016 <item row="1" column="8">
2017 <widget class="QPushButton" name="button_asterisk">
2018 <property name="sizePolicy">
2019 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
2020 <horstretch>1</horstretch>
2021 <verstretch>1</verstretch>
2022 </sizepolicy>
2023 </property>
2024 <property name="font">
2025 <font>
2026 <pointsize>28</pointsize>
2027 </font>
2028 </property>
2029 <property name="text">
2030 <string notr="true">*</string>
2031 </property>
2032 </widget>
2033 </item>
2034 <item row="1" column="10">
2035 <widget class="QPushButton" name="button_right_parenthesis">
2036 <property name="sizePolicy">
2037 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
2038 <horstretch>1</horstretch>
2039 <verstretch>1</verstretch>
2040 </sizepolicy>
2041 </property>
2042 <property name="font">
2043 <font>
2044 <pointsize>28</pointsize>
2045 </font>
2046 </property>
2047 <property name="text">
2048 <string notr="true">)</string>
2049 </property>
2050 </widget>
2051 </item>
2052 <item row="1" column="9">
2053 <widget class="QPushButton" name="button_left_parenthesis">
2054 <property name="sizePolicy">
2055 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
2056 <horstretch>1</horstretch>
2057 <verstretch>1</verstretch>
2058 </sizepolicy>
2059 </property>
2060 <property name="font">
2061 <font>
2062 <pointsize>28</pointsize>
2063 </font>
2064 </property>
2065 <property name="text">
2066 <string notr="true">(</string>
2067 </property>
2068 </widget>
2069 </item>
2070 <item row="2" column="2">
2071 <widget class="QPushButton" name="button_w_shift">
2072 <property name="sizePolicy">
2073 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
2074 <horstretch>1</horstretch>
2075 <verstretch>1</verstretch>
2076 </sizepolicy>
2077 </property>
2078 <property name="font">
2079 <font>
2080 <pointsize>28</pointsize>
2081 </font>
2082 </property>
2083 <property name="text">
2084 <string notr="true">W</string>
2085 </property>
2086 </widget>
2087 </item>
2088 <item row="2" column="4">
2089 <widget class="QPushButton" name="button_r_shift">
2090 <property name="sizePolicy">
2091 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
2092 <horstretch>1</horstretch>
2093 <verstretch>1</verstretch>
2094 </sizepolicy>
2095 </property>
2096 <property name="font">
2097 <font>
2098 <pointsize>28</pointsize>
2099 </font>
2100 </property>
2101 <property name="text">
2102 <string notr="true">R</string>
2103 </property>
2104 </widget>
2105 </item>
2106 <item row="2" column="3">
2107 <widget class="QPushButton" name="button_e_shift">
2108 <property name="sizePolicy">
2109 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
2110 <horstretch>1</horstretch>
2111 <verstretch>1</verstretch>
2112 </sizepolicy>
2113 </property>
2114 <property name="font">
2115 <font>
2116 <pointsize>28</pointsize>
2117 </font>
2118 </property>
2119 <property name="text">
2120 <string notr="true">E</string>
2121 </property>
2122 </widget>
2123 </item>
2124 <item row="2" column="1">
2125 <widget class="QPushButton" name="button_q_shift">
2126 <property name="sizePolicy">
2127 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
2128 <horstretch>1</horstretch>
2129 <verstretch>1</verstretch>
2130 </sizepolicy>
2131 </property>
2132 <property name="font">
2133 <font>
2134 <pointsize>28</pointsize>
2135 </font>
2136 </property>
2137 <property name="text">
2138 <string notr="true">Q</string>
2139 </property>
2140 </widget>
2141 </item>
2142 <item row="2" column="7">
2143 <widget class="QPushButton" name="button_u_shift">
2144 <property name="sizePolicy">
2145 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
2146 <horstretch>1</horstretch>
2147 <verstretch>1</verstretch>
2148 </sizepolicy>
2149 </property>
2150 <property name="font">
2151 <font>
2152 <pointsize>28</pointsize>
2153 </font>
2154 </property>
2155 <property name="text">
2156 <string notr="true">U</string>
2157 </property>
2158 </widget>
2159 </item>
2160 <item row="2" column="6">
2161 <widget class="QPushButton" name="button_y_shift">
2162 <property name="sizePolicy">
2163 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
2164 <horstretch>1</horstretch>
2165 <verstretch>1</verstretch>
2166 </sizepolicy>
2167 </property>
2168 <property name="font">
2169 <font>
2170 <pointsize>28</pointsize>
2171 </font>
2172 </property>
2173 <property name="text">
2174 <string notr="true">Y</string>
2175 </property>
2176 </widget>
2177 </item>
2178 <item row="2" column="5">
2179 <widget class="QPushButton" name="button_t_shift">
2180 <property name="sizePolicy">
2181 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
2182 <horstretch>1</horstretch>
2183 <verstretch>1</verstretch>
2184 </sizepolicy>
2185 </property>
2186 <property name="font">
2187 <font>
2188 <pointsize>28</pointsize>
2189 </font>
2190 </property>
2191 <property name="text">
2192 <string notr="true">T</string>
2193 </property>
2194 </widget>
2195 </item>
2196 <item row="2" column="9">
2197 <widget class="QPushButton" name="button_o_shift">
2198 <property name="sizePolicy">
2199 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
2200 <horstretch>1</horstretch>
2201 <verstretch>1</verstretch>
2202 </sizepolicy>
2203 </property>
2204 <property name="font">
2205 <font>
2206 <pointsize>28</pointsize>
2207 </font>
2208 </property>
2209 <property name="text">
2210 <string notr="true">O</string>
2211 </property>
2212 </widget>
2213 </item>
2214 <item row="2" column="10">
2215 <widget class="QPushButton" name="button_p_shift">
2216 <property name="sizePolicy">
2217 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
2218 <horstretch>1</horstretch>
2219 <verstretch>1</verstretch>
2220 </sizepolicy>
2221 </property>
2222 <property name="font">
2223 <font>
2224 <pointsize>28</pointsize>
2225 </font>
2226 </property>
2227 <property name="text">
2228 <string notr="true">P</string>
2229 </property>
2230 </widget>
2231 </item>
2232 <item row="2" column="8">
2233 <widget class="QPushButton" name="button_i_shift">
2234 <property name="sizePolicy">
2235 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
2236 <horstretch>1</horstretch>
2237 <verstretch>1</verstretch>
2238 </sizepolicy>
2239 </property>
2240 <property name="font">
2241 <font>
2242 <pointsize>28</pointsize>
2243 </font>
2244 </property>
2245 <property name="text">
2246 <string notr="true">I</string>
2247 </property>
2248 </widget>
2249 </item>
2250 <item row="3" column="1">
2251 <widget class="QPushButton" name="button_a_shift">
2252 <property name="sizePolicy">
2253 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
2254 <horstretch>1</horstretch>
2255 <verstretch>1</verstretch>
2256 </sizepolicy>
2257 </property>
2258 <property name="font">
2259 <font>
2260 <pointsize>28</pointsize>
2261 </font>
2262 </property>
2263 <property name="text">
2264 <string notr="true">A</string>
2265 </property>
2266 </widget>
2267 </item>
2268 <item row="3" column="2">
2269 <widget class="QPushButton" name="button_s_shift">
2270 <property name="sizePolicy">
2271 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
2272 <horstretch>1</horstretch>
2273 <verstretch>1</verstretch>
2274 </sizepolicy>
2275 </property>
2276 <property name="font">
2277 <font>
2278 <pointsize>28</pointsize>
2279 </font>
2280 </property>
2281 <property name="text">
2282 <string notr="true">S</string>
2283 </property>
2284 </widget>
2285 </item>
2286 <item row="3" column="3">
2287 <widget class="QPushButton" name="button_d_shift">
2288 <property name="sizePolicy">
2289 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
2290 <horstretch>1</horstretch>
2291 <verstretch>1</verstretch>
2292 </sizepolicy>
2293 </property>
2294 <property name="font">
2295 <font>
2296 <pointsize>28</pointsize>
2297 </font>
2298 </property>
2299 <property name="text">
2300 <string notr="true">D</string>
2301 </property>
2302 </widget>
2303 </item>
2304 <item row="3" column="4">
2305 <widget class="QPushButton" name="button_f_shift">
2306 <property name="sizePolicy">
2307 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
2308 <horstretch>1</horstretch>
2309 <verstretch>1</verstretch>
2310 </sizepolicy>
2311 </property>
2312 <property name="font">
2313 <font>
2314 <pointsize>28</pointsize>
2315 </font>
2316 </property>
2317 <property name="text">
2318 <string notr="true">F</string>
2319 </property>
2320 </widget>
2321 </item>
2322 <item row="3" column="6">
2323 <widget class="QPushButton" name="button_h_shift">
2324 <property name="sizePolicy">
2325 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
2326 <horstretch>1</horstretch>
2327 <verstretch>1</verstretch>
2328 </sizepolicy>
2329 </property>
2330 <property name="font">
2331 <font>
2332 <pointsize>28</pointsize>
2333 </font>
2334 </property>
2335 <property name="text">
2336 <string notr="true">H</string>
2337 </property>
2338 </widget>
2339 </item>
2340 <item row="3" column="7">
2341 <widget class="QPushButton" name="button_j_shift">
2342 <property name="sizePolicy">
2343 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
2344 <horstretch>1</horstretch>
2345 <verstretch>1</verstretch>
2346 </sizepolicy>
2347 </property>
2348 <property name="font">
2349 <font>
2350 <pointsize>28</pointsize>
2351 </font>
2352 </property>
2353 <property name="text">
2354 <string notr="true">J</string>
2355 </property>
2356 </widget>
2357 </item>
2358 <item row="3" column="5">
2359 <widget class="QPushButton" name="button_g_shift">
2360 <property name="sizePolicy">
2361 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
2362 <horstretch>1</horstretch>
2363 <verstretch>1</verstretch>
2364 </sizepolicy>
2365 </property>
2366 <property name="font">
2367 <font>
2368 <pointsize>28</pointsize>
2369 </font>
2370 </property>
2371 <property name="text">
2372 <string notr="true">G</string>
2373 </property>
2374 </widget>
2375 </item>
2376 <item row="3" column="8">
2377 <widget class="QPushButton" name="button_k_shift">
2378 <property name="sizePolicy">
2379 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
2380 <horstretch>1</horstretch>
2381 <verstretch>1</verstretch>
2382 </sizepolicy>
2383 </property>
2384 <property name="font">
2385 <font>
2386 <pointsize>28</pointsize>
2387 </font>
2388 </property>
2389 <property name="text">
2390 <string notr="true">K</string>
2391 </property>
2392 </widget>
2393 </item>
2394 <item row="3" column="9">
2395 <widget class="QPushButton" name="button_l_shift">
2396 <property name="sizePolicy">
2397 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
2398 <horstretch>1</horstretch>
2399 <verstretch>1</verstretch>
2400 </sizepolicy>
2401 </property>
2402 <property name="font">
2403 <font>
2404 <pointsize>28</pointsize>
2405 </font>
2406 </property>
2407 <property name="text">
2408 <string notr="true">L</string>
2409 </property>
2410 </widget>
2411 </item>
2412 <item row="3" column="10">
2413 <widget class="QPushButton" name="button_semicolon">
2414 <property name="sizePolicy">
2415 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
2416 <horstretch>1</horstretch>
2417 <verstretch>1</verstretch>
2418 </sizepolicy>
2419 </property>
2420 <property name="font">
2421 <font>
2422 <pointsize>28</pointsize>
2423 </font>
2424 </property>
2425 <property name="text">
2426 <string notr="true">;</string>
2427 </property>
2428 </widget>
2429 </item>
2430 <item row="2" column="12" rowspan="2">
2431 <widget class="QPushButton" name="button_return_shift">
2432 <property name="sizePolicy">
2433 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
2434 <horstretch>1</horstretch>
2435 <verstretch>1</verstretch>
2436 </sizepolicy>
2437 </property>
2438 <property name="font">
2439 <font>
2440 <pointsize>18</pointsize>
2441 </font>
2442 </property>
2443 <property name="text">
2444 <string notr="true">Return</string>
2445 </property>
2446 </widget>
2447 </item>
2448 <item row="4" column="12" rowspan="2">
2449 <widget class="QPushButton" name="button_ok_shift">
2450 <property name="sizePolicy">
2451 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
2452 <horstretch>1</horstretch>
2453 <verstretch>1</verstretch>
2454 </sizepolicy>
2455 </property>
2456 <property name="font">
2457 <font>
2458 <pointsize>18</pointsize>
2459 </font>
2460 </property>
2461 <property name="text">
2462 <string notr="true">OK</string>
2463 </property>
2464 </widget>
2465 </item>
2466 <item row="4" column="1">
2467 <widget class="QPushButton" name="button_z_shift">
2468 <property name="sizePolicy">
2469 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
2470 <horstretch>1</horstretch>
2471 <verstretch>1</verstretch>
2472 </sizepolicy>
2473 </property>
2474 <property name="font">
2475 <font>
2476 <pointsize>28</pointsize>
2477 </font>
2478 </property>
2479 <property name="text">
2480 <string notr="true">Z</string>
2481 </property>
2482 </widget>
2483 </item>
2484 <item row="4" column="3">
2485 <widget class="QPushButton" name="button_c_shift">
2486 <property name="sizePolicy">
2487 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
2488 <horstretch>1</horstretch>
2489 <verstretch>1</verstretch>
2490 </sizepolicy>
2491 </property>
2492 <property name="font">
2493 <font>
2494 <pointsize>28</pointsize>
2495 </font>
2496 </property>
2497 <property name="text">
2498 <string notr="true">C</string>
2499 </property>
2500 </widget>
2501 </item>
2502 <item row="4" column="2">
2503 <widget class="QPushButton" name="button_x_shift">
2504 <property name="sizePolicy">
2505 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
2506 <horstretch>1</horstretch>
2507 <verstretch>1</verstretch>
2508 </sizepolicy>
2509 </property>
2510 <property name="font">
2511 <font>
2512 <pointsize>28</pointsize>
2513 </font>
2514 </property>
2515 <property name="text">
2516 <string notr="true">X</string>
2517 </property>
2518 </widget>
2519 </item>
2520 <item row="4" column="4">
2521 <widget class="QPushButton" name="button_v_shift">
2522 <property name="sizePolicy">
2523 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
2524 <horstretch>1</horstretch>
2525 <verstretch>1</verstretch>
2526 </sizepolicy>
2527 </property>
2528 <property name="font">
2529 <font>
2530 <pointsize>28</pointsize>
2531 </font>
2532 </property>
2533 <property name="text">
2534 <string notr="true">V</string>
2535 </property>
2536 </widget>
2537 </item>
2538 <item row="4" column="7">
2539 <widget class="QPushButton" name="button_m_shift">
2540 <property name="sizePolicy">
2541 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
2542 <horstretch>1</horstretch>
2543 <verstretch>1</verstretch>
2544 </sizepolicy>
2545 </property>
2546 <property name="font">
2547 <font>
2548 <pointsize>28</pointsize>
2549 </font>
2550 </property>
2551 <property name="text">
2552 <string notr="true">M</string>
2553 </property>
2554 </widget>
2555 </item>
2556 <item row="4" column="8">
2557 <widget class="QPushButton" name="button_less_than">
2558 <property name="sizePolicy">
2559 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
2560 <horstretch>1</horstretch>
2561 <verstretch>1</verstretch>
2562 </sizepolicy>
2563 </property>
2564 <property name="font">
2565 <font>
2566 <pointsize>28</pointsize>
2567 </font>
2568 </property>
2569 <property name="text">
2570 <string notr="true">&lt;</string>
2571 </property>
2572 </widget>
2573 </item>
2574 <item row="4" column="6">
2575 <widget class="QPushButton" name="button_n_shift">
2576 <property name="sizePolicy">
2577 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
2578 <horstretch>1</horstretch>
2579 <verstretch>1</verstretch>
2580 </sizepolicy>
2581 </property>
2582 <property name="font">
2583 <font>
2584 <pointsize>28</pointsize>
2585 </font>
2586 </property>
2587 <property name="text">
2588 <string notr="true">N</string>
2589 </property>
2590 </widget>
2591 </item>
2592 <item row="4" column="5">
2593 <widget class="QPushButton" name="button_b_shift">
2594 <property name="sizePolicy">
2595 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
2596 <horstretch>1</horstretch>
2597 <verstretch>1</verstretch>
2598 </sizepolicy>
2599 </property>
2600 <property name="font">
2601 <font>
2602 <pointsize>28</pointsize>
2603 </font>
2604 </property>
2605 <property name="text">
2606 <string notr="true">B</string>
2607 </property>
2608 </widget>
2609 </item>
2610 <item row="5" column="1" colspan="2">
2611 <widget class="QPushButton" name="button_shift_shift">
2612 <property name="sizePolicy">
2613 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
2614 <horstretch>1</horstretch>
2615 <verstretch>1</verstretch>
2616 </sizepolicy>
2617 </property>
2618 <property name="font">
2619 <font>
2620 <pointsize>18</pointsize>
2621 </font>
2622 </property>
2623 <property name="text">
2624 <string notr="true"/>
2625 </property>
2626 <property name="checkable">
2627 <bool>true</bool>
2628 </property>
2629 <property name="checked">
2630 <bool>false</bool>
2631 </property>
2632 </widget>
2633 </item>
2634 <item row="4" column="10">
2635 <widget class="QPushButton" name="button_plus">
2636 <property name="sizePolicy">
2637 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
2638 <horstretch>1</horstretch>
2639 <verstretch>1</verstretch>
2640 </sizepolicy>
2641 </property>
2642 <property name="font">
2643 <font>
2644 <pointsize>28</pointsize>
2645 </font>
2646 </property>
2647 <property name="text">
2648 <string notr="true">+</string>
2649 </property>
2650 </widget>
2651 </item>
2652 <item row="4" column="9">
2653 <widget class="QPushButton" name="button_greater_than">
2654 <property name="sizePolicy">
2655 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
2656 <horstretch>1</horstretch>
2657 <verstretch>1</verstretch>
2658 </sizepolicy>
2659 </property>
2660 <property name="font">
2661 <font>
2662 <pointsize>28</pointsize>
2663 </font>
2664 </property>
2665 <property name="text">
2666 <string notr="true">&gt;</string>
2667 </property>
2668 </widget>
2669 </item>
2670 <item row="1" column="1">
2671 <widget class="QPushButton" name="button_hash">
2672 <property name="sizePolicy">
2673 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
2674 <horstretch>1</horstretch>
2675 <verstretch>1</verstretch>
2676 </sizepolicy>
2677 </property>
2678 <property name="font">
2679 <font>
2680 <pointsize>28</pointsize>
2681 </font>
2682 </property>
2683 <property name="text">
2684 <string notr="true">#</string>
2685 </property>
2686 </widget>
2687 </item>
2688 <item row="1" column="3">
2689 <widget class="QPushButton" name="button_right_bracket">
2690 <property name="sizePolicy">
2691 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
2692 <horstretch>1</horstretch>
2693 <verstretch>1</verstretch>
2694 </sizepolicy>
2695 </property>
2696 <property name="font">
2697 <font>
2698 <pointsize>28</pointsize>
2699 </font>
2700 </property>
2701 <property name="text">
2702 <string notr="true">]</string>
2703 </property>
2704 </widget>
2705 </item>
2706 <item row="1" column="4">
2707 <widget class="QPushButton" name="button_dollar">
2708 <property name="sizePolicy">
2709 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
2710 <horstretch>1</horstretch>
2711 <verstretch>1</verstretch>
2712 </sizepolicy>
2713 </property>
2714 <property name="font">
2715 <font>
2716 <pointsize>28</pointsize>
2717 </font>
2718 </property>
2719 <property name="text">
2720 <string notr="true">$</string>
2721 </property>
2722 </widget>
2723 </item>
2724 <item row="1" column="2">
2725 <widget class="QPushButton" name="button_left_bracket">
2726 <property name="sizePolicy">
2727 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
2728 <horstretch>1</horstretch>
2729 <verstretch>1</verstretch>
2730 </sizepolicy>
2731 </property>
2732 <property name="font">
2733 <font>
2734 <pointsize>28</pointsize>
2735 </font>
2736 </property>
2737 <property name="text">
2738 <string notr="true">[</string>
2739 </property>
2740 </widget>
2741 </item>
2742 <item row="1" column="6">
2743 <widget class="QPushButton" name="button_circumflex">
2744 <property name="sizePolicy">
2745 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
2746 <horstretch>1</horstretch>
2747 <verstretch>1</verstretch>
2748 </sizepolicy>
2749 </property>
2750 <property name="font">
2751 <font>
2752 <pointsize>28</pointsize>
2753 </font>
2754 </property>
2755 <property name="text">
2756 <string notr="true">^</string>
2757 </property>
2758 </widget>
2759 </item>
2760 <item row="1" column="5">
2761 <widget class="QPushButton" name="button_percent">
2762 <property name="sizePolicy">
2763 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
2764 <horstretch>1</horstretch>
2765 <verstretch>1</verstretch>
2766 </sizepolicy>
2767 </property>
2768 <property name="font">
2769 <font>
2770 <pointsize>28</pointsize>
2771 </font>
2772 </property>
2773 <property name="text">
2774 <string notr="true">%</string>
2775 </property>
2776 </widget>
2777 </item>
2778 <item row="5" column="3" colspan="9">
2779 <widget class="QPushButton" name="button_space_shift">
2780 <property name="sizePolicy">
2781 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
2782 <horstretch>1</horstretch>
2783 <verstretch>1</verstretch>
2784 </sizepolicy>
2785 </property>
2786 <property name="font">
2787 <font>
2788 <pointsize>18</pointsize>
2789 </font>
2790 </property>
2791 <property name="text">
2792 <string notr="true">Space</string>
2793 </property>
2794 </widget>
2795 </item>
2796 <item row="1" column="12">
2797 <widget class="QPushButton" name="button_backspace_shift">
2798 <property name="sizePolicy">
2799 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
2800 <horstretch>1</horstretch>
2801 <verstretch>1</verstretch>
2802 </sizepolicy>
2803 </property>
2804 <property name="font">
2805 <font>
2806 <pointsize>18</pointsize>
2807 </font>
2808 </property>
2809 <property name="text">
2810 <string notr="true"/>
2811 </property>
2812 </widget>
2813 </item>
2814 <item row="1" column="0">
2815 <spacer name="horizontalSpacer_33">
2816 <property name="orientation">
2817 <enum>Qt::Horizontal</enum>
2818 </property>
2819 <property name="sizeHint" stdset="0">
2820 <size>
2821 <width>40</width>
2822 <height>20</height>
2823 </size>
2824 </property>
2825 </spacer>
2826 </item>
2827 <item row="0" column="1">
2828 <spacer name="verticalSpacer_6">
2829 <property name="orientation">
2830 <enum>Qt::Vertical</enum>
2831 </property>
2832 <property name="sizeHint" stdset="0">
2833 <size>
2834 <width>20</width>
2835 <height>0</height>
2836 </size>
2837 </property>
2838 </spacer>
2839 </item>
2840 <item row="1" column="13">
2841 <spacer name="horizontalSpacer_34">
2842 <property name="orientation">
2843 <enum>Qt::Horizontal</enum>
2844 </property>
2845 <property name="sizeHint" stdset="0">
2846 <size>
2847 <width>40</width>
2848 <height>20</height>
2849 </size>
2850 </property>
2851 </spacer>
2852 </item>
2853 </layout>
2854 </item>
2855 </layout>
2856 </widget>
2857 <widget class="QWidget" name="numOSK">
2858 <layout class="QVBoxLayout" name="graphicsTabVerticalLayout">
2859 <property name="leftMargin">
2860 <number>0</number>
2861 </property>
2862 <property name="topMargin">
2863 <number>0</number>
2864 </property>
2865 <property name="rightMargin">
2866 <number>0</number>
2867 </property>
2868 <property name="bottomMargin">
2869 <number>0</number>
2870 </property>
2871 <item>
2872 <layout class="QGridLayout" name="kbOSKnum" rowstretch="18,63,63,63,63,10,70" columnstretch="54,307,186,186,186,120,187,54">
2873 <property name="leftMargin">
2874 <number>0</number>
2875 </property>
2876 <property name="rightMargin">
2877 <number>0</number>
2878 </property>
2879 <property name="spacing">
2880 <number>0</number>
2881 </property>
2882 <item row="1" column="5">
2883 <widget class="QPushButton" name="button_backspace_num">
2884 <property name="sizePolicy">
2885 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
2886 <horstretch>1</horstretch>
2887 <verstretch>1</verstretch>
2888 </sizepolicy>
2889 </property>
2890 <property name="font">
2891 <font>
2892 <pointsize>18</pointsize>
2893 </font>
2894 </property>
2895 <property name="text">
2896 <string notr="true"/>
2897 </property>
2898 </widget>
2899 </item>
2900 <item row="1" column="6">
2901 <spacer name="horizontalSpacer_35">
2902 <property name="orientation">
2903 <enum>Qt::Horizontal</enum>
2904 </property>
2905 <property name="sizeHint" stdset="0">
2906 <size>
2907 <width>40</width>
2908 <height>20</height>
2909 </size>
2910 </property>
2911 </spacer>
2912 </item>
2913 <item row="1" column="1">
2914 <spacer name="horizontalSpacer_36">
2915 <property name="orientation">
2916 <enum>Qt::Horizontal</enum>
2917 </property>
2918 <property name="sizeHint" stdset="0">
2919 <size>
2920 <width>40</width>
2921 <height>20</height>
2922 </size>
2923 </property>
2924 </spacer>
2925 </item>
2926 <item row="0" column="2">
2927 <spacer name="verticalSpacer_7">
2928 <property name="orientation">
2929 <enum>Qt::Vertical</enum>
2930 </property>
2931 <property name="sizeHint" stdset="0">
2932 <size>
2933 <width>20</width>
2934 <height>0</height>
2935 </size>
2936 </property>
2937 </spacer>
2938 </item>
2939 <item row="6" column="1" colspan="6">
2940 <widget class="QWidget" name="legendOSKnum" native="true">
2941 <layout class="QHBoxLayout" name="horizontalLayout_6" stretch="25,70,601,25,12,22,41,25,12,22,41,29,12,69,37,29,12,56,30">
2942 <property name="spacing">
2943 <number>0</number>
2944 </property>
2945 <property name="leftMargin">
2946 <number>0</number>
2947 </property>
2948 <property name="topMargin">
2949 <number>0</number>
2950 </property>
2951 <property name="rightMargin">
2952 <number>0</number>
2953 </property>
2954 <property name="bottomMargin">
2955 <number>0</number>
2956 </property>
2957 <item>
2958 <spacer name="horizontalSpacer_48">
2959 <property name="orientation">
2960 <enum>Qt::Horizontal</enum>
2961 </property>
2962 <property name="sizeHint" stdset="0">
2963 <size>
2964 <width>40</width>
2965 <height>20</height>
2966 </size>
2967 </property>
2968 </spacer>
2969 </item>
2970 <item>
2971 <widget class="QWidget" name="icon_controller_num" native="true">
2972 <property name="styleSheet">
2973 <string notr="true"/>
2974 </property>
2975 </widget>
2976 </item>
2977 <item>
2978 <spacer name="horizontalSpacer_38">
2979 <property name="orientation">
2980 <enum>Qt::Horizontal</enum>
2981 </property>
2982 <property name="sizeHint" stdset="0">
2983 <size>
2984 <width>0</width>
2985 <height>20</height>
2986 </size>
2987 </property>
2988 </spacer>
2989 </item>
2990 <item>
2991 <widget class="QWidget" name="button_L_num" native="true"/>
2992 </item>
2993 <item>
2994 <spacer name="horizontalSpacer_39">
2995 <property name="orientation">
2996 <enum>Qt::Horizontal</enum>
2997 </property>
2998 <property name="sizeHint" stdset="0">
2999 <size>
3000 <width>0</width>
3001 <height>20</height>
3002 </size>
3003 </property>
3004 </spacer>
3005 </item>
3006 <item>
3007 <widget class="QWidget" name="arrow_left_num" native="true"/>
3008 </item>
3009 <item>
3010 <spacer name="horizontalSpacer_40">
3011 <property name="orientation">
3012 <enum>Qt::Horizontal</enum>
3013 </property>
3014 <property name="sizeHint" stdset="0">
3015 <size>
3016 <width>0</width>
3017 <height>20</height>
3018 </size>
3019 </property>
3020 </spacer>
3021 </item>
3022 <item>
3023 <widget class="QWidget" name="button_R_num" native="true"/>
3024 </item>
3025 <item>
3026 <spacer name="horizontalSpacer_41">
3027 <property name="orientation">
3028 <enum>Qt::Horizontal</enum>
3029 </property>
3030 <property name="sizeHint" stdset="0">
3031 <size>
3032 <width>0</width>
3033 <height>20</height>
3034 </size>
3035 </property>
3036 </spacer>
3037 </item>
3038 <item>
3039 <widget class="QWidget" name="arrow_right_num" native="true"/>
3040 </item>
3041 <item>
3042 <spacer name="horizontalSpacer_42">
3043 <property name="orientation">
3044 <enum>Qt::Horizontal</enum>
3045 </property>
3046 <property name="sizeHint" stdset="0">
3047 <size>
3048 <width>0</width>
3049 <height>20</height>
3050 </size>
3051 </property>
3052 </spacer>
3053 </item>
3054 <item>
3055 <widget class="QWidget" name="button_X_num" native="true"/>
3056 </item>
3057 <item>
3058 <spacer name="horizontalSpacer_43">
3059 <property name="orientation">
3060 <enum>Qt::Horizontal</enum>
3061 </property>
3062 <property name="sizeHint" stdset="0">
3063 <size>
3064 <width>0</width>
3065 <height>20</height>
3066 </size>
3067 </property>
3068 </spacer>
3069 </item>
3070 <item>
3071 <widget class="QLabel" name="label_cancel_num">
3072 <property name="font">
3073 <font>
3074 <pointsize>18</pointsize>
3075 </font>
3076 </property>
3077 <property name="text">
3078 <string notr="true">Cancel</string>
3079 </property>
3080 </widget>
3081 </item>
3082 <item>
3083 <spacer name="horizontalSpacer_44">
3084 <property name="orientation">
3085 <enum>Qt::Horizontal</enum>
3086 </property>
3087 <property name="sizeHint" stdset="0">
3088 <size>
3089 <width>0</width>
3090 <height>20</height>
3091 </size>
3092 </property>
3093 </spacer>
3094 </item>
3095 <item>
3096 <widget class="QWidget" name="button_A_num" native="true"/>
3097 </item>
3098 <item>
3099 <spacer name="horizontalSpacer_45">
3100 <property name="orientation">
3101 <enum>Qt::Horizontal</enum>
3102 </property>
3103 <property name="sizeHint" stdset="0">
3104 <size>
3105 <width>0</width>
3106 <height>20</height>
3107 </size>
3108 </property>
3109 </spacer>
3110 </item>
3111 <item>
3112 <widget class="QLabel" name="label_enter_num">
3113 <property name="font">
3114 <font>
3115 <pointsize>18</pointsize>
3116 </font>
3117 </property>
3118 <property name="text">
3119 <string notr="true">Enter</string>
3120 </property>
3121 </widget>
3122 </item>
3123 <item>
3124 <spacer name="horizontalSpacer_46">
3125 <property name="orientation">
3126 <enum>Qt::Horizontal</enum>
3127 </property>
3128 <property name="sizeHint" stdset="0">
3129 <size>
3130 <width>0</width>
3131 <height>20</height>
3132 </size>
3133 </property>
3134 </spacer>
3135 </item>
3136 </layout>
3137 </widget>
3138 </item>
3139 <item row="2" column="4">
3140 <widget class="QPushButton" name="button_6_num">
3141 <property name="sizePolicy">
3142 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
3143 <horstretch>1</horstretch>
3144 <verstretch>1</verstretch>
3145 </sizepolicy>
3146 </property>
3147 <property name="font">
3148 <font>
3149 <pointsize>28</pointsize>
3150 </font>
3151 </property>
3152 <property name="text">
3153 <string notr="true">6</string>
3154 </property>
3155 </widget>
3156 </item>
3157 <item row="2" column="2">
3158 <widget class="QPushButton" name="button_4_num">
3159 <property name="sizePolicy">
3160 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
3161 <horstretch>1</horstretch>
3162 <verstretch>1</verstretch>
3163 </sizepolicy>
3164 </property>
3165 <property name="font">
3166 <font>
3167 <pointsize>28</pointsize>
3168 </font>
3169 </property>
3170 <property name="text">
3171 <string notr="true">4</string>
3172 </property>
3173 </widget>
3174 </item>
3175 <item row="3" column="4">
3176 <widget class="QPushButton" name="button_9_num">
3177 <property name="sizePolicy">
3178 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
3179 <horstretch>1</horstretch>
3180 <verstretch>1</verstretch>
3181 </sizepolicy>
3182 </property>
3183 <property name="font">
3184 <font>
3185 <pointsize>28</pointsize>
3186 </font>
3187 </property>
3188 <property name="text">
3189 <string notr="true">9</string>
3190 </property>
3191 </widget>
3192 </item>
3193 <item row="2" column="3">
3194 <widget class="QPushButton" name="button_5_num">
3195 <property name="sizePolicy">
3196 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
3197 <horstretch>1</horstretch>
3198 <verstretch>1</verstretch>
3199 </sizepolicy>
3200 </property>
3201 <property name="font">
3202 <font>
3203 <pointsize>28</pointsize>
3204 </font>
3205 </property>
3206 <property name="text">
3207 <string notr="true">5</string>
3208 </property>
3209 </widget>
3210 </item>
3211 <item row="2" column="5" rowspan="3">
3212 <widget class="QPushButton" name="button_ok_num">
3213 <property name="sizePolicy">
3214 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
3215 <horstretch>1</horstretch>
3216 <verstretch>1</verstretch>
3217 </sizepolicy>
3218 </property>
3219 <property name="font">
3220 <font>
3221 <pointsize>18</pointsize>
3222 </font>
3223 </property>
3224 <property name="text">
3225 <string notr="true">OK</string>
3226 </property>
3227 </widget>
3228 </item>
3229 <item row="3" column="2">
3230 <widget class="QPushButton" name="button_7_num">
3231 <property name="sizePolicy">
3232 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
3233 <horstretch>1</horstretch>
3234 <verstretch>1</verstretch>
3235 </sizepolicy>
3236 </property>
3237 <property name="font">
3238 <font>
3239 <pointsize>28</pointsize>
3240 </font>
3241 </property>
3242 <property name="text">
3243 <string notr="true">7</string>
3244 </property>
3245 </widget>
3246 </item>
3247 <item row="3" column="3">
3248 <widget class="QPushButton" name="button_8_num">
3249 <property name="sizePolicy">
3250 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
3251 <horstretch>1</horstretch>
3252 <verstretch>1</verstretch>
3253 </sizepolicy>
3254 </property>
3255 <property name="font">
3256 <font>
3257 <pointsize>28</pointsize>
3258 </font>
3259 </property>
3260 <property name="text">
3261 <string notr="true">8</string>
3262 </property>
3263 </widget>
3264 </item>
3265 <item row="1" column="3">
3266 <widget class="QPushButton" name="button_2_num">
3267 <property name="sizePolicy">
3268 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
3269 <horstretch>1</horstretch>
3270 <verstretch>1</verstretch>
3271 </sizepolicy>
3272 </property>
3273 <property name="font">
3274 <font>
3275 <pointsize>28</pointsize>
3276 </font>
3277 </property>
3278 <property name="text">
3279 <string notr="true">2</string>
3280 </property>
3281 </widget>
3282 </item>
3283 <item row="1" column="2">
3284 <widget class="QPushButton" name="button_1_num">
3285 <property name="sizePolicy">
3286 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
3287 <horstretch>1</horstretch>
3288 <verstretch>1</verstretch>
3289 </sizepolicy>
3290 </property>
3291 <property name="font">
3292 <font>
3293 <pointsize>28</pointsize>
3294 </font>
3295 </property>
3296 <property name="text">
3297 <string notr="true">1</string>
3298 </property>
3299 </widget>
3300 </item>
3301 <item row="4" column="3">
3302 <widget class="QPushButton" name="button_0_num">
3303 <property name="sizePolicy">
3304 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
3305 <horstretch>1</horstretch>
3306 <verstretch>1</verstretch>
3307 </sizepolicy>
3308 </property>
3309 <property name="font">
3310 <font>
3311 <pointsize>28</pointsize>
3312 </font>
3313 </property>
3314 <property name="text">
3315 <string notr="true">0</string>
3316 </property>
3317 </widget>
3318 </item>
3319 <item row="1" column="4">
3320 <widget class="QPushButton" name="button_3_num">
3321 <property name="sizePolicy">
3322 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
3323 <horstretch>1</horstretch>
3324 <verstretch>1</verstretch>
3325 </sizepolicy>
3326 </property>
3327 <property name="font">
3328 <font>
3329 <pointsize>28</pointsize>
3330 </font>
3331 </property>
3332 <property name="text">
3333 <string notr="true">3</string>
3334 </property>
3335 </widget>
3336 </item>
3337 <item row="1" column="0">
3338 <spacer name="horizontalSpacer_37">
3339 <property name="orientation">
3340 <enum>Qt::Horizontal</enum>
3341 </property>
3342 <property name="sizeHint" stdset="0">
3343 <size>
3344 <width>40</width>
3345 <height>20</height>
3346 </size>
3347 </property>
3348 </spacer>
3349 </item>
3350 <item row="1" column="7">
3351 <spacer name="horizontalSpacer_47">
3352 <property name="orientation">
3353 <enum>Qt::Horizontal</enum>
3354 </property>
3355 <property name="sizeHint" stdset="0">
3356 <size>
3357 <width>40</width>
3358 <height>20</height>
3359 </size>
3360 </property>
3361 </spacer>
3362 </item>
3363 <item row="5" column="3">
3364 <spacer name="verticalSpacer_8">
3365 <property name="orientation">
3366 <enum>Qt::Vertical</enum>
3367 </property>
3368 <property name="sizeHint" stdset="0">
3369 <size>
3370 <width>20</width>
3371 <height>0</height>
3372 </size>
3373 </property>
3374 </spacer>
3375 </item>
3376 </layout>
3377 </item>
3378 </layout>
3379 </widget>
3380 </widget>
3381 </item>
3382 </layout>
3383 </widget>
3384 </item>
3385 </layout>
3386 </widget>
3387 <tabstops>
3388 <tabstop>button_1</tabstop>
3389 <tabstop>button_2</tabstop>
3390 <tabstop>button_3</tabstop>
3391 <tabstop>button_4</tabstop>
3392 <tabstop>button_5</tabstop>
3393 <tabstop>button_6</tabstop>
3394 <tabstop>button_7</tabstop>
3395 <tabstop>button_8</tabstop>
3396 <tabstop>button_9</tabstop>
3397 <tabstop>button_0</tabstop>
3398 <tabstop>button_minus</tabstop>
3399 <tabstop>button_backspace</tabstop>
3400 <tabstop>button_q</tabstop>
3401 <tabstop>button_w</tabstop>
3402 <tabstop>button_e</tabstop>
3403 <tabstop>button_r</tabstop>
3404 <tabstop>button_t</tabstop>
3405 <tabstop>button_y</tabstop>
3406 <tabstop>button_u</tabstop>
3407 <tabstop>button_i</tabstop>
3408 <tabstop>button_o</tabstop>
3409 <tabstop>button_p</tabstop>
3410 <tabstop>button_slash</tabstop>
3411 <tabstop>button_return</tabstop>
3412 <tabstop>button_a</tabstop>
3413 <tabstop>button_s</tabstop>
3414 <tabstop>button_d</tabstop>
3415 <tabstop>button_f</tabstop>
3416 <tabstop>button_g</tabstop>
3417 <tabstop>button_h</tabstop>
3418 <tabstop>button_j</tabstop>
3419 <tabstop>button_k</tabstop>
3420 <tabstop>button_l</tabstop>
3421 <tabstop>button_colon</tabstop>
3422 <tabstop>button_apostrophe</tabstop>
3423 <tabstop>button_z</tabstop>
3424 <tabstop>button_x</tabstop>
3425 <tabstop>button_c</tabstop>
3426 <tabstop>button_v</tabstop>
3427 <tabstop>button_b</tabstop>
3428 <tabstop>button_n</tabstop>
3429 <tabstop>button_m</tabstop>
3430 <tabstop>button_comma</tabstop>
3431 <tabstop>button_dot</tabstop>
3432 <tabstop>button_question</tabstop>
3433 <tabstop>button_exclamation</tabstop>
3434 <tabstop>button_ok</tabstop>
3435 <tabstop>button_shift</tabstop>
3436 <tabstop>button_space</tabstop>
3437 <tabstop>button_hash</tabstop>
3438 <tabstop>button_left_bracket</tabstop>
3439 <tabstop>button_right_bracket</tabstop>
3440 <tabstop>button_dollar</tabstop>
3441 <tabstop>button_percent</tabstop>
3442 <tabstop>button_circumflex</tabstop>
3443 <tabstop>button_ampersand</tabstop>
3444 <tabstop>button_asterisk</tabstop>
3445 <tabstop>button_left_parenthesis</tabstop>
3446 <tabstop>button_right_parenthesis</tabstop>
3447 <tabstop>button_underscore</tabstop>
3448 <tabstop>button_backspace_shift</tabstop>
3449 <tabstop>button_q_shift</tabstop>
3450 <tabstop>button_w_shift</tabstop>
3451 <tabstop>button_e_shift</tabstop>
3452 <tabstop>button_r_shift</tabstop>
3453 <tabstop>button_t_shift</tabstop>
3454 <tabstop>button_y_shift</tabstop>
3455 <tabstop>button_u_shift</tabstop>
3456 <tabstop>button_i_shift</tabstop>
3457 <tabstop>button_o_shift</tabstop>
3458 <tabstop>button_p_shift</tabstop>
3459 <tabstop>button_at</tabstop>
3460 <tabstop>button_return_shift</tabstop>
3461 <tabstop>button_a_shift</tabstop>
3462 <tabstop>button_s_shift</tabstop>
3463 <tabstop>button_d_shift</tabstop>
3464 <tabstop>button_f_shift</tabstop>
3465 <tabstop>button_g_shift</tabstop>
3466 <tabstop>button_h_shift</tabstop>
3467 <tabstop>button_j_shift</tabstop>
3468 <tabstop>button_k_shift</tabstop>
3469 <tabstop>button_l_shift</tabstop>
3470 <tabstop>button_semicolon</tabstop>
3471 <tabstop>button_quotation</tabstop>
3472 <tabstop>button_z_shift</tabstop>
3473 <tabstop>button_x_shift</tabstop>
3474 <tabstop>button_c_shift</tabstop>
3475 <tabstop>button_v_shift</tabstop>
3476 <tabstop>button_b_shift</tabstop>
3477 <tabstop>button_n_shift</tabstop>
3478 <tabstop>button_m_shift</tabstop>
3479 <tabstop>button_less_than</tabstop>
3480 <tabstop>button_greater_than</tabstop>
3481 <tabstop>button_plus</tabstop>
3482 <tabstop>button_equal</tabstop>
3483 <tabstop>button_ok_shift</tabstop>
3484 <tabstop>button_shift_shift</tabstop>
3485 <tabstop>button_space_shift</tabstop>
3486 <tabstop>button_1_num</tabstop>
3487 <tabstop>button_2_num</tabstop>
3488 <tabstop>button_3_num</tabstop>
3489 <tabstop>button_backspace_num</tabstop>
3490 <tabstop>button_4_num</tabstop>
3491 <tabstop>button_5_num</tabstop>
3492 <tabstop>button_6_num</tabstop>
3493 <tabstop>button_ok_num</tabstop>
3494 <tabstop>button_7_num</tabstop>
3495 <tabstop>button_8_num</tabstop>
3496 <tabstop>button_9_num</tabstop>
3497 <tabstop>button_0_num</tabstop>
3498 </tabstops>
3499 <resources>
3500 <include location="../../../dist/icons/overlay/overlay.qrc"/>
3501 </resources>
3502 <connections/>
3503</ui>
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index fbf96be03..5f6cdc0c6 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -101,6 +101,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
101#include "core/perf_stats.h" 101#include "core/perf_stats.h"
102#include "core/telemetry_session.h" 102#include "core/telemetry_session.h"
103#include "input_common/main.h" 103#include "input_common/main.h"
104#include "util/overlay_dialog.h"
104#include "video_core/gpu.h" 105#include "video_core/gpu.h"
105#include "video_core/shader_notify.h" 106#include "video_core/shader_notify.h"
106#include "yuzu/about_dialog.h" 107#include "yuzu/about_dialog.h"
@@ -227,6 +228,8 @@ GMainWindow::GMainWindow()
227 SetDiscordEnabled(UISettings::values.enable_discord_presence); 228 SetDiscordEnabled(UISettings::values.enable_discord_presence);
228 discord_rpc->Update(); 229 discord_rpc->Update();
229 230
231 RegisterMetaTypes();
232
230 InitializeWidgets(); 233 InitializeWidgets();
231 InitializeDebugWidgets(); 234 InitializeDebugWidgets();
232 InitializeRecentFileMenuActions(); 235 InitializeRecentFileMenuActions();
@@ -375,6 +378,55 @@ GMainWindow::~GMainWindow() {
375 delete render_window; 378 delete render_window;
376} 379}
377 380
381void GMainWindow::RegisterMetaTypes() {
382 // Register integral and floating point types
383 qRegisterMetaType<u8>("u8");
384 qRegisterMetaType<u16>("u16");
385 qRegisterMetaType<u32>("u32");
386 qRegisterMetaType<u64>("u64");
387 qRegisterMetaType<u128>("u128");
388 qRegisterMetaType<s8>("s8");
389 qRegisterMetaType<s16>("s16");
390 qRegisterMetaType<s32>("s32");
391 qRegisterMetaType<s64>("s64");
392 qRegisterMetaType<f32>("f32");
393 qRegisterMetaType<f64>("f64");
394
395 // Register string types
396 qRegisterMetaType<std::string>("std::string");
397 qRegisterMetaType<std::wstring>("std::wstring");
398 qRegisterMetaType<std::u8string>("std::u8string");
399 qRegisterMetaType<std::u16string>("std::u16string");
400 qRegisterMetaType<std::u32string>("std::u32string");
401 qRegisterMetaType<std::string_view>("std::string_view");
402 qRegisterMetaType<std::wstring_view>("std::wstring_view");
403 qRegisterMetaType<std::u8string_view>("std::u8string_view");
404 qRegisterMetaType<std::u16string_view>("std::u16string_view");
405 qRegisterMetaType<std::u32string_view>("std::u32string_view");
406
407 // Register applet types
408
409 // Controller Applet
410 qRegisterMetaType<Core::Frontend::ControllerParameters>("Core::Frontend::ControllerParameters");
411
412 // Software Keyboard Applet
413 qRegisterMetaType<Core::Frontend::KeyboardInitializeParameters>(
414 "Core::Frontend::KeyboardInitializeParameters");
415 qRegisterMetaType<Core::Frontend::InlineAppearParameters>(
416 "Core::Frontend::InlineAppearParameters");
417 qRegisterMetaType<Core::Frontend::InlineTextParameters>("Core::Frontend::InlineTextParameters");
418 qRegisterMetaType<Service::AM::Applets::SwkbdResult>("Service::AM::Applets::SwkbdResult");
419 qRegisterMetaType<Service::AM::Applets::SwkbdTextCheckResult>(
420 "Service::AM::Applets::SwkbdTextCheckResult");
421 qRegisterMetaType<Service::AM::Applets::SwkbdReplyType>("Service::AM::Applets::SwkbdReplyType");
422
423 // Web Browser Applet
424 qRegisterMetaType<Service::AM::Applets::WebExitReason>("Service::AM::Applets::WebExitReason");
425
426 // Register loader types
427 qRegisterMetaType<Core::System::ResultStatus>("Core::System::ResultStatus");
428}
429
378void GMainWindow::ControllerSelectorReconfigureControllers( 430void GMainWindow::ControllerSelectorReconfigureControllers(
379 const Core::Frontend::ControllerParameters& parameters) { 431 const Core::Frontend::ControllerParameters& parameters) {
380 QtControllerSelectorDialog dialog(this, parameters, input_subsystem.get()); 432 QtControllerSelectorDialog dialog(this, parameters, input_subsystem.get());
@@ -414,25 +466,112 @@ void GMainWindow::ProfileSelectorSelectProfile() {
414 emit ProfileSelectorFinishedSelection(uuid); 466 emit ProfileSelectorFinishedSelection(uuid);
415} 467}
416 468
417void GMainWindow::SoftwareKeyboardGetText( 469void GMainWindow::SoftwareKeyboardInitialize(
418 const Core::Frontend::SoftwareKeyboardParameters& parameters) { 470 bool is_inline, Core::Frontend::KeyboardInitializeParameters initialize_parameters) {
419 QtSoftwareKeyboardDialog dialog(this, parameters); 471 if (software_keyboard) {
420 dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint | 472 LOG_ERROR(Frontend, "The software keyboard is already initialized!");
421 Qt::WindowTitleHint | Qt::WindowSystemMenuHint | 473 return;
422 Qt::WindowCloseButtonHint); 474 }
423 dialog.setWindowModality(Qt::WindowModal);
424 475
425 if (dialog.exec() == QDialog::Rejected) { 476 software_keyboard = new QtSoftwareKeyboardDialog(render_window, Core::System::GetInstance(),
426 emit SoftwareKeyboardFinishedText(std::nullopt); 477 is_inline, std::move(initialize_parameters));
478
479 if (is_inline) {
480 connect(
481 software_keyboard, &QtSoftwareKeyboardDialog::SubmitInlineText, this,
482 [this](Service::AM::Applets::SwkbdReplyType reply_type, std::u16string submitted_text,
483 s32 cursor_position) {
484 emit SoftwareKeyboardSubmitInlineText(reply_type, submitted_text, cursor_position);
485 },
486 Qt::QueuedConnection);
487 } else {
488 connect(
489 software_keyboard, &QtSoftwareKeyboardDialog::SubmitNormalText, this,
490 [this](Service::AM::Applets::SwkbdResult result, std::u16string submitted_text) {
491 emit SoftwareKeyboardSubmitNormalText(result, submitted_text);
492 },
493 Qt::QueuedConnection);
494 }
495}
496
497void GMainWindow::SoftwareKeyboardShowNormal() {
498 if (!software_keyboard) {
499 LOG_ERROR(Frontend, "The software keyboard is not initialized!");
427 return; 500 return;
428 } 501 }
429 502
430 emit SoftwareKeyboardFinishedText(dialog.GetText()); 503 const auto& layout = render_window->GetFramebufferLayout();
504
505 const auto x = layout.screen.left;
506 const auto y = layout.screen.top;
507 const auto w = layout.screen.GetWidth();
508 const auto h = layout.screen.GetHeight();
509
510 software_keyboard->ShowNormalKeyboard(render_window->mapToGlobal(QPoint(x, y)), QSize(w, h));
431} 511}
432 512
433void GMainWindow::SoftwareKeyboardInvokeCheckDialog(std::u16string error_message) { 513void GMainWindow::SoftwareKeyboardShowTextCheck(
434 QMessageBox::warning(this, tr("Text Check Failed"), QString::fromStdU16String(error_message)); 514 Service::AM::Applets::SwkbdTextCheckResult text_check_result,
435 emit SoftwareKeyboardFinishedCheckDialog(); 515 std::u16string text_check_message) {
516 if (!software_keyboard) {
517 LOG_ERROR(Frontend, "The software keyboard is not initialized!");
518 return;
519 }
520
521 software_keyboard->ShowTextCheckDialog(text_check_result, text_check_message);
522}
523
524void GMainWindow::SoftwareKeyboardShowInline(
525 Core::Frontend::InlineAppearParameters appear_parameters) {
526 if (!software_keyboard) {
527 LOG_ERROR(Frontend, "The software keyboard is not initialized!");
528 return;
529 }
530
531 const auto& layout = render_window->GetFramebufferLayout();
532
533 const auto x =
534 static_cast<int>(layout.screen.left + (0.5f * layout.screen.GetWidth() *
535 ((2.0f * appear_parameters.key_top_translate_x) +
536 (1.0f - appear_parameters.key_top_scale_x))));
537 const auto y =
538 static_cast<int>(layout.screen.top + (layout.screen.GetHeight() *
539 ((2.0f * appear_parameters.key_top_translate_y) +
540 (1.0f - appear_parameters.key_top_scale_y))));
541 const auto w = static_cast<int>(layout.screen.GetWidth() * appear_parameters.key_top_scale_x);
542 const auto h = static_cast<int>(layout.screen.GetHeight() * appear_parameters.key_top_scale_y);
543
544 software_keyboard->ShowInlineKeyboard(std::move(appear_parameters),
545 render_window->mapToGlobal(QPoint(x, y)), QSize(w, h));
546}
547
548void GMainWindow::SoftwareKeyboardHideInline() {
549 if (!software_keyboard) {
550 LOG_ERROR(Frontend, "The software keyboard is not initialized!");
551 return;
552 }
553
554 software_keyboard->HideInlineKeyboard();
555}
556
557void GMainWindow::SoftwareKeyboardInlineTextChanged(
558 Core::Frontend::InlineTextParameters text_parameters) {
559 if (!software_keyboard) {
560 LOG_ERROR(Frontend, "The software keyboard is not initialized!");
561 return;
562 }
563
564 software_keyboard->InlineTextChanged(std::move(text_parameters));
565}
566
567void GMainWindow::SoftwareKeyboardExit() {
568 if (!software_keyboard) {
569 return;
570 }
571
572 software_keyboard->ExitKeyboard();
573
574 software_keyboard = nullptr;
436} 575}
437 576
438void GMainWindow::WebBrowserOpenWebPage(std::string_view main_url, std::string_view additional_args, 577void GMainWindow::WebBrowserOpenWebPage(std::string_view main_url, std::string_view additional_args,
@@ -978,6 +1117,10 @@ void GMainWindow::ConnectWidgetEvents() {
978 connect(this, &GMainWindow::EmulationStopping, render_window, 1117 connect(this, &GMainWindow::EmulationStopping, render_window,
979 &GRenderWindow::OnEmulationStopping); 1118 &GRenderWindow::OnEmulationStopping);
980 1119
1120 // Software Keyboard Applet
1121 connect(this, &GMainWindow::EmulationStarting, this, &GMainWindow::SoftwareKeyboardExit);
1122 connect(this, &GMainWindow::EmulationStopping, this, &GMainWindow::SoftwareKeyboardExit);
1123
981 connect(&status_bar_update_timer, &QTimer::timeout, this, &GMainWindow::UpdateStatusBar); 1124 connect(&status_bar_update_timer, &QTimer::timeout, this, &GMainWindow::UpdateStatusBar);
982} 1125}
983 1126
@@ -2187,15 +2330,6 @@ void GMainWindow::OnStartGame() {
2187 2330
2188 emu_thread->SetRunning(true); 2331 emu_thread->SetRunning(true);
2189 2332
2190 qRegisterMetaType<Core::Frontend::ControllerParameters>("Core::Frontend::ControllerParameters");
2191 qRegisterMetaType<Core::Frontend::SoftwareKeyboardParameters>(
2192 "Core::Frontend::SoftwareKeyboardParameters");
2193 qRegisterMetaType<Core::System::ResultStatus>("Core::System::ResultStatus");
2194 qRegisterMetaType<std::string>("std::string");
2195 qRegisterMetaType<std::optional<std::u16string>>("std::optional<std::u16string>");
2196 qRegisterMetaType<std::string_view>("std::string_view");
2197 qRegisterMetaType<Service::AM::Applets::WebExitReason>("Service::AM::Applets::WebExitReason");
2198
2199 connect(emu_thread.get(), &EmuThread::ErrorThrown, this, &GMainWindow::OnCoreError); 2333 connect(emu_thread.get(), &EmuThread::ErrorThrown, this, &GMainWindow::OnCoreError);
2200 2334
2201 ui.action_Start->setEnabled(false); 2335 ui.action_Start->setEnabled(false);
@@ -2244,8 +2378,11 @@ void GMainWindow::OnExecuteProgram(std::size_t program_index) {
2244 BootGame(last_filename_booted, program_index); 2378 BootGame(last_filename_booted, program_index);
2245} 2379}
2246 2380
2247void GMainWindow::ErrorDisplayDisplayError(QString body) { 2381void GMainWindow::ErrorDisplayDisplayError(QString error_code, QString error_text) {
2248 QMessageBox::critical(this, tr("Error Display"), body); 2382 OverlayDialog dialog(render_window, Core::System::GetInstance(), error_code, error_text,
2383 QString{}, tr("OK"), Qt::AlignLeft | Qt::AlignVCenter);
2384 dialog.exec();
2385
2249 emit ErrorDisplayFinished(); 2386 emit ErrorDisplayFinished();
2250} 2387}
2251 2388
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 04d37d4ae..7f1e50a5b 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -37,9 +37,13 @@ enum class GameListRemoveTarget;
37enum class InstalledEntryType; 37enum class InstalledEntryType;
38class GameListPlaceholder; 38class GameListPlaceholder;
39 39
40class QtSoftwareKeyboardDialog;
41
40namespace Core::Frontend { 42namespace Core::Frontend {
41struct ControllerParameters; 43struct ControllerParameters;
42struct SoftwareKeyboardParameters; 44struct InlineAppearParameters;
45struct InlineTextParameters;
46struct KeyboardInitializeParameters;
43} // namespace Core::Frontend 47} // namespace Core::Frontend
44 48
45namespace DiscordRPC { 49namespace DiscordRPC {
@@ -57,8 +61,11 @@ class InputSubsystem;
57} 61}
58 62
59namespace Service::AM::Applets { 63namespace Service::AM::Applets {
64enum class SwkbdResult : u32;
65enum class SwkbdTextCheckResult : u32;
66enum class SwkbdReplyType : u32;
60enum class WebExitReason : u32; 67enum class WebExitReason : u32;
61} 68} // namespace Service::AM::Applets
62 69
63enum class EmulatedDirectoryTarget { 70enum class EmulatedDirectoryTarget {
64 NAND, 71 NAND,
@@ -128,8 +135,10 @@ signals:
128 135
129 void ProfileSelectorFinishedSelection(std::optional<Common::UUID> uuid); 136 void ProfileSelectorFinishedSelection(std::optional<Common::UUID> uuid);
130 137
131 void SoftwareKeyboardFinishedText(std::optional<std::u16string> text); 138 void SoftwareKeyboardSubmitNormalText(Service::AM::Applets::SwkbdResult result,
132 void SoftwareKeyboardFinishedCheckDialog(); 139 std::u16string submitted_text);
140 void SoftwareKeyboardSubmitInlineText(Service::AM::Applets::SwkbdReplyType reply_type,
141 std::u16string submitted_text, s32 cursor_position);
133 142
134 void WebBrowserExtractOfflineRomFS(); 143 void WebBrowserExtractOfflineRomFS();
135 void WebBrowserClosed(Service::AM::Applets::WebExitReason exit_reason, std::string last_url); 144 void WebBrowserClosed(Service::AM::Applets::WebExitReason exit_reason, std::string last_url);
@@ -139,15 +148,24 @@ public slots:
139 void OnExecuteProgram(std::size_t program_index); 148 void OnExecuteProgram(std::size_t program_index);
140 void ControllerSelectorReconfigureControllers( 149 void ControllerSelectorReconfigureControllers(
141 const Core::Frontend::ControllerParameters& parameters); 150 const Core::Frontend::ControllerParameters& parameters);
142 void ErrorDisplayDisplayError(QString body); 151 void SoftwareKeyboardInitialize(
152 bool is_inline, Core::Frontend::KeyboardInitializeParameters initialize_parameters);
153 void SoftwareKeyboardShowNormal();
154 void SoftwareKeyboardShowTextCheck(Service::AM::Applets::SwkbdTextCheckResult text_check_result,
155 std::u16string text_check_message);
156 void SoftwareKeyboardShowInline(Core::Frontend::InlineAppearParameters appear_parameters);
157 void SoftwareKeyboardHideInline();
158 void SoftwareKeyboardInlineTextChanged(Core::Frontend::InlineTextParameters text_parameters);
159 void SoftwareKeyboardExit();
160 void ErrorDisplayDisplayError(QString error_code, QString error_text);
143 void ProfileSelectorSelectProfile(); 161 void ProfileSelectorSelectProfile();
144 void SoftwareKeyboardGetText(const Core::Frontend::SoftwareKeyboardParameters& parameters);
145 void SoftwareKeyboardInvokeCheckDialog(std::u16string error_message);
146 void WebBrowserOpenWebPage(std::string_view main_url, std::string_view additional_args, 162 void WebBrowserOpenWebPage(std::string_view main_url, std::string_view additional_args,
147 bool is_local); 163 bool is_local);
148 void OnAppFocusStateChanged(Qt::ApplicationState state); 164 void OnAppFocusStateChanged(Qt::ApplicationState state);
149 165
150private: 166private:
167 void RegisterMetaTypes();
168
151 void InitializeWidgets(); 169 void InitializeWidgets();
152 void InitializeDebugWidgets(); 170 void InitializeDebugWidgets();
153 void InitializeRecentFileMenuActions(); 171 void InitializeRecentFileMenuActions();
@@ -334,6 +352,9 @@ private:
334 // Disables the web applet for the rest of the emulated session 352 // Disables the web applet for the rest of the emulated session
335 bool disable_web_applet{}; 353 bool disable_web_applet{};
336 354
355 // Applets
356 QtSoftwareKeyboardDialog* software_keyboard = nullptr;
357
337protected: 358protected:
338 void dropEvent(QDropEvent* event) override; 359 void dropEvent(QDropEvent* event) override;
339 void dragEnterEvent(QDragEnterEvent* event) override; 360 void dragEnterEvent(QDragEnterEvent* event) override;
diff --git a/src/yuzu/util/overlay_dialog.cpp b/src/yuzu/util/overlay_dialog.cpp
new file mode 100644
index 000000000..95b148545
--- /dev/null
+++ b/src/yuzu/util/overlay_dialog.cpp
@@ -0,0 +1,249 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <QKeyEvent>
6#include <QScreen>
7
8#include "core/core.h"
9#include "core/frontend/input_interpreter.h"
10#include "ui_overlay_dialog.h"
11#include "yuzu/util/overlay_dialog.h"
12
13namespace {
14
15constexpr float BASE_TITLE_FONT_SIZE = 14.0f;
16constexpr float BASE_FONT_SIZE = 18.0f;
17constexpr float BASE_WIDTH = 1280.0f;
18constexpr float BASE_HEIGHT = 720.0f;
19
20} // Anonymous namespace
21
22OverlayDialog::OverlayDialog(QWidget* parent, Core::System& system, const QString& title_text,
23 const QString& body_text, const QString& left_button_text,
24 const QString& right_button_text, Qt::Alignment alignment,
25 bool use_rich_text_)
26 : QDialog(parent), ui{std::make_unique<Ui::OverlayDialog>()}, use_rich_text{use_rich_text_} {
27 ui->setupUi(this);
28
29 setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint | Qt::WindowTitleHint |
30 Qt::WindowSystemMenuHint | Qt::CustomizeWindowHint);
31 setWindowModality(Qt::WindowModal);
32 setAttribute(Qt::WA_TranslucentBackground);
33
34 if (use_rich_text) {
35 InitializeRichTextDialog(title_text, body_text, left_button_text, right_button_text,
36 alignment);
37 } else {
38 InitializeRegularTextDialog(title_text, body_text, left_button_text, right_button_text,
39 alignment);
40 }
41
42 MoveAndResizeWindow();
43
44 // TODO (Morph): Remove this when InputInterpreter no longer relies on the HID backend
45 if (system.IsPoweredOn()) {
46 input_interpreter = std::make_unique<InputInterpreter>(system);
47
48 StartInputThread();
49 }
50}
51
52OverlayDialog::~OverlayDialog() {
53 StopInputThread();
54}
55
56void OverlayDialog::InitializeRegularTextDialog(const QString& title_text, const QString& body_text,
57 const QString& left_button_text,
58 const QString& right_button_text,
59 Qt::Alignment alignment) {
60 ui->stackedDialog->setCurrentIndex(0);
61
62 ui->label_title->setText(title_text);
63 ui->label_dialog->setText(body_text);
64 ui->button_cancel->setText(left_button_text);
65 ui->button_ok_label->setText(right_button_text);
66
67 ui->label_dialog->setAlignment(alignment);
68
69 if (title_text.isEmpty()) {
70 ui->label_title->hide();
71 ui->verticalLayout_2->setStretch(0, 0);
72 ui->verticalLayout_2->setStretch(1, 219);
73 ui->verticalLayout_2->setStretch(2, 82);
74 }
75
76 if (left_button_text.isEmpty()) {
77 ui->button_cancel->hide();
78 ui->button_cancel->setEnabled(false);
79 }
80
81 if (right_button_text.isEmpty()) {
82 ui->button_ok_label->hide();
83 ui->button_ok_label->setEnabled(false);
84 }
85
86 connect(
87 ui->button_cancel, &QPushButton::clicked, this,
88 [this](bool) {
89 StopInputThread();
90 QDialog::reject();
91 },
92 Qt::QueuedConnection);
93 connect(
94 ui->button_ok_label, &QPushButton::clicked, this,
95 [this](bool) {
96 StopInputThread();
97 QDialog::accept();
98 },
99 Qt::QueuedConnection);
100}
101
102void OverlayDialog::InitializeRichTextDialog(const QString& title_text, const QString& body_text,
103 const QString& left_button_text,
104 const QString& right_button_text,
105 Qt::Alignment alignment) {
106 ui->stackedDialog->setCurrentIndex(1);
107
108 ui->label_title_rich->setText(title_text);
109 ui->text_browser_dialog->setText(body_text);
110 ui->button_cancel_rich->setText(left_button_text);
111 ui->button_ok_rich->setText(right_button_text);
112
113 // TODO (Morph/Rei): Replace this with something that works better
114 ui->text_browser_dialog->setAlignment(alignment);
115
116 if (title_text.isEmpty()) {
117 ui->label_title_rich->hide();
118 ui->verticalLayout_3->setStretch(0, 0);
119 ui->verticalLayout_3->setStretch(1, 438);
120 ui->verticalLayout_3->setStretch(2, 82);
121 }
122
123 if (left_button_text.isEmpty()) {
124 ui->button_cancel_rich->hide();
125 ui->button_cancel_rich->setEnabled(false);
126 }
127
128 if (right_button_text.isEmpty()) {
129 ui->button_ok_rich->hide();
130 ui->button_ok_rich->setEnabled(false);
131 }
132
133 connect(
134 ui->button_cancel_rich, &QPushButton::clicked, this,
135 [this](bool) {
136 StopInputThread();
137 QDialog::reject();
138 },
139 Qt::QueuedConnection);
140 connect(
141 ui->button_ok_rich, &QPushButton::clicked, this,
142 [this](bool) {
143 StopInputThread();
144 QDialog::accept();
145 },
146 Qt::QueuedConnection);
147}
148
149void OverlayDialog::MoveAndResizeWindow() {
150 const auto pos = parentWidget()->mapToGlobal(parentWidget()->rect().topLeft());
151 const auto width = static_cast<float>(parentWidget()->width());
152 const auto height = static_cast<float>(parentWidget()->height());
153
154 // High DPI
155 const float dpi_scale = qApp->screenAt(pos)->logicalDotsPerInch() / 96.0f;
156
157 const auto title_text_font_size = BASE_TITLE_FONT_SIZE * (height / BASE_HEIGHT) / dpi_scale;
158 const auto body_text_font_size =
159 BASE_FONT_SIZE * (((width / BASE_WIDTH) + (height / BASE_HEIGHT)) / 2.0f) / dpi_scale;
160 const auto button_text_font_size = BASE_FONT_SIZE * (height / BASE_HEIGHT) / dpi_scale;
161
162 QFont title_text_font(QStringLiteral("MS Shell Dlg 2"), title_text_font_size, QFont::Normal);
163 QFont body_text_font(QStringLiteral("MS Shell Dlg 2"), body_text_font_size, QFont::Normal);
164 QFont button_text_font(QStringLiteral("MS Shell Dlg 2"), button_text_font_size, QFont::Normal);
165
166 if (use_rich_text) {
167 ui->label_title_rich->setFont(title_text_font);
168 ui->text_browser_dialog->setFont(body_text_font);
169 ui->button_cancel_rich->setFont(button_text_font);
170 ui->button_ok_rich->setFont(button_text_font);
171 } else {
172 ui->label_title->setFont(title_text_font);
173 ui->label_dialog->setFont(body_text_font);
174 ui->button_cancel->setFont(button_text_font);
175 ui->button_ok_label->setFont(button_text_font);
176 }
177
178 QDialog::move(pos);
179 QDialog::resize(width, height);
180}
181
182template <HIDButton... T>
183void OverlayDialog::HandleButtonPressedOnce() {
184 const auto f = [this](HIDButton button) {
185 if (input_interpreter->IsButtonPressedOnce(button)) {
186 TranslateButtonPress(button);
187 }
188 };
189
190 (f(T), ...);
191}
192
193void OverlayDialog::TranslateButtonPress(HIDButton button) {
194 QPushButton* left_button = use_rich_text ? ui->button_cancel_rich : ui->button_cancel;
195 QPushButton* right_button = use_rich_text ? ui->button_ok_rich : ui->button_ok_label;
196
197 // TODO (Morph): Handle QTextBrowser text scrolling
198 // TODO (Morph): focusPrevious/NextChild() doesn't work well with the rich text dialog, fix it
199
200 switch (button) {
201 case HIDButton::A:
202 case HIDButton::B:
203 if (left_button->hasFocus()) {
204 left_button->click();
205 } else if (right_button->hasFocus()) {
206 right_button->click();
207 }
208 break;
209 case HIDButton::DLeft:
210 case HIDButton::LStickLeft:
211 focusPreviousChild();
212 break;
213 case HIDButton::DRight:
214 case HIDButton::LStickRight:
215 focusNextChild();
216 break;
217 default:
218 break;
219 }
220}
221
222void OverlayDialog::StartInputThread() {
223 if (input_thread_running) {
224 return;
225 }
226
227 input_thread_running = true;
228
229 input_thread = std::thread(&OverlayDialog::InputThread, this);
230}
231
232void OverlayDialog::StopInputThread() {
233 input_thread_running = false;
234
235 if (input_thread.joinable()) {
236 input_thread.join();
237 }
238}
239
240void OverlayDialog::InputThread() {
241 while (input_thread_running) {
242 input_interpreter->PollInput();
243
244 HandleButtonPressedOnce<HIDButton::A, HIDButton::B, HIDButton::DLeft, HIDButton::DRight,
245 HIDButton::LStickLeft, HIDButton::LStickRight>();
246
247 std::this_thread::sleep_for(std::chrono::milliseconds(50));
248 }
249}
diff --git a/src/yuzu/util/overlay_dialog.h b/src/yuzu/util/overlay_dialog.h
new file mode 100644
index 000000000..e8c388bd0
--- /dev/null
+++ b/src/yuzu/util/overlay_dialog.h
@@ -0,0 +1,107 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include <atomic>
9#include <memory>
10#include <thread>
11
12#include <QDialog>
13
14#include "common/common_types.h"
15
16enum class HIDButton : u8;
17
18class InputInterpreter;
19
20namespace Core {
21class System;
22}
23
24namespace Ui {
25class OverlayDialog;
26}
27
28/**
29 * An OverlayDialog is an interactive dialog that accepts controller input (while a game is running)
30 * This dialog attempts to replicate the look and feel of the Nintendo Switch's overlay dialogs and
31 * provide some extra features such as embedding HTML/Rich Text content in a QTextBrowser.
32 * The OverlayDialog provides 2 modes: one to embed regular text into a QLabel and another to embed
33 * HTML/Rich Text content into a QTextBrowser.
34 */
35class OverlayDialog final : public QDialog {
36 Q_OBJECT
37
38public:
39 explicit OverlayDialog(QWidget* parent, Core::System& system, const QString& title_text,
40 const QString& body_text, const QString& left_button_text,
41 const QString& right_button_text,
42 Qt::Alignment alignment = Qt::AlignCenter, bool use_rich_text_ = false);
43 ~OverlayDialog() override;
44
45private:
46 /**
47 * Initializes a text dialog with a QLabel storing text.
48 * Only use this for short text as the dialog buttons would be squashed with longer text.
49 *
50 * @param title_text Title text to be displayed
51 * @param body_text Main text to be displayed
52 * @param left_button_text Left button text. If empty, the button is hidden and disabled
53 * @param right_button_text Right button text. If empty, the button is hidden and disabled
54 * @param alignment Main text alignment
55 */
56 void InitializeRegularTextDialog(const QString& title_text, const QString& body_text,
57 const QString& left_button_text,
58 const QString& right_button_text, Qt::Alignment alignment);
59
60 /**
61 * Initializes a text dialog with a QTextBrowser storing text.
62 * This is ideal for longer text or rich text content. A scrollbar is shown for longer text.
63 *
64 * @param title_text Title text to be displayed
65 * @param body_text Main text to be displayed
66 * @param left_button_text Left button text. If empty, the button is hidden and disabled
67 * @param right_button_text Right button text. If empty, the button is hidden and disabled
68 * @param alignment Main text alignment
69 */
70 void InitializeRichTextDialog(const QString& title_text, const QString& body_text,
71 const QString& left_button_text, const QString& right_button_text,
72 Qt::Alignment alignment);
73
74 /// Moves and resizes the dialog to be fully overlayed on top of the parent window.
75 void MoveAndResizeWindow();
76
77 /**
78 * Handles button presses and converts them into keyboard input.
79 *
80 * @tparam HIDButton The list of buttons that can be converted into keyboard input.
81 */
82 template <HIDButton... T>
83 void HandleButtonPressedOnce();
84
85 /**
86 * Translates a button press to focus or click either the left or right buttons.
87 *
88 * @param button The button press to process.
89 */
90 void TranslateButtonPress(HIDButton button);
91
92 void StartInputThread();
93 void StopInputThread();
94
95 /// The thread where input is being polled and processed.
96 void InputThread();
97
98 std::unique_ptr<Ui::OverlayDialog> ui;
99
100 bool use_rich_text;
101
102 std::unique_ptr<InputInterpreter> input_interpreter;
103
104 std::thread input_thread;
105
106 std::atomic<bool> input_thread_running{};
107};
diff --git a/src/yuzu/util/overlay_dialog.ui b/src/yuzu/util/overlay_dialog.ui
new file mode 100644
index 000000000..278e2f219
--- /dev/null
+++ b/src/yuzu/util/overlay_dialog.ui
@@ -0,0 +1,404 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>OverlayDialog</class>
4 <widget class="QDialog" name="OverlayDialog">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>1280</width>
10 <height>720</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>Dialog</string>
15 </property>
16 <property name="styleSheet">
17 <string notr="true"/>
18 </property>
19 <layout class="QVBoxLayout" name="verticalLayout">
20 <property name="spacing">
21 <number>0</number>
22 </property>
23 <property name="leftMargin">
24 <number>0</number>
25 </property>
26 <property name="topMargin">
27 <number>0</number>
28 </property>
29 <property name="rightMargin">
30 <number>0</number>
31 </property>
32 <property name="bottomMargin">
33 <number>0</number>
34 </property>
35 <item>
36 <widget class="QStackedWidget" name="stackedDialog">
37 <property name="currentIndex">
38 <number>0</number>
39 </property>
40 <widget class="QWidget" name="lineDialog">
41 <layout class="QGridLayout" name="lineDialogGridLayout" rowstretch="210,300,210" columnstretch="250,780,250">
42 <property name="leftMargin">
43 <number>0</number>
44 </property>
45 <property name="topMargin">
46 <number>0</number>
47 </property>
48 <property name="rightMargin">
49 <number>0</number>
50 </property>
51 <property name="bottomMargin">
52 <number>0</number>
53 </property>
54 <property name="spacing">
55 <number>0</number>
56 </property>
57 <item row="1" column="1">
58 <widget class="QWidget" name="contentDialog" native="true">
59 <layout class="QVBoxLayout" name="verticalLayout_2" stretch="70,149,82">
60 <property name="spacing">
61 <number>0</number>
62 </property>
63 <property name="leftMargin">
64 <number>0</number>
65 </property>
66 <property name="topMargin">
67 <number>0</number>
68 </property>
69 <property name="rightMargin">
70 <number>0</number>
71 </property>
72 <property name="bottomMargin">
73 <number>0</number>
74 </property>
75 <item>
76 <widget class="QLabel" name="label_title">
77 <property name="font">
78 <font>
79 <pointsize>14</pointsize>
80 </font>
81 </property>
82 <property name="alignment">
83 <set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
84 </property>
85 </widget>
86 </item>
87 <item>
88 <widget class="QLabel" name="label_dialog">
89 <property name="font">
90 <font>
91 <pointsize>18</pointsize>
92 </font>
93 </property>
94 <property name="alignment">
95 <set>Qt::AlignCenter</set>
96 </property>
97 <property name="wordWrap">
98 <bool>true</bool>
99 </property>
100 </widget>
101 </item>
102 <item>
103 <widget class="QWidget" name="buttonsDialog" native="true">
104 <layout class="QHBoxLayout" name="horizontalLayout">
105 <property name="spacing">
106 <number>0</number>
107 </property>
108 <property name="leftMargin">
109 <number>0</number>
110 </property>
111 <property name="topMargin">
112 <number>0</number>
113 </property>
114 <property name="rightMargin">
115 <number>0</number>
116 </property>
117 <property name="bottomMargin">
118 <number>0</number>
119 </property>
120 <item>
121 <widget class="QPushButton" name="button_cancel">
122 <property name="sizePolicy">
123 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
124 <horstretch>0</horstretch>
125 <verstretch>0</verstretch>
126 </sizepolicy>
127 </property>
128 <property name="font">
129 <font>
130 <pointsize>18</pointsize>
131 </font>
132 </property>
133 <property name="text">
134 <string>Cancel</string>
135 </property>
136 </widget>
137 </item>
138 <item>
139 <widget class="QPushButton" name="button_ok_label">
140 <property name="sizePolicy">
141 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
142 <horstretch>0</horstretch>
143 <verstretch>0</verstretch>
144 </sizepolicy>
145 </property>
146 <property name="font">
147 <font>
148 <pointsize>18</pointsize>
149 </font>
150 </property>
151 <property name="text">
152 <string>OK</string>
153 </property>
154 </widget>
155 </item>
156 </layout>
157 </widget>
158 </item>
159 </layout>
160 </widget>
161 </item>
162 <item row="0" column="1">
163 <spacer name="verticalSpacer">
164 <property name="orientation">
165 <enum>Qt::Vertical</enum>
166 </property>
167 <property name="sizeHint" stdset="0">
168 <size>
169 <width>20</width>
170 <height>40</height>
171 </size>
172 </property>
173 </spacer>
174 </item>
175 <item row="1" column="0">
176 <spacer name="horizontalSpacer">
177 <property name="orientation">
178 <enum>Qt::Horizontal</enum>
179 </property>
180 <property name="sizeHint" stdset="0">
181 <size>
182 <width>40</width>
183 <height>20</height>
184 </size>
185 </property>
186 </spacer>
187 </item>
188 <item row="2" column="1">
189 <spacer name="verticalSpacer_2">
190 <property name="orientation">
191 <enum>Qt::Vertical</enum>
192 </property>
193 <property name="sizeHint" stdset="0">
194 <size>
195 <width>20</width>
196 <height>40</height>
197 </size>
198 </property>
199 </spacer>
200 </item>
201 <item row="1" column="2">
202 <spacer name="horizontalSpacer_2">
203 <property name="orientation">
204 <enum>Qt::Horizontal</enum>
205 </property>
206 <property name="sizeHint" stdset="0">
207 <size>
208 <width>40</width>
209 <height>20</height>
210 </size>
211 </property>
212 </spacer>
213 </item>
214 </layout>
215 </widget>
216 <widget class="QWidget" name="richDialog">
217 <layout class="QGridLayout" name="richDialogGridLayout" rowstretch="100,520,100" columnstretch="165,950,165">
218 <property name="leftMargin">
219 <number>0</number>
220 </property>
221 <property name="topMargin">
222 <number>0</number>
223 </property>
224 <property name="rightMargin">
225 <number>0</number>
226 </property>
227 <property name="bottomMargin">
228 <number>0</number>
229 </property>
230 <property name="spacing">
231 <number>0</number>
232 </property>
233 <item row="1" column="0">
234 <spacer name="horizontalSpacer_3">
235 <property name="orientation">
236 <enum>Qt::Horizontal</enum>
237 </property>
238 <property name="sizeHint" stdset="0">
239 <size>
240 <width>40</width>
241 <height>20</height>
242 </size>
243 </property>
244 </spacer>
245 </item>
246 <item row="2" column="1">
247 <spacer name="verticalSpacer_4">
248 <property name="orientation">
249 <enum>Qt::Vertical</enum>
250 </property>
251 <property name="sizeHint" stdset="0">
252 <size>
253 <width>20</width>
254 <height>40</height>
255 </size>
256 </property>
257 </spacer>
258 </item>
259 <item row="0" column="1">
260 <spacer name="verticalSpacer_3">
261 <property name="orientation">
262 <enum>Qt::Vertical</enum>
263 </property>
264 <property name="sizeHint" stdset="0">
265 <size>
266 <width>20</width>
267 <height>40</height>
268 </size>
269 </property>
270 </spacer>
271 </item>
272 <item row="1" column="1">
273 <widget class="QWidget" name="contentRichDialog" native="true">
274 <layout class="QVBoxLayout" name="verticalLayout_3" stretch="70,368,82">
275 <property name="spacing">
276 <number>0</number>
277 </property>
278 <property name="leftMargin">
279 <number>0</number>
280 </property>
281 <property name="topMargin">
282 <number>0</number>
283 </property>
284 <property name="rightMargin">
285 <number>0</number>
286 </property>
287 <property name="bottomMargin">
288 <number>0</number>
289 </property>
290 <item>
291 <widget class="QLabel" name="label_title_rich">
292 <property name="font">
293 <font>
294 <pointsize>14</pointsize>
295 </font>
296 </property>
297 <property name="text">
298 <string/>
299 </property>
300 <property name="alignment">
301 <set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
302 </property>
303 </widget>
304 </item>
305 <item>
306 <widget class="QTextBrowser" name="text_browser_dialog">
307 <property name="font">
308 <font>
309 <pointsize>18</pointsize>
310 </font>
311 </property>
312 <property name="html">
313 <string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
314&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
315p, li { white-space: pre-wrap; }
316&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:18pt; font-weight:400; font-style:normal;&quot;&gt;
317&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
318 </property>
319 </widget>
320 </item>
321 <item>
322 <widget class="QWidget" name="buttonsRichDialog" native="true">
323 <layout class="QHBoxLayout" name="horizontalLayout_2">
324 <property name="spacing">
325 <number>0</number>
326 </property>
327 <property name="leftMargin">
328 <number>0</number>
329 </property>
330 <property name="topMargin">
331 <number>0</number>
332 </property>
333 <property name="rightMargin">
334 <number>0</number>
335 </property>
336 <property name="bottomMargin">
337 <number>0</number>
338 </property>
339 <item>
340 <widget class="QPushButton" name="button_cancel_rich">
341 <property name="sizePolicy">
342 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
343 <horstretch>0</horstretch>
344 <verstretch>0</verstretch>
345 </sizepolicy>
346 </property>
347 <property name="font">
348 <font>
349 <pointsize>18</pointsize>
350 </font>
351 </property>
352 <property name="text">
353 <string>Cancel</string>
354 </property>
355 </widget>
356 </item>
357 <item>
358 <widget class="QPushButton" name="button_ok_rich">
359 <property name="sizePolicy">
360 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
361 <horstretch>0</horstretch>
362 <verstretch>0</verstretch>
363 </sizepolicy>
364 </property>
365 <property name="font">
366 <font>
367 <pointsize>18</pointsize>
368 </font>
369 </property>
370 <property name="text">
371 <string>OK</string>
372 </property>
373 </widget>
374 </item>
375 </layout>
376 </widget>
377 </item>
378 </layout>
379 </widget>
380 </item>
381 <item row="1" column="2">
382 <spacer name="horizontalSpacer_4">
383 <property name="orientation">
384 <enum>Qt::Horizontal</enum>
385 </property>
386 <property name="sizeHint" stdset="0">
387 <size>
388 <width>40</width>
389 <height>20</height>
390 </size>
391 </property>
392 </spacer>
393 </item>
394 </layout>
395 </widget>
396 </widget>
397 </item>
398 </layout>
399 </widget>
400 <resources>
401 <include location="../../../dist/icons/overlay/overlay.qrc"/>
402 </resources>
403 <connections/>
404</ui>