1/* 2 * Copyright (c) 2024 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#include <map> 16#include <mutex> 17 18#include "global.h" 19#include "input_method_controller.h" 20#include "input_method_utils.h" 21#include "inputmethod_controller_capi.h" 22#include "native_inputmethod_types.h" 23#include "native_inputmethod_utils.h" 24#include "native_text_changed_listener.h" 25using namespace OHOS::MiscServices; 26#ifdef __cplusplus 27extern "C" { 28#endif /* __cplusplus */ 29 30struct InputMethod_InputMethodProxy { 31 InputMethod_TextEditorProxy *textEditor = nullptr; 32 OHOS::sptr<NativeTextChangedListener> listener = nullptr; 33 bool attached = false; 34}; 35 36InputMethod_InputMethodProxy *g_inputMethodProxy = nullptr; 37std::mutex g_textEditorProxyMapMutex; 38 39InputMethod_ErrorCode IsValidInputMethodProxy(const InputMethod_InputMethodProxy *inputMethodProxy) 40{ 41 if (inputMethodProxy == nullptr) { 42 IMSA_HILOGE("inputMethodProxy is nullptr"); 43 return IME_ERR_NULL_POINTER; 44 } 45 std::lock_guard<std::mutex> guard(g_textEditorProxyMapMutex); 46 if (g_inputMethodProxy == nullptr) { 47 IMSA_HILOGE("g_inputMethodProxy is nullptr"); 48 return IME_ERR_DETACHED; 49 } 50 51 if (g_inputMethodProxy != inputMethodProxy) { 52 IMSA_HILOGE("g_inputMethodProxy is not equal to inputMethodProxy"); 53 return IME_ERR_PARAMCHECK; 54 } 55 56 if (!(g_inputMethodProxy->attached)) { 57 IMSA_HILOGE("g_inputMethodProxy is not attached"); 58 return IME_ERR_DETACHED; 59 } 60 61 return IME_ERR_OK; 62} 63static InputMethod_ErrorCode GetInputMethodProxy(InputMethod_TextEditorProxy *textEditor) 64{ 65 std::lock_guard<std::mutex> guard(g_textEditorProxyMapMutex); 66 if (g_inputMethodProxy != nullptr && textEditor == g_inputMethodProxy->textEditor) { 67 return IME_ERR_OK; 68 } 69 70 if (g_inputMethodProxy != nullptr && textEditor != g_inputMethodProxy->textEditor) { 71 g_inputMethodProxy->listener = nullptr; 72 delete g_inputMethodProxy; 73 g_inputMethodProxy = nullptr; 74 } 75 OHOS::sptr<NativeTextChangedListener> listener = new NativeTextChangedListener(textEditor); 76 if (listener == nullptr) { 77 IMSA_HILOGE("new NativeTextChangedListener failed"); 78 return IME_ERR_NULL_POINTER; 79 } 80 81 g_inputMethodProxy = new InputMethod_InputMethodProxy({ textEditor, listener }); 82 if (g_inputMethodProxy == nullptr) { 83 IMSA_HILOGE("new InputMethod_InputMethodProxy failed"); 84 listener = nullptr; 85 return IME_ERR_NULL_POINTER; 86 } 87 return IME_ERR_OK; 88} 89#define CHECK_MEMBER_NULL(textEditor, member) \ 90 do { \ 91 if ((textEditor)->member == nullptr) { \ 92 IMSA_HILOGE(#member " is nullptr"); \ 93 return IME_ERR_NULL_POINTER; \ 94 } \ 95 } while (0) 96static int32_t IsValidTextEditorProxy(InputMethod_TextEditorProxy *textEditor) 97{ 98 if (textEditor == nullptr) { 99 IMSA_HILOGE("textEditor is nullptr"); 100 return IME_ERR_NULL_POINTER; 101 } 102 103 CHECK_MEMBER_NULL(textEditor, getTextConfigFunc); 104 CHECK_MEMBER_NULL(textEditor, insertTextFunc); 105 CHECK_MEMBER_NULL(textEditor, deleteForwardFunc); 106 CHECK_MEMBER_NULL(textEditor, deleteBackwardFunc); 107 CHECK_MEMBER_NULL(textEditor, sendKeyboardStatusFunc); 108 CHECK_MEMBER_NULL(textEditor, sendEnterKeyFunc); 109 CHECK_MEMBER_NULL(textEditor, moveCursorFunc); 110 CHECK_MEMBER_NULL(textEditor, handleSetSelectionFunc); 111 CHECK_MEMBER_NULL(textEditor, handleExtendActionFunc); 112 CHECK_MEMBER_NULL(textEditor, getLeftTextOfCursorFunc); 113 CHECK_MEMBER_NULL(textEditor, getRightTextOfCursorFunc); 114 CHECK_MEMBER_NULL(textEditor, getTextIndexAtCursorFunc); 115 CHECK_MEMBER_NULL(textEditor, receivePrivateCommandFunc); 116 CHECK_MEMBER_NULL(textEditor, setPreviewTextFunc); 117 CHECK_MEMBER_NULL(textEditor, finishTextPreviewFunc); 118 return IME_ERR_OK; 119} 120 121static TextConfig ConstructTextConfig(const InputMethod_TextConfig &config) 122{ 123 TextConfig textConfig = { 124 .inputAttribute = { 125 .inputPattern = static_cast<InputMethod_TextInputType>(config.inputType), 126 .enterKeyType = static_cast<InputMethod_EnterKeyType>(config.enterKeyType), 127 .isTextPreviewSupported = config.previewTextSupported, 128 }, 129 .cursorInfo = { 130 .left = config.cursorInfo.left, 131 .top = config.cursorInfo.top, 132 .width = config.cursorInfo.width, 133 .height = config.cursorInfo.height, 134 }, 135 .range = { 136 .start = config.selectionStart, 137 .end = config.selectionEnd, 138 }, 139 .windowId = config.windowId, 140 .positionY = config.avoidInfo.positionY, 141 .height = config.avoidInfo.height, 142 }; 143 144 return textConfig; 145} 146 147InputMethod_ErrorCode OH_InputMethodController_Attach(InputMethod_TextEditorProxy *textEditor, 148 InputMethod_AttachOptions *options, InputMethod_InputMethodProxy **inputMethodProxy) 149{ 150 if ((IsValidTextEditorProxy(textEditor) != IME_ERR_OK) || options == nullptr || inputMethodProxy == nullptr) { 151 IMSA_HILOGE("invalid parameter"); 152 return IME_ERR_NULL_POINTER; 153 } 154 155 InputMethod_ErrorCode errCode = GetInputMethodProxy(textEditor); 156 if (errCode != IME_ERR_OK) { 157 return errCode; 158 } 159 160 InputMethod_TextConfig config; 161 textEditor->getTextConfigFunc(textEditor, &config); 162 163 auto textConfig = ConstructTextConfig(config); 164 165 auto controller = InputMethodController::GetInstance(); 166 OHOS::sptr<NativeTextChangedListener> listener = nullptr; 167 { 168 std::lock_guard<std::mutex> guard(g_textEditorProxyMapMutex); 169 if (g_inputMethodProxy != nullptr) { 170 listener = g_inputMethodProxy->listener; 171 } 172 } 173 174 int32_t err = controller->Attach(listener, options->showKeyboard, textConfig); 175 if (err == ErrorCode::NO_ERROR) { 176 errCode = IME_ERR_OK; 177 std::lock_guard<std::mutex> guard(g_textEditorProxyMapMutex); 178 if (g_inputMethodProxy != nullptr) { 179 g_inputMethodProxy->attached = true; 180 } 181 *inputMethodProxy = g_inputMethodProxy; 182 } else { 183 errCode = ErrorCodeConvert(err); 184 } 185 186 return errCode; 187} 188 189void ClearInputMethodProxy(void) 190{ 191 std::lock_guard<std::mutex> guard(g_textEditorProxyMapMutex); 192 if (g_inputMethodProxy != nullptr) { 193 IMSA_HILOGI("g_inputMethodProxy is detached"); 194 g_inputMethodProxy->attached = false; 195 } 196} 197 198InputMethod_ErrorCode OH_InputMethodController_Detach(InputMethod_InputMethodProxy *inputMethodProxy) 199{ 200 if (inputMethodProxy == nullptr) { 201 IMSA_HILOGE("inputMethodProxy is nullptr"); 202 return IME_ERR_NULL_POINTER; 203 } 204 { 205 std::lock_guard<std::mutex> guard(g_textEditorProxyMapMutex); 206 if (g_inputMethodProxy == nullptr) { 207 IMSA_HILOGE("g_inputMethodProxy is nullptr"); 208 return IME_ERR_DETACHED; 209 } 210 211 if (g_inputMethodProxy != inputMethodProxy) { 212 IMSA_HILOGE("g_inputMethodProxy is not equal to inputMethodProxy"); 213 return IME_ERR_PARAMCHECK; 214 } 215 216 IMSA_HILOGI("detach g_inputMethodProxy"); 217 g_inputMethodProxy->listener = nullptr; 218 delete g_inputMethodProxy; 219 g_inputMethodProxy = nullptr; 220 } 221 return ErrorCodeConvert(InputMethodController::GetInstance()->Close()); 222} 223#ifdef __cplusplus 224} 225#endif /* __cplusplus */