1# XComponent Development 2 3## When to Use 4 5**NativeXComponent** provides an instance for the **\<XComponent>** at the native layer, which can be used as a bridge for binding with the **\<XComponent>** at the JS layer. The NDK APIs provided by the **\<XComponent>** depend on this instance. The provided APIs include those for obtaining a native window, obtaining the layout or event information of the **\<XComponent>**, registering the lifecycle callbacks of the **\<XComponent>**, and registering the callbacks for the touch, mouse, and key events of the **\<XComponent>**. You can use the provided APIs in the following scenarios: 6 7- Register the lifecycle and event callbacks of the **\<XComponent>**. 8- Initialize the environment, obtain the current state, and respond to various events via these callbacks. 9- Use the native window and EGL APIs to develop custom drawing content, and apply for and submit buffers to the graphics queue. 10 11## Available APIs 12 13| API| Description.| 14| -------- | -------- | 15|OH_NativeXComponent_GetXComponentId(OH_NativeXComponent* component, char* id, uint64_t* size)|Obtains the ID of an **\<XComponent>**.| 16|OH_NativeXComponent_GetXComponentSize(OH_NativeXComponent* component, const void* window, uint64_t* width, uint64_t* height)|Obtains the size of the surface held by an **\<XComponent>**.| 17|OH_NativeXComponent_GetXComponentOffset(OH_NativeXComponent* component, const void* window, double* x, double* y)|Obtains the offset of the surface held by an **\<XComponent>** relative to the upper left corner of the window.| 18|OH_NativeXComponent_GetTouchEvent(OH_NativeXComponent* component, const void* window, OH_NativeXComponent_TouchEvent* touchEvent)|Obtains the touch event triggered by an **\<XComponent>**. For details about the attribute values in **touchEvent**, see [OH_NativeXComponent_TouchEvent](../reference/native-apis/_o_h___native_x_component___touch_event.md).| 19|OH_NativeXComponent_GetTouchPointToolType(OH_NativeXComponent* component, uint32_t pointIndex, OH_NativeXComponent_TouchPointToolType* toolType)|Obtains the tool type of an **\<XComponent>** touch point.| 20|OH_NativeXComponent_GetTouchPointTiltX(OH_NativeXComponent* component, uint32_t pointIndex, float* tiltX)|Obtains the tilt of an **\<XComponent>** touch point relative to the x-axis.| 21|OH_NativeXComponent_GetTouchPointTiltY(OH_NativeXComponent* component, uint32_t pointIndex, float* tiltY)|Obtains the tilt of an **\<XComponent>** touch point relative to the y-axis.| 22|OH_NativeXComponent_GetMouseEvent(OH_NativeXComponent* component, const void* window, OH_NativeXComponent_MouseEvent* mouseEvent)|Obtains the mouse event triggered by an **\<XComponent>**.| 23|OH_NativeXComponent_RegisterCallback(OH_NativeXComponent* component, OH_NativeXComponent_Callback* callback)|Registers a lifecycle or touch event callback for an **OH_NativeXComponent** instance.| 24|OH_NativeXComponent_RegisterMouseEventCallback(OH_NativeXComponent* component, OH_NativeXComponent_MouseEvent_Callback* callback)|Registers the mouse event callback for an **OH_NativeXComponent** instance.| 25|OH_NativeXComponent_RegisterFocusEventCallback(OH_NativeXComponent* component, void (\*callback)(OH_NativeXComponent* component, void* window))|Registers the focus obtaining event callback for an **OH_NativeXComponent** instance.| 26|OH_NativeXComponent_RegisterKeyEventCallback(OH_NativeXComponent* component, void (\*callback)(OH_NativeXComponent* component, void* window))|Registers the key event callback for an **OH_NativeXComponent** instance.| 27|OH_NativeXComponent_RegisterBlurEventCallback(OH_NativeXComponent* component, void (\*callback)(OH_NativeXComponent* component, void* window))|Registers the focus loss event callback for an **OH_NativeXComponent** instance.| 28|OH_NativeXComponent_GetKeyEvent(OH_NativeXComponent* component, OH_NativeXComponent_KeyEvent\** keyEvent)|Obtains the key event triggered by an **\<XComponent>**.| 29|OH_NativeXComponent_GetKeyEventAction(OH_NativeXComponent_KeyEvent* keyEvent, OH_NativeXComponent_KeyAction* action)|Obtains the action of a key event.| 30|OH_NativeXComponent_GetKeyEventCode(OH_NativeXComponent_KeyEvent* keyEvent, OH_NativeXComponent_KeyCode* code)|Obtains the key code value of a key event.| 31|OH_NativeXComponent_GetKeyEventSourceType(OH_NativeXComponent_KeyEvent* keyEvent, OH_NativeXComponent_EventSourceType* sourceType)|Obtains the input source type of a key event.| 32|OH_NativeXComponent_GetKeyEventDeviceId(OH_NativeXComponent_KeyEvent* keyEvent, int64_t* deviceId)|Obtains the device ID of a key event.| 33|OH_NativeXComponent_GetKeyEventTimestamp(OH_NativeXComponent_KeyEvent* keyEvent, int64_t* timestamp)|Obtains the timestamp of a key event.| 34 35## Lifecycle Description 36 37You can use the **\<XComponent>** to develop EGL/OpenGL ES rendering by using the following ArkTS code: 38 39```typescript 40@Builder 41function myComponent() { 42 XComponent({ id: 'xcomponentId1', type: 'surface', libraryname: 'nativerender' }) 43 .onLoad((context) => {}) 44 .onDestroy(() => {}) 45} 46``` 47 48### **onLoad** Event 49 50Trigger time: when the surface of the **\<XComponent>** is ready. 51 52**context** parameter: where the native API exposed on the module is mounted. Its usage is similar to the usage of a **context** instance obtained after the module is directly loaded using **import context from "libnativerender.so"**. 53 54Time sequence: subject to the surface. The figure below shows the time sequence of the **onLoad** event and the **OnSurfaceCreated** event at the native layer. 55 56 57 58### **onDestroy** Event 59 60Trigger time: when the **\<XComponent>** is destroyed, in the same manner as that when an ArkUI component is destroyed. The figure below shows the time sequence of the **onDestroy** event and the **OnSurfaceDestroyed** event at the native layer. 61 62 63 64## How to Develop 65The following describes how to use the **\<XComponent>** to call the native APIs to create the EGL/OpenGL ES environment, draw graphics on the main page, and change graphics colors. 66 671. Define the **\<XComponent>** on the GUI. 68 69 ```typescript 70 @Entry 71 @Component 72 struct Index { 73 @State message: string = 'Hello World' 74 xComponentContext: object | undefined = undefined; 75 xComponentAttrs: XComponentAttrs = { 76 id: 'xcomponentId', 77 type: XComponentType.SURFACE, 78 libraryname: 'nativerender' 79 } 80 81 build() { 82 Row() { 83 // ... 84 // Define XComponent in an .ets file. 85 XComponent(this.xComponentAttrs) 86 .focusable(true) // Set the component to be able to respond to key events. 87 .onLoad((xComponentContext) => { 88 this.xComponentContext = xComponentContext; 89 }) 90 .onDestroy(() => { 91 console.log("onDestroy"); 92 }) 93 // ... 94 } 95 .height('100%') 96 } 97 } 98 99 interface XComponentAttrs { 100 id: string; 101 type: number; 102 libraryname: string; 103 } 104 ``` 105 1062. Register the Node-API module. For details, see [Node-API Development Specifications](napi-guidelines.md). 107 108 ```c++ 109 // In the napi_init.cpp file, use the Init method to register the target function to pass in the encapsulated C++ methods for the JS method to call. 110 EXTERN_C_START 111 static napi_value Init(napi_env env, napi_value exports) 112 { 113 // ... 114 // Expose the getContext() API to the JS code. 115 napi_property_descriptor desc[] = { 116 { "getContext", nullptr, PluginManager::GetContext, nullptr, nullptr, nullptr, napi_default, nullptr } 117 }; 118 if (napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc) != napi_ok) { 119 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Init", "napi_define_properties failed"); 120 return nullptr; 121 } 122 // Check whether the environment variables in the method contain the <XComponent> instance. If the instance exists, register the drawing-related API. 123 PluginManager::GetInstance()->Export(env, exports); 124 return exports; 125 } 126 EXTERN_C_END 127 128 // Write the API description. You can modify parameters as required. 129 static napi_module nativerenderModule = { 130 .nm_version = 1, 131 .nm_flags = 0, 132 .nm_filename = nullptr, 133 // Entry point function 134 .nm_register_func = Init, 135 // Module name 136 .nm_modname = "nativerender", 137 .nm_priv = ((void *)0), 138 .reserved = { 0 } 139 }; 140 141 // The method decorated by __attribute__((constructor)) is automatically called by the system. The Node-API napi_module_register() is used to pass in the module description for module registration. 142 extern "C" __attribute__((constructor)) void RegisterModule(void) 143 { 144 napi_module_register(&nativerenderModule); 145 } 146 147 // Use the napi_define_properties method to expose the drawPattern() method to the JS code and call the JS drawPattern() method to draw content. 148 void PluginRender::Export(napi_env env, napi_value exports) 149 { 150 // ... 151 // Register the function as the JS API drawPattern. 152 napi_property_descriptor desc[] = { 153 { "drawPattern", nullptr, PluginRender::NapiDrawPattern, nullptr, nullptr, nullptr, napi_default, nullptr } 154 }; 155 if (napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc) != napi_ok) { 156 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginRender", "Export: napi_define_properties failed"); 157 } 158 } 159 ``` 160 1613. Register the **\<XComponent>** event callback and use Node-API to implement it. 162 163 (1) Define the callbacks for the touch event of the **\<XComponent>** and for when a surface is successfully created, changed, or destroyed. 164 165 ```c++ 166 // Define the OnSurfaceCreatedCB() function to encapsulate the initialization environment and drawing background. 167 void OnSurfaceCreatedCB(OH_NativeXComponent *component, void *window) 168 { 169 // ... 170 // Obtain the ID of the <XComponent>, that is, the id parameter in the <XComponent> struct in the JS code. 171 char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = { '\0' }; 172 uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; 173 if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { 174 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback", 175 "OnSurfaceCreatedCB: Unable to get XComponent id"); 176 return; 177 } 178 179 // Initialize the environment and draw the background. 180 std::string id(idStr); 181 auto render = PluginRender::GetInstance(id); 182 uint64_t width; 183 uint64_t height; 184 // Obtain the size of the surface held by the <XComponent>. 185 int32_t xSize = OH_NativeXComponent_GetXComponentSize(component, window, &width, &height); 186 if ((xSize == OH_NATIVEXCOMPONENT_RESULT_SUCCESS) && (render != nullptr)) { 187 if (render->eglCore_->EglContextInit(window, width, height)) { 188 render->eglCore_->Background(); 189 } 190 } 191 } 192 193 // Define the OnSurfaceChangedCB() function. 194 void OnSurfaceChangedCB(OH_NativeXComponent *component, void *window) 195 { 196 // ... 197 // Obtain the ID of the <XComponent>. 198 char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = { '\0' }; 199 uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; 200 if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { 201 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback", 202 "OnSurfaceChangedCB: Unable to get XComponent id"); 203 return; 204 } 205 206 std::string id(idStr); 207 auto render = PluginRender::GetInstance(id); 208 if (render != nullptr) { 209 // Encapsulate the OnSurfaceChanged method. 210 render->OnSurfaceChanged(component, window); 211 } 212 } 213 214 // Define the OnSurfaceDestroyedCB() function and encapsulate in it the Release() method in the PluginRender class for releasing resources. 215 void OnSurfaceDestroyedCB(OH_NativeXComponent *component, void *window) 216 { 217 // ... 218 // Obtain the ID of the <XComponent>. 219 char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = { '\0' }; 220 uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; 221 if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { 222 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback", 223 "OnSurfaceDestroyedCB: Unable to get XComponent id"); 224 return; 225 } 226 227 std::string id(idStr); 228 // Release resources. 229 PluginRender::Release(id); 230 } 231 232 // Define the DispatchTouchEventCB() function, which is triggered to respond to a touch event. 233 void DispatchTouchEventCB(OH_NativeXComponent *component, void *window) 234 { 235 // ... 236 // Obtain the ID of the <XComponent>. 237 char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = { '\0' }; 238 uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; 239 if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { 240 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback", 241 "DispatchTouchEventCB: Unable to get XComponent id"); 242 return; 243 } 244 245 std::string id(idStr); 246 PluginRender *render = PluginRender::GetInstance(id); 247 if (render != nullptr) { 248 // Encapsulate the OnTouchEvent method. 249 render->OnTouchEvent(component, window); 250 } 251 } 252 253 // Define the DispatchMouseEventCB() function, which is triggered when a mouse event is responded to. 254 void DispatchMouseEventCB(OH_NativeXComponent *component, void *window) { 255 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Callback", "DispatchMouseEventCB"); 256 int32_t ret; 257 char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {}; 258 uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; 259 ret = OH_NativeXComponent_GetXComponentId(component, idStr, &idSize); 260 if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { 261 return; 262 } 263 264 std::string id(idStr); 265 auto render = PluginRender::GetInstance(id); 266 if (render) { 267 // Encapsulate the OnMouseEvent method. 268 render->OnMouseEvent(component, window); 269 } 270 } 271 272 // Define the DispatchHoverEventCB() function, which is triggered when the mouse pointer hover event is responded to. 273 void DispatchHoverEventCB(OH_NativeXComponent *component, bool isHover) { 274 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Callback", "DispatchHoverEventCB"); 275 int32_t ret; 276 char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {}; 277 uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; 278 ret = OH_NativeXComponent_GetXComponentId(component, idStr, &idSize); 279 if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { 280 return; 281 } 282 283 std::string id(idStr); 284 auto render = PluginRender::GetInstance(id); 285 if (render) { 286 // Encapsulate the OnHoverEvent method. 287 render->OnHoverEvent(component, isHover); 288 } 289 } 290 291 // Define the OnFocusEventCB() function, which is triggered when a focus obtaining event is responded to. 292 void OnFocusEventCB(OH_NativeXComponent *component, void *window) { 293 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Callback", "OnFocusEventCB"); 294 int32_t ret; 295 char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {}; 296 uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; 297 ret = OH_NativeXComponent_GetXComponentId(component, idStr, &idSize); 298 if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { 299 return; 300 } 301 302 std::string id(idStr); 303 auto render = PluginRender::GetInstance(id); 304 if (render) { 305 // Encapsulate the OnFocusEvent method. 306 render->OnFocusEvent(component, window); 307 } 308 } 309 310 // Define the OnBlurEventCB() function, which is triggered when the focus loss event is responded to. 311 void OnBlurEventCB(OH_NativeXComponent *component, void *window) { 312 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Callback", "OnBlurEventCB"); 313 int32_t ret; 314 char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {}; 315 uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; 316 ret = OH_NativeXComponent_GetXComponentId(component, idStr, &idSize); 317 if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { 318 return; 319 } 320 321 std::string id(idStr); 322 auto render = PluginRender::GetInstance(id); 323 if (render) { 324 // Encapsulate the OnBlurEvent method. 325 render->OnBlurEvent(component, window); 326 } 327 } 328 329 // Define the OnKeyEventCB() function, which is triggered when a key event is responded to. 330 void OnKeyEventCB(OH_NativeXComponent *component, void *window) { 331 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Callback", "OnKeyEventCB"); 332 int32_t ret; 333 char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {}; 334 uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; 335 ret = OH_NativeXComponent_GetXComponentId(component, idStr, &idSize); 336 if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { 337 return; 338 } 339 std::string id(idStr); 340 auto render = PluginRender::GetInstance(id); 341 if (render) { 342 // Encapsulate the OnKeyEvent method. 343 render->OnKeyEvent(component, window); 344 } 345 } 346 347 // Define an OnSurfaceChanged() method. 348 void PluginRender::OnSurfaceChanged(OH_NativeXComponent* component, void* window) 349 { 350 // ... 351 std::string id(idStr); 352 PluginRender* render = PluginRender::GetInstance(id); 353 double offsetX; 354 double offsetY; 355 // Obtain the offset of the surface held by the <XComponent> relative to the upper left corner of the window. 356 OH_NativeXComponent_GetXComponentOffset(component, window, &offsetX, &offsetY); 357 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "OH_NativeXComponent_GetXComponentOffset", 358 "offsetX = %{public}lf, offsetY = %{public}lf", offsetX, offsetY); 359 uint64_t width; 360 uint64_t height; 361 OH_NativeXComponent_GetXComponentSize(component, window, &width, &height); 362 if (render != nullptr) { 363 render->eglCore_->UpdateSize(width, height); 364 } 365 } 366 367 // Define an OnTouchEvent() method. 368 void PluginRender::OnTouchEvent(OH_NativeXComponent* component, void* window) 369 { 370 // ... 371 OH_NativeXComponent_TouchEvent touchEvent; 372 // Obtain the touch event triggered by the <XComponent>. 373 OH_NativeXComponent_GetTouchEvent(component, window, &touchEvent); 374 // Obtain the x coordinate of the <XComponent> touch point relative to the left edge of the <XComponent> and the y coordinate of the <XComponent> touch point relative to the upper edge of the <XComponent>. 375 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "OnTouchEvent", 376 "touch info: x = %{public}lf, y = %{public}lf", touchEvent.x, touchEvent.y); 377 // Obtain the x coordinate and y-coordinate of the <XComponent> touch point relative to the upper left corner of the application window where the <XComponent> is located. 378 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "OnTouchEvent", 379 "touch info: screenX = %{public}lf, screenY = %{public}lf", touchEvent.screenX, touchEvent.screenY); 380 std::string id(idStr); 381 PluginRender* render = PluginRender::GetInstance(id); 382 if (render != nullptr && touchEvent.type == OH_NativeXComponent_TouchEventType::OH_NATIVEXCOMPONENT_UP) { 383 render->eglCore_->ChangeColor(); 384 hasChangeColor_ = 1; 385 } 386 float tiltX = 0.0f; 387 float tiltY = 0.0f; 388 OH_NativeXComponent_TouchPointToolType toolType = 389 OH_NativeXComponent_TouchPointToolType::OH_NATIVEXCOMPONENT_TOOL_TYPE_UNKNOWN; 390 // Obtain the tool type of the <XComponent> touch point. 391 OH_NativeXComponent_GetTouchPointToolType(component, 0, &toolType); 392 // Obtain the tilt of the <XComponent> touch point relative to the x-axis. 393 OH_NativeXComponent_GetTouchPointTiltX(component, 0, &tiltX); 394 // Obtain the tilt of the <XComponent> touch point relative to the y-axis. 395 OH_NativeXComponent_GetTouchPointTiltY(component, 0, &tiltY); 396 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "OnTouchEvent", 397 "touch info: toolType = %{public}d, tiltX = %{public}lf, tiltY = %{public}lf", toolType, tiltX, tiltY); 398 } 399 400 // Define an OnMouseEvent() method. 401 void PluginRender::OnMouseEvent(OH_NativeXComponent *component, void *window) { 402 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "PluginRender", "OnMouseEvent"); 403 OH_NativeXComponent_MouseEvent mouseEvent; 404 // Obtain the mouse event triggered by the <XComponent>. 405 int32_t ret = OH_NativeXComponent_GetMouseEvent(component, window, &mouseEvent); 406 if (ret == OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { 407 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "PluginRender", "MouseEvent Info: x = %{public}f, y = %{public}f, action = %{public}d, button = %{public}d", mouseEvent.x, mouseEvent.y, mouseEvent.action, mouseEvent.button); 408 } else { 409 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginRender", "GetMouseEvent error"); 410 } 411 } 412 413 // Define an OnMouseEvent() method. 414 void PluginRender::OnKeyEvent(OH_NativeXComponent *component, void *window) { 415 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "PluginRender", "OnKeyEvent"); 416 417 OH_NativeXComponent_KeyEvent *keyEvent = nullptr; 418 // Obtain the key event triggered by the <XComponent>. 419 if (OH_NativeXComponent_GetKeyEvent(component, &keyEvent) >= 0) { 420 OH_NativeXComponent_KeyAction action; 421 // Obtain the action of a key event. 422 OH_NativeXComponent_GetKeyEventAction(keyEvent, &action); 423 OH_NativeXComponent_KeyCode code; 424 // Obtain the key code value of a key event. 425 OH_NativeXComponent_GetKeyEventCode(keyEvent, &code); 426 OH_NativeXComponent_EventSourceType sourceType; 427 // Obtain the input source type of a key event. 428 OH_NativeXComponent_GetKeyEventSourceType(keyEvent, &sourceType); 429 int64_t deviceId; 430 // Obtain the device ID of a key event. 431 OH_NativeXComponent_GetKeyEventDeviceId(keyEvent, &deviceId); 432 int64_t timeStamp; 433 // Obtain the timestamp of a key event. 434 OH_NativeXComponent_GetKeyEventTimestamp(keyEvent, &timeStamp); 435 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "PluginRender", "KeyEvent Info: action=%{public}d, code=%{public}d, sourceType=%{public}d, deviceId=%{public}ld, timeStamp=%{public}ld", action, code, sourceType, deviceId, timeStamp); 436 } else { 437 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginRender", "GetKeyEvent error"); 438 } 439 } 440 ``` 441 442 (2) Register the **\<XComponent>** event callback and call the method defined in step 3.1 when the **\<XComponent>** event is triggered. 443 444 ```c++ 445 void PluginRender::RegisterCallback(OH_NativeXComponent *nativeXComponent) { 446 // Set the callback of the component creation event. When the component is created, related operations are triggered to initialize the environment and draw the background. 447 renderCallback_.OnSurfaceCreated = OnSurfaceCreatedCB; 448 // Set the callback of the component change event. When the component changes, related operations are triggered. 449 renderCallback_.OnSurfaceChanged = OnSurfaceChangedCB; 450 // Set the callback of the component destruction event. When the component is destroyed, related operations are triggered to release the requested resources. 451 renderCallback_.OnSurfaceDestroyed = OnSurfaceDestroyedCB; 452 // Set the callback of the touch event. When the touch event is triggered, the Node-API is called to invoke the embedded C++ method. 453 renderCallback_.DispatchTouchEvent = DispatchTouchEventCB; 454 // Register OH_NativeXComponent_Callback with NativeXComponent. 455 OH_NativeXComponent_RegisterCallback(nativeXComponent, &renderCallback_); 456 457 // Set the callback of the mouse event. When the event is triggered, the Node-API is called to call the original C++ method. 458 mouseCallback_.DispatchMouseEvent = DispatchMouseEventCB; 459 // Set the callback of the mouse pointer hover event. When the event is triggered, the Node-API is called to call the original C++ method. 460 mouseCallback_.DispatchHoverEvent = DispatchHoverEventCB; 461 // Register OH_NativeXComponent_MouseEvent_Callback with NativeXComponent. 462 OH_NativeXComponent_RegisterMouseEventCallback(nativeXComponent, &mouseCallback_); 463 464 // Register the OnFocusEventCB method with NativeXComponent. 465 OH_NativeXComponent_RegisterFocusEventCallback(nativeXComponent, OnFocusEventCB); 466 // Register the OnKeyEventCB method with NativeXComponent. 467 OH_NativeXComponent_RegisterKeyEventCallback(nativeXComponent, OnKeyEventCB); 468 // Register the OnBlurEventCB method with NativeXComponent. 469 OH_NativeXComponent_RegisterBlurEventCallback(nativeXComponent, OnBlurEventCB); 470 } 471 ``` 472 473 (3) Define the **NapiDrawPattern** method, which will be called by the **drawPattern()** method exposed to the JS code. 474 475 ```c++ 476 napi_value PluginRender::NapiDrawPattern(napi_env env, napi_callback_info info) 477 { 478 // ... 479 // Obtain environment variables. 480 napi_value thisArg; 481 if (napi_get_cb_info(env, info, nullptr, nullptr, &thisArg, nullptr) != napi_ok) { 482 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginRender", "NapiDrawPattern: napi_get_cb_info fail"); 483 return nullptr; 484 } 485 486 // Obtain the XComponent instance from the environment variables. 487 napi_value exportInstance; 488 if (napi_get_named_property(env, thisArg, OH_NATIVE_XCOMPONENT_OBJ, &exportInstance) != napi_ok) { 489 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginRender", 490 "NapiDrawPattern: napi_get_named_property fail"); 491 return nullptr; 492 } 493 494 // Use napi_unwrap to obtain the pointer to the XComponent instance. 495 OH_NativeXComponent *nativeXComponent = nullptr; 496 if (napi_unwrap(env, exportInstance, reinterpret_cast<void **>(&nativeXComponent)) != napi_ok) { 497 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginRender", "NapiDrawPattern: napi_unwrap fail"); 498 return nullptr; 499 } 500 501 // Obtain the ID of the XComponent. 502 char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = { '\0' }; 503 uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; 504 if (OH_NativeXComponent_GetXComponentId(nativeXComponent, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { 505 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginRender", 506 "NapiDrawPattern: Unable to get XComponent id"); 507 return nullptr; 508 } 509 510 std::string id(idStr); 511 PluginRender *render = PluginRender::GetInstance(id); 512 if (render) { 513 // Call the drawing method. 514 render->eglCore_->Draw(); 515 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "PluginRender", "render->eglCore_->Draw() executed"); 516 } 517 return nullptr; 518 } 519 ``` 520 5214. Initialize the environment, including initializing the available EGLDisplay, determining the available surface configuration, creating the rendering area surface, and creating and associating the context. 522 523 ```c++ 524 void EGLCore::UpdateSize(int width, int height) 525 { 526 width_ = width; 527 height_ = height; 528 if (width_ > 0) { 529 // Calculate the width percentage of the drawn rectangle. 530 width_Percent_ = FIFTY_PERCENT * height_ / width_; 531 } 532 } 533 534 bool EGLCore::EglContextInit(void *window, int width, int height) 535 { 536 // ... 537 UpdateSize(width, height); 538 eglWindow_ = static_cast<EGLNativeWindowType>(window); 539 540 // Initialize the display. 541 eglDisplay_ = eglGetDisplay(EGL_DEFAULT_DISPLAY); 542 if (eglDisplay_ == EGL_NO_DISPLAY) { 543 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglGetDisplay: unable to get EGL display"); 544 return false; 545 } 546 547 // Initialize the EGL. 548 EGLint majorVersion; 549 EGLint minorVersion; 550 if (!eglInitialize(eglDisplay_, &majorVersion, &minorVersion)) { 551 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", 552 "eglInitialize: unable to get initialize EGL display"); 553 return false; 554 } 555 556 // Select the configuration. 557 const EGLint maxConfigSize = 1; 558 EGLint numConfigs; 559 if (!eglChooseConfig(eglDisplay_, ATTRIB_LIST, &eglConfig_, maxConfigSize, &numConfigs)) { 560 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglChooseConfig: unable to choose configs"); 561 return false; 562 } 563 564 // Create an environment. 565 return CreateEnvironment(); 566 } 567 ``` 568 569 ```c++ 570 bool EGLCore::CreateEnvironment() 571 { 572 // ... 573 // Create a surface. 574 eglSurface_ = eglCreateWindowSurface(eglDisplay_, eglConfig_, eglWindow_, NULL); 575 576 // ... 577 // Create a context. 578 eglContext_ = eglCreateContext(eglDisplay_, eglConfig_, EGL_NO_CONTEXT, CONTEXT_ATTRIBS); 579 if (!eglMakeCurrent(eglDisplay_, eglSurface_, eglSurface_, eglContext_)) { 580 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglMakeCurrent failed"); 581 return false; 582 } 583 584 // Create a program. 585 program_ = CreateProgram(VERTEX_SHADER, FRAGMENT_SHADER); 586 if (program_ == PROGRAM_ERROR) { 587 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "CreateProgram: unable to create program"); 588 return false; 589 } 590 return true; 591 } 592 ``` 593 5945. Implement the rendering function. 595 596 (1) Draw the background. 597 598 ```c++ 599 // Draw the background color #f4f4f4. 600 const GLfloat BACKGROUND_COLOR[] = { 244.0f / 255, 244.0f / 255, 244.0f / 255, 1.0f }; 601 602 // Draw the background vertex. 603 const GLfloat BACKGROUND_RECTANGLE_VERTICES[] = { 604 -1.0f, 1.0f, 605 1.0f, 1.0f, 606 1.0f, -1.0f, 607 -1.0f, -1.0f 608 }; 609 ``` 610 611 ```c++ 612 // Draw the background color. 613 void EGLCore::Background() 614 { 615 GLint position = PrepareDraw(); 616 if (position == POSITION_ERROR) { 617 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Background get position failed"); 618 return; 619 } 620 621 if (!ExecuteDraw(position, BACKGROUND_COLOR, BACKGROUND_RECTANGLE_VERTICES, 622 sizeof(BACKGROUND_RECTANGLE_VERTICES))) { 623 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Background execute draw failed"); 624 return; 625 } 626 627 if (!FinishDraw()) { 628 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Background FinishDraw failed"); 629 return; 630 } 631 } 632 633 // Prepare for drawing and obtain the value of position. When the creation is successful, the value of position starts from 0. 634 GLint EGLCore::PrepareDraw() 635 { 636 if ((eglDisplay_ == nullptr) || (eglSurface_ == nullptr) || (eglContext_ == nullptr) || 637 (!eglMakeCurrent(eglDisplay_, eglSurface_, eglSurface_, eglContext_))) { 638 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "PrepareDraw: param error"); 639 return POSITION_ERROR; 640 } 641 642 glViewport(DEFAULT_X_POSITION, DEFAULT_Y_POSITION, width_, height_); 643 glClearColor(GL_RED_DEFAULT, GL_GREEN_DEFAULT, GL_BLUE_DEFAULT, GL_ALPHA_DEFAULT); 644 glClear(GL_COLOR_BUFFER_BIT); 645 glUseProgram(program_); 646 647 return glGetAttribLocation(program_, POSITION_NAME); 648 } 649 650 // Draw a specified color in the specified area based on the input parameters. 651 bool EGLCore::ExecuteDraw(GLint position, const GLfloat *color, const GLfloat shapeVertices[], 652 unsigned long vertSize) 653 { 654 if ((position > 0) || (color == nullptr) || (vertSize / sizeof(shapeVertices[0]) != SHAPE_VERTICES_SIZE)) { 655 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ExecuteDraw: param error"); 656 return false; 657 } 658 659 glVertexAttribPointer(position, POINTER_SIZE, GL_FLOAT, GL_FALSE, 0, shapeVertices); 660 glEnableVertexAttribArray(position); 661 glVertexAttrib4fv(1, color); 662 glDrawArrays(GL_TRIANGLE_FAN, 0, TRIANGLE_FAN_SIZE); 663 glDisableVertexAttribArray(position); 664 665 return true; 666 } 667 668 // End the drawing operation. 669 bool EGLCore::FinishDraw() 670 { 671 // Forcibly flush the buffer. 672 glFlush(); 673 glFinish(); 674 return eglSwapBuffers(eglDisplay_, eglSurface_); 675 } 676 ``` 677 678 (2) Draw the shape. 679 680 ```c++ 681 void EGLCore::Draw() 682 { 683 flag_ = false; 684 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "EGLCore", "Draw"); 685 GLint position = PrepareDraw(); 686 if (position == POSITION_ERROR) { 687 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw get position failed"); 688 return; 689 } 690 691 // Draw the background. 692 if (!ExecuteDraw(position, BACKGROUND_COLOR, BACKGROUND_RECTANGLE_VERTICES, 693 sizeof(BACKGROUND_RECTANGLE_VERTICES))) { 694 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw background failed"); 695 return; 696 } 697 698 // Divide the pentagon into five quadrilaterals and calculate the four vertices of one of the quadrilaterals. 699 GLfloat rotateX = 0; 700 GLfloat rotateY = FIFTY_PERCENT * height_; 701 GLfloat centerX = 0; 702 GLfloat centerY = -rotateY * (M_PI / 180 * 54) * (M_PI / 180 * 18); 703 GLfloat leftX = -rotateY * (M_PI / 180 * 18); 704 GLfloat leftY = 0; 705 GLfloat rightX = rotateY * (M_PI / 180 * 18); 706 GLfloat rightY = 0; 707 708 // Determine the vertices for drawing the quadrilateral, which are represented by the percentages of the drawing area. 709 const GLfloat shapeVertices[] = { 710 centerX / width_, centerY / height_, 711 leftX / width_, leftY / height_, 712 rotateX / width_, rotateY / height_, 713 rightX / width_, rightY / height_ 714 }; 715 716 if (!ExecuteDrawStar(position, DRAW_COLOR, shapeVertices, sizeof(shapeVertices))) { 717 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw star failed"); 718 return; 719 } 720 721 GLfloat rad = M_PI / 180 * 72; 722 for (int i = 0; i < 4; ++i) 723 { 724 // Obtain the vertices of the other four quadrilaterals through rotation. 725 rotate2d(centerX, centerY, &rotateX, &rotateY,rad); 726 rotate2d(centerX, centerY, &leftX, &leftY,rad); 727 rotate2d(centerX, centerY, &rightX, &rightY,rad); 728 729 // Determine the vertices for drawing the quadrilateral, which are represented by the percentages of the drawing area. 730 const GLfloat shapeVertices[] = { 731 centerX / width_, centerY / height_, 732 leftX / width_, leftY / height_, 733 rotateX / width_, rotateY / height_, 734 rightX / width_, rightY / height_ 735 }; 736 737 // Draw the shape. 738 if (!ExecuteDrawStar(position, DRAW_COLOR, shapeVertices, sizeof(shapeVertices))) { 739 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw star failed"); 740 return; 741 } 742 } 743 744 // End drawing. 745 if (!FinishDraw()) { 746 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw FinishDraw failed"); 747 return; 748 } 749 750 flag_ = true; 751 } 752 ``` 753 754 (3) Change the colors, by drawing a new shape with the same size but different colors and replacing the original shape with the new shape. 755 756 ```c++ 757 void EGLCore::ChangeColor() 758 { 759 if (!flag_) { 760 return; 761 } 762 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "EGLCore", "ChangeColor"); 763 GLint position = PrepareDraw(); 764 if (position == POSITION_ERROR) { 765 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ChangeColor get position failed"); 766 return; 767 } 768 769 // Draw the background. 770 if (!ExecuteDraw(position, BACKGROUND_COLOR, BACKGROUND_RECTANGLE_VERTICES, 771 sizeof(BACKGROUND_RECTANGLE_VERTICES))) { 772 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ChangeColor execute draw background failed"); 773 return; 774 } 775 776 // Determine the vertices for drawing the quadrilateral, which are represented by the percentages of the drawing area. 777 GLfloat rotateX = 0; 778 GLfloat rotateY = FIFTY_PERCENT * height_; 779 GLfloat centerX = 0; 780 GLfloat centerY = -rotateY * (M_PI / 180 * 54) * (M_PI / 180 * 18); 781 GLfloat leftX = -rotateY * (M_PI / 180 * 18); 782 GLfloat leftY = 0; 783 GLfloat rightX = rotateY * (M_PI / 180 * 18); 784 GLfloat rightY = 0; 785 786 // Determine the vertices for drawing the quadrilateral, which are represented by the percentages of the drawing area. 787 const GLfloat shapeVertices[] = { 788 centerX / width_, centerY / height_, 789 leftX / width_, leftY / height_, 790 rotateX / width_, rotateY / height_, 791 rightX / width_, rightY / height_ 792 }; 793 794 // Use the new colors for drawing. 795 if (!ExecuteDrawStar2(position, CHANGE_COLOR, shapeVertices, sizeof(shapeVertices))) { 796 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw star failed"); 797 return; 798 } 799 800 GLfloat rad = M_PI / 180 * 72; 801 for (int i = 0; i < 4; ++i) 802 { 803 // Obtain the vertices of the other four quadrilaterals through rotation. 804 rotate2d(centerX, centerY, &rotateX, &rotateY,rad); 805 rotate2d(centerX, centerY, &leftX, &leftY,rad); 806 rotate2d(centerX, centerY, &rightX, &rightY,rad); 807 808 // Determine the vertices for drawing the quadrilateral, which are represented by the percentages of the drawing area. 809 const GLfloat shapeVertices[] = { 810 centerX / width_, centerY / height_, 811 leftX / width_, leftY / height_, 812 rotateX / width_, rotateY / height_, 813 rightX / width_, rightY / height_ 814 }; 815 816 // Use the new colors for drawing. 817 if (!ExecuteDrawStar2(position, CHANGE_COLOR, shapeVertices, sizeof(shapeVertices))) { 818 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw star failed"); 819 return; 820 } 821 } 822 823 // End drawing. 824 if (!FinishDraw()) { 825 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ChangeColor FinishDraw failed"); 826 } 827 } 828 ``` 829 8306. Release related resources. 831 832 (1) Create the **Release()** method in the **EGLCore** class to release the resources requested during environment initialization, including the window display, rendering area surface, and environment context. 833 834 ```c++ 835 void EGLCore::Release() 836 { 837 // Release the surface. 838 if ((eglDisplay_ == nullptr) || (eglSurface_ == nullptr) || (!eglDestroySurface(eglDisplay_, eglSurface_))) { 839 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Release eglDestroySurface failed"); 840 } 841 // Release the context. 842 if ((eglDisplay_ == nullptr) || (eglContext_ == nullptr) || (!eglDestroyContext(eglDisplay_, eglContext_))) { 843 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Release eglDestroyContext failed"); 844 } 845 // Release the display. 846 if ((eglDisplay_ == nullptr) || (!eglTerminate(eglDisplay_))) { 847 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Release eglTerminate failed"); 848 } 849 } 850 ``` 851 852 (2) Add the **Release()** method to the **PluginRender** class to release the **EGLCore** and **PluginRender** instances. 853 854 ```c++ 855 void PluginRender::Release(std::string &id) 856 { 857 PluginRender *render = PluginRender::GetInstance(id); 858 if (render != nullptr) { 859 render->eglCore_->Release(); 860 delete render->eglCore_; 861 render->eglCore_ = nullptr; 862 delete render; 863 render = nullptr; 864 instance_.erase(instance_.find(id)); 865 } 866 } 867 ``` 868 8697. Use the CMake toolchain to compile the C++ source code into a dynamic link library (DLL) file. 870 871 ```CMake 872 # Set the minimum CMake version. 873 cmake_minimum_required(VERSION 3.4.1) 874 # Project name 875 project(XComponent) 876 877 set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}) 878 add_definitions(-DOHOS_PLATFORM) 879 # Set the header file search directory. 880 include_directories( 881 ${NATIVERENDER_ROOT_PATH} 882 ${NATIVERENDER_ROOT_PATH}/include 883 ) 884 # Add the **nativerender** dynamic library, with the **libnativerender.so** library file. Add the .cpp files. 885 add_library(nativerender SHARED 886 render/egl_core.cpp 887 render/plugin_render.cpp 888 manager/plugin_manager.cpp 889 napi_init.cpp 890 ) 891 892 find_library( 893 EGL-lib 894 EGL 895 ) 896 897 find_library( 898 GLES-lib 899 GLESv3 900 ) 901 902 find_library( 903 hilog-lib 904 hilog_ndk.z 905 ) 906 907 find_library( 908 libace-lib 909 ace_ndk.z 910 ) 911 912 find_library( 913 libnapi-lib 914 ace_napi.z 915 ) 916 917 find_library( 918 libuv-lib 919 uv 920 ) 921 # Add the libraries to be linked. 922 target_link_libraries(nativerender PUBLIC 923 ${EGL-lib} ${GLES-lib} ${hilog-lib} ${libace-lib} ${libnapi-lib} ${libuv-lib}) 924 ``` 925 926