1 /*
2 * Copyright (c) 2022-2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "adapter/preview/entrance/event_dispatcher.h"
17
18 #include <map>
19
20 #include "base/log/log.h"
21 #include "base/log/ace_trace.h"
22 #include "core/common/container_scope.h"
23 #include "adapter/preview/entrance/ace_container.h"
24 #include "adapter/preview/entrance/editing/text_input_client_mgr.h"
25 #include "adapter/preview/entrance/flutter_ace_view.h"
26
27 namespace OHOS::Ace::Platform {
28 namespace {
29
30 const wchar_t UPPER_CASE_A = L'A';
31 const wchar_t LOWER_CASE_A = L'a';
32 const wchar_t CASE_0 = L'0';
33 const std::wstring NUM_SYMBOLS = L")!@#$%^&*(";
34 const std::map<KeyCode, wchar_t> PRINTABEL_SYMBOLS = {
35 {KeyCode::KEY_GRAVE, L'`'},
36 {KeyCode::KEY_MINUS, L'-'},
37 {KeyCode::KEY_EQUALS, L'='},
38 {KeyCode::KEY_LEFT_BRACKET, L'['},
39 {KeyCode::KEY_RIGHT_BRACKET, L']'},
40 {KeyCode::KEY_BACKSLASH, L'\\'},
41 {KeyCode::KEY_SEMICOLON, L';'},
42 {KeyCode::KEY_APOSTROPHE, L'\''},
43 {KeyCode::KEY_COMMA, L','},
44 {KeyCode::KEY_PERIOD, L'.'},
45 {KeyCode::KEY_SLASH, L'/'},
46 {KeyCode::KEY_SPACE, L' '},
47 {KeyCode::KEY_NUMPAD_DIVIDE, L'/'},
48 {KeyCode::KEY_NUMPAD_MULTIPLY, L'*'},
49 {KeyCode::KEY_NUMPAD_SUBTRACT, L'-'},
50 {KeyCode::KEY_NUMPAD_ADD, L'+'},
51 {KeyCode::KEY_NUMPAD_DOT, L'.'},
52 {KeyCode::KEY_NUMPAD_COMMA, L','},
53 {KeyCode::KEY_NUMPAD_EQUALS, L'='},
54 };
55
56 const std::map<KeyCode, wchar_t> SHIFT_PRINTABEL_SYMBOLS = {
57 {KeyCode::KEY_GRAVE, L'~'},
58 {KeyCode::KEY_MINUS, L'_'},
59 {KeyCode::KEY_EQUALS, L'+'},
60 {KeyCode::KEY_LEFT_BRACKET, L'{'},
61 {KeyCode::KEY_RIGHT_BRACKET, L'}'},
62 {KeyCode::KEY_BACKSLASH, L'|'},
63 {KeyCode::KEY_SEMICOLON, L':'},
64 {KeyCode::KEY_APOSTROPHE, L'\"'},
65 {KeyCode::KEY_COMMA, L'<'},
66 {KeyCode::KEY_PERIOD, L'>'},
67 {KeyCode::KEY_SLASH, L'?'},
68 };
69
70 }
71
EventDispatcher()72 EventDispatcher::EventDispatcher()
73 {}
74
75 EventDispatcher::~EventDispatcher() = default;
76
SetGlfwWindowController(const FlutterDesktopWindowControllerRef & controller)77 void EventDispatcher::SetGlfwWindowController(const FlutterDesktopWindowControllerRef& controller)
78 {
79 controller_ = controller;
80 }
81
Initialize()82 void EventDispatcher::Initialize()
83 {
84 LOGI("Initialize event dispatcher");
85 // Initial the proxy of Input method
86 TextInputClientMgr::GetInstance().InitTextInputProxy();
87 // Register the idle event callback function.
88 #ifndef ENABLE_ROSEN_BACKEND
89 IdleCallback idleNoticeCallback = [] (int64_t deadline) {
90 EventDispatcher::GetInstance().DispatchIdleEvent(deadline);
91 };
92 FlutterDesktopSetIdleCallback(controller_, idleNoticeCallback);
93 #else
94 // rosen process idle
95 #endif
96 }
97
DispatchIdleEvent(int64_t deadline)98 void EventDispatcher::DispatchIdleEvent(int64_t deadline)
99 {
100 ACE_SCOPED_TRACE("DispatchIdleEvent");
101 auto container = AceContainer::GetContainerInstance(ACE_INSTANCE_ID);
102 if (!container) {
103 LOGE("container is null");
104 return;
105 }
106
107 auto aceView = container->GetAceView();
108 if (!aceView) {
109 LOGE("aceView is null");
110 return;
111 }
112
113 aceView->ProcessIdleEvent(deadline);
114 }
115
DispatchTouchEvent(const TouchEvent & event)116 bool EventDispatcher::DispatchTouchEvent(const TouchEvent& event)
117 {
118 ACE_SCOPED_TRACE("DispatchTouchEvent");
119 LOGI("Dispatch touch event");
120 auto container = AceContainer::GetContainerInstance(ACE_INSTANCE_ID);
121 if (!container) {
122 LOGE("container is null");
123 return false;
124 }
125
126 auto aceView = container->GetAceView();
127 if (!aceView) {
128 LOGE("aceView is null");
129 return false;
130 }
131
132 return aceView->HandleTouchEvent(event);
133 }
134
DispatchBackPressedEvent()135 bool EventDispatcher::DispatchBackPressedEvent()
136 {
137 ACE_SCOPED_TRACE("DispatchBackPressedEvent");
138 LOGI("Dispatch back pressed event");
139 auto container = AceContainer::GetContainerInstance(ACE_INSTANCE_ID);
140 if (!container) {
141 return false;
142 }
143
144 auto context = container->GetPipelineContext();
145 if (!context) {
146 return false;
147 }
148
149 std::promise<bool> backPromise;
150 std::future<bool> backFuture = backPromise.get_future();
151 auto weak = AceType::WeakClaim(AceType::RawPtr(context));
152 container->GetTaskExecutor()->PostTask(
153 [weak, &backPromise]() {
154 auto context = weak.Upgrade();
155 if (context == nullptr) {
156 LOGW("context is nullptr.");
157 return;
158 }
159 bool canBack = false;
160 if (context->IsLastPage()) {
161 LOGW("Can't back because this is the last page!");
162 } else {
163 canBack = context->CallRouterBackToPopPage();
164 }
165 backPromise.set_value(canBack);
166 },
167 TaskExecutor::TaskType::PLATFORM);
168 return backFuture.get();
169 }
170
DispatchInputMethodEvent(unsigned int code_point)171 bool EventDispatcher::DispatchInputMethodEvent(unsigned int code_point)
172 {
173 ACE_SCOPED_TRACE("DispatchInputMethodEvent");
174 LOGI("Dispatch input method event");
175 return TextInputClientMgr::GetInstance().AddCharacter(static_cast<wchar_t>(code_point));
176 }
177
DispatchKeyEvent(const KeyEvent & event)178 bool EventDispatcher::DispatchKeyEvent(const KeyEvent& event)
179 {
180 ACE_SCOPED_TRACE("DispatchKeyEvent");
181 LOGI("Dispatch key event");
182 if (HandleTextKeyEvent(event)) {
183 LOGI("The event is related to the input component and has been handled successfully.");
184 return true;
185 }
186 auto container = AceContainer::GetContainerInstance(ACE_INSTANCE_ID);
187 if (!container) {
188 LOGE("container is null");
189 return false;
190 }
191
192 auto aceView = container->GetAceView();
193 if (!aceView) {
194 LOGE("aceView is null");
195 return false;
196 }
197 #ifdef USE_GLFW_WINDOW
198 container->GetTaskExecutor()->PostTask(
199 [aceView, event]() {
200 aceView->HandleKeyEvent(event);
201 },
202 TaskExecutor::TaskType::UI);
203 return true;
204 #else
205 return aceView->HandleKeyEvent(event);
206 #endif
207 }
208
RegisterCallbackGetCapsLockStatus(CallbackGetKeyboardStatus callback)209 void EventDispatcher::RegisterCallbackGetCapsLockStatus(CallbackGetKeyboardStatus callback)
210 {
211 if (callback) {
212 callbackGetCapsLockStatus_ = callback;
213 }
214 }
215
RegisterCallbackGetNumLockStatus(CallbackGetKeyboardStatus callback)216 void EventDispatcher::RegisterCallbackGetNumLockStatus(CallbackGetKeyboardStatus callback)
217 {
218 if (callback) {
219 callbackGetNumLockStatus_ = callback;
220 }
221 }
222
HandleTextKeyEvent(const KeyEvent & event)223 bool EventDispatcher::HandleTextKeyEvent(const KeyEvent& event)
224 {
225 // Only the keys involved in the input component are processed here, and the other keys will be forwarded.
226 if (!TextInputClientMgr::GetInstance().IsValidClientId()) {
227 return false;
228 }
229
230 bool enableCapsLock = callbackGetCapsLockStatus_ && callbackGetCapsLockStatus_();
231 bool enableNumLock = callbackGetNumLockStatus_ && callbackGetNumLockStatus_();
232 const static size_t maxKeySizes = 2;
233 wchar_t keyChar;
234 if (event.pressedCodes.size() == 1) {
235 auto iterCode = PRINTABEL_SYMBOLS.find(event.code);
236 if (iterCode != PRINTABEL_SYMBOLS.end()) {
237 keyChar = iterCode->second;
238 } else if (KeyCode::KEY_0 <= event.code && event.code <= KeyCode::KEY_9) {
239 keyChar = static_cast<wchar_t>(event.code) - static_cast<wchar_t>(KeyCode::KEY_0) + CASE_0;
240 } else if (KeyCode::KEY_NUMPAD_0 <= event.code && event.code <= KeyCode::KEY_NUMPAD_9) {
241 if (!enableNumLock) {
242 return true;
243 }
244 keyChar = static_cast<wchar_t>(event.code) - static_cast<wchar_t>(KeyCode::KEY_NUMPAD_0) + CASE_0;
245 } else if (KeyCode::KEY_A <= event.code && event.code <= KeyCode::KEY_Z) {
246 keyChar = static_cast<wchar_t>(event.code) - static_cast<wchar_t>(KeyCode::KEY_A);
247 keyChar += (enableCapsLock ? UPPER_CASE_A : LOWER_CASE_A);
248 } else {
249 return false;
250 }
251 } else if (event.pressedCodes.size() == maxKeySizes && event.pressedCodes[0] == KeyCode::KEY_SHIFT_LEFT) {
252 auto iterCode = SHIFT_PRINTABEL_SYMBOLS.find(event.code);
253 if (iterCode != SHIFT_PRINTABEL_SYMBOLS.end()) {
254 keyChar = iterCode->second;
255 } else if (KeyCode::KEY_A <= event.code && event.code <= KeyCode::KEY_Z) {
256 keyChar = static_cast<wchar_t>(event.code) - static_cast<wchar_t>(KeyCode::KEY_A);
257 keyChar += (enableCapsLock ? LOWER_CASE_A : UPPER_CASE_A);
258 } else if (KeyCode::KEY_0 <= event.code && event.code <= KeyCode::KEY_9) {
259 keyChar = NUM_SYMBOLS[static_cast<int32_t>(event.code) - static_cast<int32_t>(KeyCode::KEY_0)];
260 } else {
261 return false;
262 }
263 } else {
264 return false;
265 }
266 #ifdef USE_GLFW_WINDOW
267 return true;
268 #else
269 if (event.action != KeyAction::DOWN) {
270 return true;
271 }
272 return TextInputClientMgr::GetInstance().AddCharacter(keyChar);
273 #endif
274 }
275
276 } // namespace OHOS::Ace::Platform
277