1# Custom Rendering (XComponent) 2 3## Overview 4 5The **XComponent** is a rendering component that can be used for EGL/OpenGL ES and media data output. It uses the held [NativeWindow](../graphics/native-window-guidelines.md) to render graphics and is typically employed to meet complex custom rendering needs, such as displaying camera preview streams and rendering game graphics. You can specify different rendering methods through the **type** field, which are [XComponentType](../reference/apis-arkui/arkui-ts/ts-appendix-enums.md#xcomponenttype10).SURFACE and XComponentType.TEXTURE. For the SURFACE type, you display the custom drawing content on the screen separately. For the TEXTURE type, you combine custom drawing content with the content of the **XComponent** and display it on the screen. 6 7The **XComponent** is mainly used in two scenarios. In the native XComponent scenario, the native layer is responsible for obtaining the native **XComponent** instance and registering the lifecycle callbacks of the **XComponent** along with touch, mouse, and key event callbacks. In the ArkTS XComponent scenario, the **SurfaceId** is obtained on the ArkTS side, with lifecycle callbacks, touch, mouse, and key event callbacks all being managed and triggered there. 8 9## Native XComponent Scenario 10Specify the dynamic library to be loaded with the **libraryname** parameter of the **XComponent** constructor. The application can then obtain the native **XComponent** instance at the native layer. This instance, provided by the **XComponent** itself, acts as a bridge, facilitating interactions between the ArkTS layer and the native layer. The NDK APIs provided by the **XComponent** all depend on this instance. The provided APIs include those for obtaining a **NativeWindow** instance, 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: 11 12- Register the lifecycle and event callbacks of the **XComponent**. 13- Initialize the environment, obtain the current state, and respond to various events via these callbacks. 14- Use the **NativeWindow** instance with EGL APIs to develop custom drawing content, and apply for and submit buffers to the graphics queue. 15 16**Available APIs** 17 18| API | Description | 19| ------------------------------------------------------------ | ------------------------------------------------------------ | 20| OH_NativeXComponent_GetXComponentId(OH_NativeXComponent* component, char* id, uint64_t* size) | Obtains the ID of an **XComponent**. | 21| 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**. | 22| OH_NativeXComponent_GetXComponentOffset(OH_NativeXComponent* component, const void* window, double* x, double* y) | Obtains the offset of the surface held by the **XComponent** relative to the upper left corner of its parent component. | 23| 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/apis-arkui/_o_h___native_x_component___touch_event.md).| 24| OH_NativeXComponent_GetTouchPointToolType(OH_NativeXComponent* component, uint32_t pointIndex, OH_NativeXComponent_TouchPointToolType* toolType) | Obtains the tool type of an **XComponent** touch point. | 25| OH_NativeXComponent_GetTouchPointTiltX(OH_NativeXComponent* component, uint32_t pointIndex, float* tiltX) | Obtains the tilt of an **XComponent** touch point relative to the x-axis. | 26| OH_NativeXComponent_GetTouchPointTiltY(OH_NativeXComponent* component, uint32_t pointIndex, float* tiltY) | Obtains the tilt of an **XComponent** touch point relative to the y-axis. | 27| OH_NativeXComponent_GetMouseEvent(OH_NativeXComponent* component, const void* window, OH_NativeXComponent_MouseEvent* mouseEvent) | Obtains the mouse event triggered by an **XComponent**. | 28| OH_NativeXComponent_RegisterCallback(OH_NativeXComponent* component, OH_NativeXComponent_Callback* callback) | Registers a lifecycle or touch event callback for an **OH_NativeXComponent** instance. | 29| OH_NativeXComponent_RegisterMouseEventCallback(OH_NativeXComponent* component, OH_NativeXComponent_MouseEvent_Callback* callback) | Registers the mouse event callback for an **OH_NativeXComponent** instance. | 30| OH_NativeXComponent_RegisterFocusEventCallback(OH_NativeXComponent* component, void (\*callback)(OH_NativeXComponent* component, void* window)) | Registers the focus obtaining event callback for an **OH_NativeXComponent** instance. | 31| OH_NativeXComponent_RegisterKeyEventCallback(OH_NativeXComponent* component, void (\*callback)(OH_NativeXComponent* component, void* window)) | Registers the key event callback for an **OH_NativeXComponent** instance. | 32| OH_NativeXComponent_RegisterBlurEventCallback(OH_NativeXComponent* component, void (\*callback)(OH_NativeXComponent* component, void* window)) | Registers the focus loss event callback for an **OH_NativeXComponent** instance. | 33| OH_NativeXComponent_GetKeyEvent(OH_NativeXComponent* component, OH_NativeXComponent_KeyEvent\** keyEvent) | Obtains the key event triggered by an **XComponent**. | 34| OH_NativeXComponent_GetKeyEventAction(OH_NativeXComponent_KeyEvent* keyEvent, OH_NativeXComponent_KeyAction* action) | Obtains the action of a key event. | 35| OH_NativeXComponent_GetKeyEventCode(OH_NativeXComponent_KeyEvent* keyEvent, OH_NativeXComponent_KeyCode* code) | Obtains the key code value of a key event. | 36| OH_NativeXComponent_GetKeyEventSourceType(OH_NativeXComponent_KeyEvent* keyEvent, OH_NativeXComponent_EventSourceType* sourceType) | Obtains the input source type of a key event. | 37| OH_NativeXComponent_GetKeyEventDeviceId(OH_NativeXComponent_KeyEvent* keyEvent, int64_t* deviceId) | Obtains the device ID of a key event. | 38| OH_NativeXComponent_GetKeyEventTimestamp(OH_NativeXComponent_KeyEvent* keyEvent, int64_t* timestamp) | Obtains the timestamp of a key event. | 39 40> **NOTE** 41> 42> The preceding APIs do not support cross-thread access. 43> 44> When the XComponent is destroyed (after the **onSurfaceDestroyed** callback is triggered), the **OH_NativeXComponent** and window objects obtained through the preceding APIs will be released. If these object are used again, crashes may occur due to the use of dangling or null pointers. 45 46**How to Develop** 47 48The following uses the SURFACE type as an example to describe how to use the **XComponent** to call the Node-API to create an EGL/GLES environment, implement drawing graphics on the main page, and change the graphics color. 49 501. Define the **XComponent** on the UI. 51 52 ```typescript 53 // Declare the native APIs in ets/interface/XComponentContext.ts. 54 export default interface XComponentContext { 55 drawPattern(): void; 56 57 getStatus(): XComponentContextStatus; 58 }; 59 60 type XComponentContextStatus = { 61 hasDraw: boolean, 62 hasChangeColor: boolean, 63 }; 64 ``` 65 66 ```typescript 67 import XComponentContext from "../interface/XComponentContext" 68 69 @Entry 70 @Component 71 struct Index { 72 xComponentContext: XComponentContext | undefined = undefined; 73 xComponentAttrs: XComponentAttrs = { 74 id: 'xcomponentId', 75 type: XComponentType.SURFACE, 76 libraryname: 'nativerender' 77 } 78 79 build() { 80 Row() { 81 // ... 82 // Define XComponent in an .ets file. 83 XComponent(this.xComponentAttrs) 84 .focusable(true) // Set the component to be able to respond to key events. 85 .onLoad((xComponentContext) => { 86 console.log("onLoad"); 87 this.xComponentContext = xComponentContext as XComponentContext; 88 89 // Call drawPattern to draw content. 90 if (this.xComponentContext) { 91 this.xComponentContext.drawPattern(); 92 if (this.xComponentContext.getStatus()) { 93 this.xComponentContext.getStatus().hasDraw; 94 } 95 } 96 }) 97 .onDestroy(() => { 98 console.log("onDestroy"); 99 }) 100 // ... 101 } 102 .onClick(() => { 103 // Call getStatus to change the drawing content. 104 if (this.xComponentContext && this.xComponentContext.getStatus()) { 105 this.xComponentContext.getStatus().hasChangeColor; 106 } 107 }) 108 .height('100%') 109 } 110 } 111 112 interface XComponentAttrs { 113 id: string; 114 type: number; 115 libraryname: string; 116 } 117 ``` 118 1192. Register the Node-API module. For details, see [Node-API Development Specifications](../napi/napi-guidelines.md). 120 121 ```c++ 122 #include <hilog/log.h> 123 #include "common/common.h" 124 #include "manager/plugin_manager.h" 125 126 // In the napi_init.cpp file, use the Init method to register the target function to pass in the encapsulated C++ methods for the ArkTS method to call. 127 EXTERN_C_START 128 static napi_value Init(napi_env env, napi_value exports) { 129 // ... 130 // Expose the getContext() API to the ArkTS code. 131 napi_property_descriptor desc[] = { 132 { "getContext", nullptr, PluginManager::GetContext, nullptr, nullptr, nullptr, napi_default, nullptr } 133 }; 134 if (napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc) != napi_ok) { 135 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Init", "napi_define_properties failed"); 136 return nullptr; 137 } 138 // Check whether the environment variable contains an instance of XComponent. If the instance exists, export the drawing-related API. 139 PluginManager::GetInstance()->Export(env, exports); 140 return exports; 141 } 142 EXTERN_C_END 143 144 // Write the API description. You can modify parameters as required. 145 static napi_module nativerenderModule = { 146 .nm_version = 1, 147 .nm_flags = 0, 148 .nm_filename = nullptr, 149 // Entry point function 150 .nm_register_func = Init,// Specify the callback for when the corresponding module is loaded. 151 // Module name 152 .nm_modname = 153 "nativerender", // Specify the module name. The name must be the same as the value of libraryname in the XComponent on the ArkTS side. 154 .nm_priv = ((void *)0), 155 .reserved = {0}}; 156 157 // 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. 158 extern "C" __attribute__((constructor)) void RegisterModule(void) { napi_module_register(&nativerenderModule); } 159 ``` 160 161 ```c++ 162 // Check whether the environment variable contains an instance of XComponent. If the instance exists, export the drawing-related API. 163 void PluginManager::Export(napi_env env, napi_value exports) 164 { 165 if ((env == nullptr) || (exports == nullptr)) { 166 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "Export: env or exports is null"); 167 return; 168 } 169 170 napi_value exportInstance = nullptr; 171 if (napi_get_named_property(env, exports, OH_NATIVE_XCOMPONENT_OBJ, &exportInstance) != napi_ok) { 172 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "Export: napi_get_named_property fail"); 173 } 174 175 // Obtain a native XComponent. 176 OH_NativeXComponent* nativeXComponent = nullptr; 177 if (napi_unwrap(env, exportInstance, reinterpret_cast<void**>(&nativeXComponent)) != napi_ok) { 178 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "Export: napi_unwrap fail"); 179 return; 180 } 181 182 // Obtain the ID of the XComponent, that is, the id parameter in the XComponent struct in the ArkTS code. 183 char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {'\0'}; 184 uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; 185 if (OH_NativeXComponent_GetXComponentId(nativeXComponent, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { 186 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", 187 "Export: OH_NativeXComponent_GetXComponentId fail"); 188 return; 189 } 190 191 std::string id(idStr); 192 auto context = PluginManager::GetInstance(); 193 if ((context != nullptr) && (nativeXComponent != nullptr)) { 194 context->SetNativeXComponent(id, nativeXComponent); 195 auto render = context->GetRender(id); 196 if (render != nullptr) { 197 // Register the callbacks. 198 render->RegisterCallback(nativeXComponent); 199 // Use Node-API in the method to export drawing-related APIs to expose them to the ArkTS side. 200 render->Export(env, exports); 201 } 202 } 203 } 204 ``` 205 ```c++ 206 // Use the napi_define_properties method to expose the drawPattern() and getStatus() methods to the ArkTS side. 207 // Call drawPattern() on the ArkTS side to draw content, and call getStatus() to change the drawing content. 208 void PluginRender::Export(napi_env env, napi_value exports) 209 { 210 // ... 211 // Register the functions as the drawPattern() and getStatus() APIs on the ArkTS side. 212 napi_property_descriptor desc[] = { 213 {"drawPattern", nullptr, PluginRender::NapiDrawPattern, nullptr, nullptr, nullptr, napi_default, nullptr}, 214 {"getStatus", nullptr, PluginRender::TestGetXComponentStatus, nullptr, nullptr, nullptr, napi_default, 215 nullptr}}; 216 if (napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc) != napi_ok) { 217 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginRender", "Export: napi_define_properties failed"); 218 } 219 } 220 ``` 221 2223. Register the **XComponent** event callback and use the Node-API to implement it. 223 224 (1) Define the callbacks for the touch event of the **XComponent** and for when a surface is successfully created, changed, or destroyed. 225 226 ```c++ 227 // Define the PluginRender class in the header file. 228 class PluginRender { 229 public: 230 explicit PluginRender(std::string& id); 231 ~PluginRender() { 232 if (eglCore_ != nullptr) { 233 eglCore_->Release(); 234 delete eglCore_; 235 eglCore_ = nullptr; 236 } 237 } 238 static PluginRender* GetInstance(std::string& id); 239 static void Release(std::string& id); 240 static napi_value NapiDrawPattern(napi_env env, napi_callback_info info); 241 static napi_value TestGetXComponentStatus(napi_value env, napi_callback_info info); 242 void Export(napi_env env, napi_value exports); 243 void OnSurfaceChanged(OH_NativeXComponent* component, void* window); 244 void OnTouchEvent(OH_NativeXComponent* component, void* window); 245 void OnMouseEvent(OH_NativeXComponent* component, void* window); 246 void OnHoverEvent(OH_NativeXComponent* component, bool isHover); 247 void OnFocusEvent(OH_NativeXComponent* component, void* window); 248 void OnBlurEvent(OH_NativeXComponent* component, void* window); 249 void OnKeyEvent(OH_NativeXComponent* component, void* window); 250 void RegisterCallback(OH_NativeXComponent* NativeXComponent); 251 252 public: 253 static std::unordered_map<std::string, PluginRender*> instance_; 254 EGLCore* eglCore_; 255 std::string id_; 256 static int32_t hasDraw_; 257 static int32_t hasChangeColor_; 258 259 private: 260 OH_NativeXComponent_Callback renderCallback_; 261 OH_NativeXComponent_MouseEvent_Callback mouseCallback_; 262 }; 263 ``` 264 265 ```c++ 266 // Implement methods of the PluginRender class in the source file. 267 std::unordered_map<std::string, PluginRender *> PluginRender::instance_; 268 int32_t PluginRender::hasDraw_ = 0; 269 int32_t PluginRender::hasChangeColor_ = 0; 270 271 PluginRender::PluginRender(std::string& id) { 272 this->id_ = id; 273 this->eglCore_ = new EGLCore(); 274 } 275 276 PluginRender* PluginRender::GetInstance(std::string& id) { 277 if (instance_.find(id) == instance_.end()) { 278 PluginRender* instance = new PluginRender(id); 279 instance_[id] = instance; 280 return instance; 281 } else { 282 return instance_[id]; 283 } 284 } 285 286 // Define the OnSurfaceCreatedCB() function to encapsulate the initialization environment and drawing background. 287 void OnSurfaceCreatedCB(OH_NativeXComponent *component, void *window) { 288 // ... 289 // Obtain the ID of the XComponent, that is, the id parameter in the XComponent struct in the ArkTS code. 290 char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {'\0'}; 291 uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; 292 if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { 293 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback", 294 "OnSurfaceCreatedCB: Unable to get XComponent id"); 295 return; 296 } 297 298 // Initialize the environment and draw the background. 299 std::string id(idStr); 300 auto render = PluginRender::GetInstance(id); 301 uint64_t width; 302 uint64_t height; 303 // Obtain the size of the surface held by the XComponent. 304 int32_t xSize = OH_NativeXComponent_GetXComponentSize(component, window, &width, &height); 305 if ((xSize == OH_NATIVEXCOMPONENT_RESULT_SUCCESS) && (render != nullptr)) { 306 if (render->eglCore_->EglContextInit(window, width, height)) { 307 render->eglCore_->Background(); 308 } 309 } 310 } 311 312 // Define the OnSurfaceChangedCB() function. 313 void OnSurfaceChangedCB(OH_NativeXComponent *component, void *window) { 314 // ... 315 // Obtain the ID of the XComponent. 316 char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {'\0'}; 317 uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; 318 if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { 319 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback", 320 "OnSurfaceChangedCB: Unable to get XComponent id"); 321 return; 322 } 323 324 std::string id(idStr); 325 auto render = PluginRender::GetInstance(id); 326 if (render != nullptr) { 327 // Encapsulate the OnSurfaceChanged method. 328 render->OnSurfaceChanged(component, window); 329 } 330 } 331 332 // Define the OnSurfaceDestroyedCB() function and encapsulate in it the Release() method in the PluginRender class for releasing resources. 333 void OnSurfaceDestroyedCB(OH_NativeXComponent *component, void *window) { 334 // ... 335 // Obtain the ID of the XComponent. 336 char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {'\0'}; 337 uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; 338 if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { 339 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback", 340 "OnSurfaceDestroyedCB: Unable to get XComponent id"); 341 return; 342 } 343 344 std::string id(idStr); 345 // Release resources. 346 PluginRender::Release(id); 347 } 348 349 // Define the DispatchTouchEventCB() function, which is triggered to respond to a touch event. 350 void DispatchTouchEventCB(OH_NativeXComponent *component, void *window) { 351 // ... 352 // Obtain the ID of the XComponent. 353 char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = { '\0' }; 354 uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; 355 if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { 356 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback", 357 "DispatchTouchEventCB: Unable to get XComponent id"); 358 return; 359 } 360 361 std::string id(idStr); 362 PluginRender *render = PluginRender::GetInstance(id); 363 if (render != nullptr) { 364 // Encapsulate the OnTouchEvent method. 365 render->OnTouchEvent(component, window); 366 } 367 } 368 369 // Define the DispatchMouseEventCB() function, which is triggered when a mouse event is responded to. 370 void DispatchMouseEventCB(OH_NativeXComponent *component, void *window) { 371 // ... 372 int32_t ret; 373 char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {}; 374 uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; 375 ret = OH_NativeXComponent_GetXComponentId(component, idStr, &idSize); 376 if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { 377 return; 378 } 379 380 std::string id(idStr); 381 auto render = PluginRender::GetInstance(id); 382 if (render) { 383 // Encapsulate the OnMouseEvent method. 384 render->OnMouseEvent(component, window); 385 } 386 } 387 388 // Define the DispatchHoverEventCB() function, which is triggered when the mouse pointer hover event is responded to. 389 void DispatchHoverEventCB(OH_NativeXComponent *component, bool isHover) { 390 // ... 391 int32_t ret; 392 char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {}; 393 uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; 394 ret = OH_NativeXComponent_GetXComponentId(component, idStr, &idSize); 395 if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { 396 return; 397 } 398 399 std::string id(idStr); 400 auto render = PluginRender::GetInstance(id); 401 if (render) { 402 // Encapsulate the OnHoverEvent method. 403 render->OnHoverEvent(component, isHover); 404 } 405 } 406 407 // Define the OnFocusEventCB() function, which is triggered when a focus obtaining event is responded to. 408 void OnFocusEventCB(OH_NativeXComponent *component, void *window) { 409 // ... 410 int32_t ret; 411 char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {}; 412 uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; 413 ret = OH_NativeXComponent_GetXComponentId(component, idStr, &idSize); 414 if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { 415 return; 416 } 417 418 std::string id(idStr); 419 auto render = PluginRender::GetInstance(id); 420 if (render) { 421 // Encapsulate the OnFocusEvent method. 422 render->OnFocusEvent(component, window); 423 } 424 } 425 426 // Define the OnBlurEventCB() function, which is triggered when the focus loss event is responded to. 427 void OnBlurEventCB(OH_NativeXComponent *component, void *window) { 428 // ... 429 int32_t ret; 430 char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {}; 431 uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; 432 ret = OH_NativeXComponent_GetXComponentId(component, idStr, &idSize); 433 if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { 434 return; 435 } 436 437 std::string id(idStr); 438 auto render = PluginRender::GetInstance(id); 439 if (render) { 440 // Encapsulate the OnBlurEvent method. 441 render->OnBlurEvent(component, window); 442 } 443 } 444 445 // Define the OnKeyEventCB() function, which is triggered when a key event is responded to. 446 void OnKeyEventCB(OH_NativeXComponent *component, void *window) { 447 // ... 448 int32_t ret; 449 char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {}; 450 uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; 451 ret = OH_NativeXComponent_GetXComponentId(component, idStr, &idSize); 452 if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { 453 return; 454 } 455 456 std::string id(idStr); 457 auto render = PluginRender::GetInstance(id); 458 if (render) { 459 // Encapsulate the OnKeyEvent method. 460 render->OnKeyEvent(component, window); 461 } 462 } 463 464 // Define an OnSurfaceChanged() method. 465 void PluginRender::OnSurfaceChanged(OH_NativeXComponent* component, void* window) { 466 char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = { '\0' }; 467 uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; 468 if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { 469 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback", "OnSurfaceChanged: Unable to get XComponent id"); 470 return; 471 } 472 473 std::string id(idStr); 474 PluginRender* render = PluginRender::GetInstance(id); 475 double offsetX; 476 double offsetY; 477 // Obtain the offset of the surface held by the XComponent relative to the upper left corner of its parent component. 478 OH_NativeXComponent_GetXComponentOffset(component, window, &offsetX, &offsetY); 479 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "OH_NativeXComponent_GetXComponentOffset", 480 "offsetX = %{public}lf, offsetY = %{public}lf", offsetX, offsetY); 481 uint64_t width; 482 uint64_t height; 483 OH_NativeXComponent_GetXComponentSize(component, window, &width, &height); 484 if (render != nullptr) { 485 render->eglCore_->UpdateSize(width, height); 486 } 487 } 488 489 // Define an OnTouchEvent() method. 490 void PluginRender::OnTouchEvent(OH_NativeXComponent* component, void* window) { 491 char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = { '\0' }; 492 uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; 493 if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { 494 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback", 495 "OnTouchEvent: Unable to get XComponent id"); 496 return; 497 } 498 499 OH_NativeXComponent_TouchEvent touchEvent; 500 // Obtain the touch event triggered by the XComponent. 501 OH_NativeXComponent_GetTouchEvent(component, window, &touchEvent); 502 // 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. 503 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "OnTouchEvent", "touch info: x = %{public}lf, y = %{public}lf", 504 touchEvent.x, touchEvent.y); 505 // 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. 506 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "OnTouchEvent", 507 "touch info: screenX = %{public}lf, screenY = %{public}lf", touchEvent.screenX, touchEvent.screenY); 508 std::string id(idStr); 509 PluginRender* render = PluginRender::GetInstance(id); 510 if (render != nullptr && touchEvent.type == OH_NativeXComponent_TouchEventType::OH_NATIVEXCOMPONENT_UP) { 511 render->eglCore_->ChangeColor(); 512 hasChangeColor_ = 1; 513 } 514 float tiltX = 0.0f; 515 float tiltY = 0.0f; 516 OH_NativeXComponent_TouchPointToolType toolType = 517 OH_NativeXComponent_TouchPointToolType::OH_NATIVEXCOMPONENT_TOOL_TYPE_UNKNOWN; 518 // Obtain the tool type of the XComponent touch point. 519 OH_NativeXComponent_GetTouchPointToolType(component, 0, &toolType); 520 // Obtain the tilt of the XComponent touch point relative to the x-axis. 521 OH_NativeXComponent_GetTouchPointTiltX(component, 0, &tiltX); 522 // Obtain the tilt of the XComponent touch point relative to the y-axis. 523 OH_NativeXComponent_GetTouchPointTiltY(component, 0, &tiltY); 524 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "OnTouchEvent", 525 "touch info: toolType = %{public}d, tiltX = %{public}lf, tiltY = %{public}lf", toolType, tiltX, tiltY); 526 } 527 528 // Define an OnMouseEvent() method. 529 void PluginRender::OnMouseEvent(OH_NativeXComponent *component, void *window) { 530 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "PluginRender", "OnMouseEvent"); 531 OH_NativeXComponent_MouseEvent mouseEvent; 532 // Obtain the mouse event triggered by the XComponent. 533 int32_t ret = OH_NativeXComponent_GetMouseEvent(component, window, &mouseEvent); 534 if (ret == OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { 535 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "PluginRender", 536 "MouseEvent Info: x = %{public}f, y = %{public}f, action = %{public}d, button = %{public}d", 537 mouseEvent.x, mouseEvent.y, mouseEvent.action, mouseEvent.button); 538 } else { 539 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginRender", "GetMouseEvent error"); 540 } 541 } 542 543 // Define an OnHoverEvent() method. 544 void PluginRender::OnHoverEvent(OH_NativeXComponent* component, bool isHover) { 545 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "PluginRender", "OnHoverEvent isHover_ = %{public}d", isHover); 546 } 547 548 // Define an OnFocusEvent() method. 549 void PluginRender::OnFocusEvent(OH_NativeXComponent* component, void* window) { 550 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "PluginRender", "OnFocusEvent"); 551 } 552 553 // Define an OnBlurEvent() method. 554 void PluginRender::OnBlurEvent(OH_NativeXComponent* component, void* window) { 555 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "PluginRender", "OnBlurEvent"); 556 } 557 558 // Define an OnKeyEvent() method. 559 void PluginRender::OnKeyEvent(OH_NativeXComponent *component, void *window) { 560 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "PluginRender", "OnKeyEvent"); 561 562 OH_NativeXComponent_KeyEvent *keyEvent = nullptr; 563 // Obtain the key event triggered by the XComponent. 564 if (OH_NativeXComponent_GetKeyEvent(component, &keyEvent) >= 0) { 565 OH_NativeXComponent_KeyAction action; 566 // Obtain the action of a key event. 567 OH_NativeXComponent_GetKeyEventAction(keyEvent, &action); 568 OH_NativeXComponent_KeyCode code; 569 // Obtain the key code value of a key event. 570 OH_NativeXComponent_GetKeyEventCode(keyEvent, &code); 571 OH_NativeXComponent_EventSourceType sourceType; 572 // Obtain the input source type of a key event. 573 OH_NativeXComponent_GetKeyEventSourceType(keyEvent, &sourceType); 574 int64_t deviceId; 575 // Obtain the device ID of a key event. 576 OH_NativeXComponent_GetKeyEventDeviceId(keyEvent, &deviceId); 577 int64_t timeStamp; 578 // Obtain the timestamp of a key event. 579 OH_NativeXComponent_GetKeyEventTimestamp(keyEvent, &timeStamp); 580 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "PluginRender", 581 "KeyEvent Info: action=%{public}d, code=%{public}d, sourceType=%{public}d, deviceId=%{public}ld, " 582 "timeStamp=%{public}ld", 583 action, code, sourceType, deviceId, timeStamp); 584 } else { 585 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginRender", "GetKeyEvent error"); 586 } 587 } 588 ``` 589 590 (2) Register the **XComponent** event callback and call the method defined in step 3.1 when the **XComponent** event is triggered. 591 592 ```c++ 593 void PluginRender::RegisterCallback(OH_NativeXComponent *NativeXComponent) { 594 // 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. 595 renderCallback_.OnSurfaceCreated = OnSurfaceCreatedCB; 596 // Set the callback of the component change event. When the component changes, related operations are triggered. 597 renderCallback_.OnSurfaceChanged = OnSurfaceChangedCB; 598 // Set the callback of the component destruction event. When the component is destroyed, related operations are triggered to release the requested resources. 599 renderCallback_.OnSurfaceDestroyed = OnSurfaceDestroyedCB; 600 // Set the callback of the touch event. When the touch event is triggered, the Node-API is called to invoke the embedded C++ method. 601 renderCallback_.DispatchTouchEvent = DispatchTouchEventCB; 602 // Register OH_NativeXComponent_Callback with NativeXComponent. 603 OH_NativeXComponent_RegisterCallback(NativeXComponent, &renderCallback_); 604 605 // Set the callback of the mouse event. When the touch event is triggered, the Node-API is called to invoke the embedded C++ method. 606 mouseCallback_.DispatchMouseEvent = DispatchMouseEventCB; 607 // Set the callback of the mouse hover event. When the touch event is triggered, the Node-API is called to invoke the embedded C++ method. 608 mouseCallback_.DispatchHoverEvent = DispatchHoverEventCB; 609 // Register OH_NativeXComponent_MouseEvent_Callback with NativeXComponent. 610 OH_NativeXComponent_RegisterMouseEventCallback(NativeXComponent, &mouseCallback_); 611 612 // Register the OnFocusEventCB method with NativeXComponent. 613 OH_NativeXComponent_RegisterFocusEventCallback(NativeXComponent, OnFocusEventCB); 614 // Register the OnKeyEventCB method with NativeXComponent. 615 OH_NativeXComponent_RegisterKeyEventCallback(NativeXComponent, OnKeyEventCB); 616 // Register the OnBlurEventCB method with NativeXComponent. 617 OH_NativeXComponent_RegisterBlurEventCallback(NativeXComponent, OnBlurEventCB); 618 } 619 ``` 620 621 (3) Define the **NapiDrawPattern** method, which will be called by the **drawPattern()** method exposed to the ArkTS side. 622 623 ```c++ 624 napi_value PluginRender::NapiDrawPattern(napi_env env, napi_callback_info info) { 625 // ... 626 // Obtain environment variables. 627 napi_value thisArg; 628 if (napi_get_cb_info(env, info, nullptr, nullptr, &thisArg, nullptr) != napi_ok) { 629 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginRender", "NapiDrawPattern: napi_get_cb_info fail"); 630 return nullptr; 631 } 632 633 // Obtain the XComponent instance from the environment variables. 634 napi_value exportInstance; 635 if (napi_get_named_property(env, thisArg, OH_NATIVE_XCOMPONENT_OBJ, &exportInstance) != napi_ok) { 636 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginRender", 637 "NapiDrawPattern: napi_get_named_property fail"); 638 return nullptr; 639 } 640 641 // Use napi_unwrap to obtain the pointer to the XComponent instance. 642 OH_NativeXComponent *NativeXComponent = nullptr; 643 if (napi_unwrap(env, exportInstance, reinterpret_cast<void **>(&NativeXComponent)) != napi_ok) { 644 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginRender", "NapiDrawPattern: napi_unwrap fail"); 645 return nullptr; 646 } 647 648 // Obtain the ID of the XComponent. 649 char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = { '\0' }; 650 uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; 651 if (OH_NativeXComponent_GetXComponentId(NativeXComponent, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { 652 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginRender", 653 "NapiDrawPattern: Unable to get XComponent id"); 654 return nullptr; 655 } 656 657 std::string id(idStr); 658 PluginRender *render = PluginRender::GetInstance(id); 659 if (render) { 660 // Call the drawing method. 661 render->eglCore_->Draw(); 662 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "PluginRender", "render->eglCore_->Draw() executed"); 663 } 664 return nullptr; 665 } 666 ``` 667 668 (4) Define the **TestGetXComponentStatus** method, which will be called by the **getStatus()** method exposed to the ArkTS side. 669 670 ```c++ 671 napi_value PluginRender::TestGetXComponentStatus(napi_env env, napi_callback_info info) { 672 napi_value hasDraw; 673 napi_value hasChangeColor; 674 675 napi_create_int32(env, hasDraw_, &(hasDraw)); 676 napi_create_int32(env, hasChangeColor_, &(hasChangeColor)); 677 678 napi_value obj; 679 napi_create_object(env, &obj); 680 napi_set_named_property(env, obj, "hasDraw", hasDraw); 681 napi_set_named_property(env, obj, "hasChangeColor", hasChangeColor); 682 683 return obj; 684 } 685 ``` 686 6874. Initialize the environment, including initializing the available EGLDisplay, determining the available surface configuration, creating the rendering area surface, and creating and associating the context. 688 689 ```c++ 690 void EGLCore::UpdateSize(int width, int height) { 691 // width_ and height_ are defined in the header file. 692 width_ = width; 693 height_ = height; 694 } 695 696 bool EGLCore::EglContextInit(void *window, int width, int height) { 697 // ... 698 UpdateSize(width, height); 699 eglWindow_ = static_cast<EGLNativeWindowType>(window); 700 701 // Initialize the display. 702 eglDisplay_ = eglGetDisplay(EGL_DEFAULT_DISPLAY); 703 if (eglDisplay_ == EGL_NO_DISPLAY) { 704 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglGetDisplay: unable to get EGL display"); 705 return false; 706 } 707 708 // Initialize the EGL. 709 EGLint majorVersion; 710 EGLint minorVersion; 711 if (!eglInitialize(eglDisplay_, &majorVersion, &minorVersion)) { 712 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", 713 "eglInitialize: unable to get initialize EGL display"); 714 return false; 715 } 716 717 // Select the configuration. 718 const EGLint maxConfigSize = 1; 719 EGLint numConfigs; 720 if (!eglChooseConfig(eglDisplay_, ATTRIB_LIST, &eglConfig_, maxConfigSize, &numConfigs)) { 721 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglChooseConfig: unable to choose configs"); 722 return false; 723 } 724 725 // Create an environment. 726 return CreateEnvironment(); 727 } 728 ``` 729 730 ```c++ 731 bool EGLCore::CreateEnvironment() { 732 // ... 733 // Create a surface. 734 eglSurface_ = eglCreateWindowSurface(eglDisplay_, eglConfig_, eglWindow_, NULL); 735 736 // ... 737 // Create a context. 738 eglContext_ = eglCreateContext(eglDisplay_, eglConfig_, EGL_NO_CONTEXT, CONTEXT_ATTRIBS); 739 if (!eglMakeCurrent(eglDisplay_, eglSurface_, eglSurface_, eglContext_)) { 740 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglMakeCurrent failed"); 741 return false; 742 } 743 744 // Create a program. 745 program_ = CreateProgram(VERTEX_SHADER, FRAGMENT_SHADER); 746 if (program_ == PROGRAM_ERROR) { 747 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "CreateProgram: unable to create program"); 748 return false; 749 } 750 return true; 751 } 752 753 GLuint EGLCore::CreateProgram(const char* vertexShader, const char* fragShader) { 754 if ((vertexShader == nullptr) || (fragShader == nullptr)) { 755 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", 756 "createProgram: vertexShader or fragShader is null"); 757 return PROGRAM_ERROR; 758 } 759 760 GLuint vertex = LoadShader(GL_VERTEX_SHADER, vertexShader); 761 if (vertex == PROGRAM_ERROR) { 762 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "createProgram vertex error"); 763 return PROGRAM_ERROR; 764 } 765 766 GLuint fragment = LoadShader(GL_FRAGMENT_SHADER, fragShader); 767 if (fragment == PROGRAM_ERROR) { 768 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "createProgram fragment error"); 769 return PROGRAM_ERROR; 770 } 771 772 GLuint program = glCreateProgram(); 773 if (program == PROGRAM_ERROR) { 774 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "createProgram program error"); 775 glDeleteShader(vertex); 776 glDeleteShader(fragment); 777 return PROGRAM_ERROR; 778 } 779 780 // The gl function has no return value. 781 glAttachShader(program, vertex); 782 glAttachShader(program, fragment); 783 glLinkProgram(program); 784 785 GLint linked; 786 glGetProgramiv(program, GL_LINK_STATUS, &linked); 787 if (linked != 0) { 788 glDeleteShader(vertex); 789 glDeleteShader(fragment); 790 return program; 791 } 792 793 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "createProgram linked error"); 794 GLint infoLen = 0; 795 glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLen); 796 if (infoLen > 1) { 797 char* infoLog = (char*)malloc(sizeof(char) * (infoLen + 1)); 798 memset(infoLog, 0, infoLen + 1); 799 glGetProgramInfoLog(program, infoLen, nullptr, infoLog); 800 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "glLinkProgram error = %s", infoLog); 801 free(infoLog); 802 infoLog = nullptr; 803 } 804 glDeleteShader(vertex); 805 glDeleteShader(fragment); 806 glDeleteProgram(program); 807 return PROGRAM_ERROR; 808 } 809 810 GLuint EGLCore::LoadShader(GLenum type, const char* shaderSrc) { 811 if ((type <= 0) || (shaderSrc == nullptr)) { 812 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "glCreateShader type or shaderSrc error"); 813 return PROGRAM_ERROR; 814 } 815 816 GLuint shader = glCreateShader(type); 817 if (shader == 0) { 818 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "glCreateShader unable to load shader"); 819 return PROGRAM_ERROR; 820 } 821 822 // The gl function has no return value. 823 glShaderSource(shader, 1, &shaderSrc, nullptr); 824 glCompileShader(shader); 825 826 GLint compiled; 827 glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); 828 if (compiled != 0) { 829 return shader; 830 } 831 832 GLint infoLen = 0; 833 glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen); 834 if (infoLen <= 1) { 835 glDeleteShader(shader); 836 return PROGRAM_ERROR; 837 } 838 839 char *infoLog = (char*)malloc(sizeof(char) * (infoLen + 1)); 840 if (infoLog != nullptr) { 841 memset(infoLog, 0, infoLen + 1); 842 glGetShaderInfoLog(shader, infoLen, nullptr, infoLog); 843 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "glCompileShader error = %s", infoLog); 844 free(infoLog); 845 infoLog = nullptr; 846 } 847 glDeleteShader(shader); 848 return PROGRAM_ERROR; 849 } 850 851 ``` 852 8535. Implement the rendering function. 854 855 (1) Draw the background. 856 857 ```c++ 858 // ... 859 // Background color #f4f4f4 860 const GLfloat BACKGROUND_COLOR[] = { 244.0f / 255, 244.0f / 255, 244.0f / 255, 1.0f }; 861 862 // Drawing pattern color 863 const GLfloat DRAW_COLOR[] = {126.0f / 255, 143.0f / 255, 251.0f / 255, 1.0f}; 864 865 // Changed drawing pattern color 866 const GLfloat CHANGE_COLOR[] = {146.0f / 255, 214.0f / 255, 204.0f / 255, 1.0f}; 867 868 // Vertices for the background rectangle 869 const GLfloat BACKGROUND_RECTANGLE_VERTICES[] = { 870 -1.0f, 1.0f, 871 1.0f, 1.0f, 872 1.0f, -1.0f, 873 -1.0f, -1.0f 874 }; 875 // ... 876 ``` 877 878 ```c++ 879 // Background color 880 void EGLCore::Background() { 881 GLint position = PrepareDraw(); 882 if (position == POSITION_ERROR) { 883 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Background get position failed"); 884 return; 885 } 886 887 if (!ExecuteDraw(position, BACKGROUND_COLOR, BACKGROUND_RECTANGLE_VERTICES, 888 sizeof(BACKGROUND_RECTANGLE_VERTICES))) { 889 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Background execute draw failed"); 890 return; 891 } 892 893 if (!FinishDraw()) { 894 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Background FinishDraw failed"); 895 return; 896 } 897 } 898 899 // Prepare for drawing and obtain the value of position. When the creation is successful, the value of position starts from 0. 900 GLint EGLCore::PrepareDraw() { 901 if ((eglDisplay_ == nullptr) || (eglSurface_ == nullptr) || (eglContext_ == nullptr) || 902 (!eglMakeCurrent(eglDisplay_, eglSurface_, eglSurface_, eglContext_))) { 903 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "PrepareDraw: param error"); 904 return POSITION_ERROR; 905 } 906 907 glViewport(DEFAULT_X_POSITION, DEFAULT_Y_POSITION, width_, height_); 908 glClearColor(GL_RED_DEFAULT, GL_GREEN_DEFAULT, GL_BLUE_DEFAULT, GL_ALPHA_DEFAULT); 909 glClear(GL_COLOR_BUFFER_BIT); 910 glUseProgram(program_); 911 912 return glGetAttribLocation(program_, POSITION_NAME); 913 } 914 915 // Draw a specified color in the specified area based on the input parameters. 916 bool EGLCore::ExecuteDraw(GLint position, const GLfloat *color, const GLfloat shapeVertices[], unsigned long vertSize) { 917 if ((position > 0) || (color == nullptr) || (vertSize / sizeof(shapeVertices[0]) != SHAPE_VERTICES_SIZE)) { 918 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ExecuteDraw: param error"); 919 return false; 920 } 921 922 glVertexAttribPointer(position, POINTER_SIZE, GL_FLOAT, GL_FALSE, 0, shapeVertices); 923 glEnableVertexAttribArray(position); 924 glVertexAttrib4fv(1, color); 925 glDrawArrays(GL_TRIANGLE_FAN, 0, TRIANGLE_FAN_SIZE); 926 glDisableVertexAttribArray(position); 927 928 return true; 929 } 930 931 // End the drawing operation. 932 bool EGLCore::FinishDraw() { 933 // Forcibly flush the buffer. 934 glFlush(); 935 glFinish(); 936 return eglSwapBuffers(eglDisplay_, eglSurface_); 937 } 938 ``` 939 940 (2) Draw the shape. 941 942 ```c++ 943 void EGLCore::Draw() { 944 flag_ = false; 945 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "EGLCore", "Draw"); 946 GLint position = PrepareDraw(); 947 if (position == POSITION_ERROR) { 948 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw get position failed"); 949 return; 950 } 951 952 // Draw the background. 953 if (!ExecuteDraw(position, BACKGROUND_COLOR, BACKGROUND_RECTANGLE_VERTICES, 954 sizeof(BACKGROUND_RECTANGLE_VERTICES))) { 955 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw background failed"); 956 return; 957 } 958 959 // Divide the pentagon into five quadrilaterals and calculate the four vertices of one of the quadrilaterals. 960 GLfloat rotateX = 0; 961 GLfloat rotateY = FIFTY_PERCENT * height_; 962 GLfloat centerX = 0; 963 GLfloat centerY = -rotateY * (M_PI / 180 * 54) * (M_PI / 180 * 18); 964 GLfloat leftX = -rotateY * (M_PI / 180 * 18); 965 GLfloat leftY = 0; 966 GLfloat rightX = rotateY * (M_PI / 180 * 18); 967 GLfloat rightY = 0; 968 969 // Determine the vertices for drawing the quadrilateral, which are represented by the percentages of the drawing area. 970 const GLfloat shapeVertices[] = { 971 centerX / width_, centerY / height_, 972 leftX / width_, leftY / height_, 973 rotateX / width_, rotateY / height_, 974 rightX / width_, rightY / height_ 975 }; 976 977 if (!ExecuteDrawStar(position, DRAW_COLOR, shapeVertices, sizeof(shapeVertices))) { 978 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw star failed"); 979 return; 980 } 981 982 GLfloat rad = M_PI / 180 * 72; 983 for (int i = 0; i < 4; ++i) 984 { 985 // Obtain the vertices of the other four quadrilaterals through rotation. 986 rotate2d(centerX, centerY, &rotateX, &rotateY,rad); 987 rotate2d(centerX, centerY, &leftX, &leftY,rad); 988 rotate2d(centerX, centerY, &rightX, &rightY,rad); 989 990 // Determine the vertices for drawing the quadrilateral, which are represented by the percentages of the drawing area. 991 const GLfloat shapeVertices[] = { 992 centerX / width_, centerY / height_, 993 leftX / width_, leftY / height_, 994 rotateX / width_, rotateY / height_, 995 rightX / width_, rightY / height_ 996 }; 997 998 // Draw the shape. 999 if (!ExecuteDrawStar(position, DRAW_COLOR, shapeVertices, sizeof(shapeVertices))) { 1000 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw star failed"); 1001 return; 1002 } 1003 } 1004 1005 // End drawing. 1006 if (!FinishDraw()) { 1007 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw FinishDraw failed"); 1008 return; 1009 } 1010 1011 flag_ = true; 1012 } 1013 ``` 1014 1015 (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. 1016 1017 ```c++ 1018 void EGLCore::ChangeColor() { 1019 if (!flag_) { 1020 return; 1021 } 1022 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "EGLCore", "ChangeColor"); 1023 GLint position = PrepareDraw(); 1024 if (position == POSITION_ERROR) { 1025 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ChangeColor get position failed"); 1026 return; 1027 } 1028 1029 // Draw the background. 1030 if (!ExecuteDraw(position, BACKGROUND_COLOR, BACKGROUND_RECTANGLE_VERTICES, 1031 sizeof(BACKGROUND_RECTANGLE_VERTICES))) { 1032 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ChangeColor execute draw background failed"); 1033 return; 1034 } 1035 1036 // Determine the vertices for drawing the quadrilateral, which are represented by the percentages of the drawing area. 1037 GLfloat rotateX = 0; 1038 GLfloat rotateY = FIFTY_PERCENT * height_; 1039 GLfloat centerX = 0; 1040 GLfloat centerY = -rotateY * (M_PI / 180 * 54) * (M_PI / 180 * 18); 1041 GLfloat leftX = -rotateY * (M_PI / 180 * 18); 1042 GLfloat leftY = 0; 1043 GLfloat rightX = rotateY * (M_PI / 180 * 18); 1044 GLfloat rightY = 0; 1045 1046 // Determine the vertices for drawing the quadrilateral, which are represented by the percentages of the drawing area. 1047 const GLfloat shapeVertices[] = { 1048 centerX / width_, centerY / height_, 1049 leftX / width_, leftY / height_, 1050 rotateX / width_, rotateY / height_, 1051 rightX / width_, rightY / height_ 1052 }; 1053 1054 // Use the new colors for drawing. 1055 if (!ExecuteDrawNewStar(position, CHANGE_COLOR, shapeVertices, sizeof(shapeVertices))) { 1056 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw star failed"); 1057 return; 1058 } 1059 1060 GLfloat rad = M_PI / 180 * 72; 1061 for (int i = 0; i < 4; ++i) { 1062 // Obtain the vertices of the other four quadrilaterals through rotation. 1063 rotate2d(centerX, centerY, &rotateX, &rotateY,rad); 1064 rotate2d(centerX, centerY, &leftX, &leftY,rad); 1065 rotate2d(centerX, centerY, &rightX, &rightY,rad); 1066 1067 // Determine the vertices for drawing the quadrilateral, which are represented by the percentages of the drawing area. 1068 const GLfloat shapeVertices[] = { 1069 centerX / width_, centerY / height_, 1070 leftX / width_, leftY / height_, 1071 rotateX / width_, rotateY / height_, 1072 rightX / width_, rightY / height_ 1073 }; 1074 1075 // Use the new colors for drawing. 1076 if (!ExecuteDrawNewStar(position, CHANGE_COLOR, shapeVertices, sizeof(shapeVertices))) { 1077 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw star failed"); 1078 return; 1079 } 1080 } 1081 1082 // End drawing. 1083 if (!FinishDraw()) { 1084 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ChangeColor FinishDraw failed"); 1085 } 1086 } 1087 1088 bool EGLCore::ExecuteDrawNewStar( 1089 GLint position, const GLfloat* color, const GLfloat shapeVertices[], unsigned long vertSize) { 1090 if ((position > 0) || (color == nullptr) || (vertSize / sizeof(shapeVertices[0])) != SHAPE_VERTICES_SIZE) { 1091 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ExecuteDraw: param error"); 1092 return false; 1093 } 1094 1095 // The gl function has no return value. 1096 glVertexAttribPointer(position, POINTER_SIZE, GL_FLOAT, GL_FALSE, 0, shapeVertices); 1097 glEnableVertexAttribArray(position); 1098 glVertexAttrib4fv(1, color); 1099 glDrawArrays(GL_TRIANGLE_FAN, 0, TRIANGLE_FAN_SIZE); 1100 glDisableVertexAttribArray(position); 1101 1102 return true; 1103 } 1104 ``` 1105 11066. Release related resources. 1107 1108 (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. 1109 1110 ```c++ 1111 void EGLCore::Release() { 1112 // Release the surface. 1113 if ((eglDisplay_ == nullptr) || (eglSurface_ == nullptr) || (!eglDestroySurface(eglDisplay_, eglSurface_))) { 1114 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Release eglDestroySurface failed"); 1115 } 1116 // Release the context. 1117 if ((eglDisplay_ == nullptr) || (eglContext_ == nullptr) || (!eglDestroyContext(eglDisplay_, eglContext_))) { 1118 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Release eglDestroyContext failed"); 1119 } 1120 // Release the display. 1121 if ((eglDisplay_ == nullptr) || (!eglTerminate(eglDisplay_))) { 1122 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Release eglTerminate failed"); 1123 } 1124 } 1125 ``` 1126 1127 (2) Add the **Release()** method to the **PluginRender** class to release the **EGLCore** and **PluginRender** instances. 1128 1129 ```c++ 1130 void PluginRender::Release(std::string &id) { 1131 PluginRender *render = PluginRender::GetInstance(id); 1132 if (render != nullptr) { 1133 render->eglCore_->Release(); 1134 delete render->eglCore_; 1135 render->eglCore_ = nullptr; 1136 instance_.erase(instance_.find(id)); 1137 } 1138 } 1139 ``` 1140 11417. Configure the specific CMakeLists to use the CMake toolchain to compile the C++ source code into a dynamic link library file. 1142 1143 ```CMake 1144 # Set the minimum CMake version. 1145 cmake_minimum_required(VERSION 3.4.1) 1146 # Project name 1147 project(XComponent) 1148 1149 set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}) 1150 add_definitions(-DOHOS_PLATFORM) 1151 # Set the header file search directory. 1152 include_directories( 1153 ${NATIVERENDER_ROOT_PATH} 1154 ${NATIVERENDER_ROOT_PATH}/include 1155 ) 1156 # Add the **nativerender** dynamic library, with the **libnativerender.so** library file. Add the .cpp files. 1157 add_library(nativerender SHARED 1158 render/egl_core.cpp 1159 render/plugin_render.cpp 1160 manager/plugin_manager.cpp 1161 napi_init.cpp 1162 ) 1163 1164 find_library( 1165 EGL-lib 1166 EGL 1167 ) 1168 1169 find_library( 1170 GLES-lib 1171 GLESv3 1172 ) 1173 1174 find_library( 1175 hilog-lib 1176 hilog_ndk.z 1177 ) 1178 1179 find_library( 1180 libace-lib 1181 ace_ndk.z 1182 ) 1183 1184 find_library( 1185 libnapi-lib 1186 ace_napi.z 1187 ) 1188 1189 find_library( 1190 libuv-lib 1191 uv 1192 ) 1193 # Add the libraries to be linked. 1194 target_link_libraries(nativerender PUBLIC 1195 ${EGL-lib} ${GLES-lib} ${hilog-lib} ${libace-lib} ${libnapi-lib} ${libuv-lib}) 1196 ``` 1197 1198## ArkTS XComponent Scenario 1199 1200Unlike the native **XComponent**, the ArkTS **XComponent** does not require the **libraryname** parameter. It obtains the **SurfaceId** on the ArkTS side, and layout information, lifecycle callbacks, touch, mouse, key event callbacks, and more are all triggered on the ArkTS side and passed to the native side for processing as needed. The development mainly involves the following scenarios: 1201- Use the **SurfaceId** obtained on the ArkTS side to call the **OH_NativeWindow_CreateNativeWindowFromSurfaceId** API on the native side to create a **NativeWindow** instance. 1202- Use the **NativeWindow** instance with EGL APIs to develop custom drawing content, and apply for and submit buffers to the graphics queue. 1203- Obtain lifecycle and event information on the ArkTS side and pass it to the native side for processing. 1204 1205**Available APIs** 1206 1207XComponentController on the ArkTS side 1208 1209| API | Description | 1210| ------------------------------------------------------------ | ------------------------------------------------------------ | 1211| getXComponentSurfaceId(): string | Obtains the ID of the surface associated with the **XComponent**. | 1212| onSurfaceCreated(surfaceId: string): void | Called when the surface held by the **XComponent** is created. | 1213| onSurfaceChanged(surfaceId: string, rect: SurfaceRect): void | Called when the size of the surface held by the **XComponent** changes (including the initial size change upon creation).| 1214| onSurfaceDestroyed(surfaceId: string): void | Called when the surface held by the **XComponent** is destroyed. | 1215 1216Native side 1217 1218| API | Description | 1219| ------------------------------------------------------------ | ------------------------------------------------------------ | 1220| int32_t OH_NativeWindow_CreateNativeWindowFromSurfaceId (uint64_t surfaceId, OHNativeWindow **window ) | Creates an **OHNativeWindow** instance based on a surface ID. | 1221| void OH_NativeWindow_DestroyNativeWindow (OHNativeWindow* window) | Decreases the reference count of an **OHNativeWindow** instance by 1 and when the reference count reaches 0, destroys the instance.| 1222 1223**How to Develop** 1224 1225The following uses the SURFACE type as an example to describe how to use the **XComponent** to pass in **SurfaceId** on the ArkTS side, create a **NativeWindow** instance on the native side, then create an EGL/GLES environment, implement drawing graphics on the main page, and change the graphics color. 1226 12271. Define the **XComponent** on the UI. 1228 1229 ```javascript 1230 // Function declarations defined in cpp/types/libnativerender/Index.d.ts 1231 type XComponentContextStatus = { 1232 hasDraw: boolean, 1233 hasChangeColor: boolean, 1234 }; 1235 export const SetSurfaceId: (id: BigInt) => any; 1236 export const ChangeSurface: (id: BigInt, w: number, h: number) =>any; 1237 export const DrawPattern: (id: BigInt) => any; 1238 export const GetXComponentStatus: (id: BigInt) => XComponentContextStatus 1239 export const ChangeColor: (id: BigInt) => any; 1240 export const DestroySurface: (id: BigInt) => any; 1241 ``` 1242 1243 ```typescript 1244 import nativeRender from 'libnativerender.so' 1245 1246 // Override XComponentController to set lifecycle callbacks. 1247 class MyXComponentController extends XComponentController { 1248 onSurfaceCreated(surfaceId: string): void { 1249 console.log(`onSurfaceCreated surfaceId: ${surfaceId}`) 1250 nativeRender.SetSurfaceId(BigInt(surfaceId)); 1251 } 1252 1253 onSurfaceChanged(surfaceId: string, rect: SurfaceRect): void { 1254 console.log(`onSurfaceChanged surfaceId: ${surfaceId}, rect: ${JSON.stringify(rect)}}`) 1255 // Call ChangeSurface to draw content in onSurfaceChanged. 1256 nativeRender.ChangeSurface(BigInt(surfaceId), rect.surfaceWidth, rect.surfaceHeight) 1257 } 1258 1259 onSurfaceDestroyed(surfaceId: string): void { 1260 console.log(`onSurfaceDestroyed surfaceId: ${surfaceId}`) 1261 nativeRender.DestroySurface(BigInt(surfaceId)) 1262 } 1263 } 1264 1265 @Entry 1266 @Component 1267 struct Index { 1268 @State currentStatus: string = "index"; 1269 xComponentController: XComponentController = new MyXComponentController(); 1270 1271 build() { 1272 Column() { 1273 //... 1274 // Define XComponent in an .ets file. 1275 Column({ space: 10 }) { 1276 XComponent({ 1277 type: XComponentType.SURFACE, 1278 controller: this.xComponentController 1279 }) 1280 Text(this.currentStatus) 1281 .fontSize('24fp') 1282 .fontWeight(500) 1283 } 1284 .onClick(() => { 1285 let surfaceId = this.xComponentController.getXComponentSurfaceId() 1286 nativeRender.ChangeColor(BigInt(surfaceId)) 1287 let hasChangeColor: boolean = false; 1288 if (nativeRender.GetXComponentStatus(BigInt(surfaceId))) { 1289 hasChangeColor = nativeRender.GetXComponentStatus(BigInt(surfaceId)).hasChangeColor; 1290 } 1291 if (hasChangeColor) { 1292 this.currentStatus = "change color"; 1293 } 1294 }) 1295 1296 //... 1297 Row() { 1298 Button('Draw Star') 1299 .fontSize('16fp') 1300 .fontWeight(500) 1301 .margin({ bottom: 24 }) 1302 .onClick(() => { 1303 let surfaceId = this.xComponentController.getXComponentSurfaceId() 1304 nativeRender.DrawPattern(BigInt(surfaceId)) 1305 let hasDraw: boolean = false; 1306 if (nativeRender.GetXComponentStatus(BigInt(surfaceId))) { 1307 hasDraw = nativeRender.GetXComponentStatus(BigInt(surfaceId)).hasDraw; 1308 } 1309 if (hasDraw) { 1310 this.currentStatus = "draw star" 1311 } 1312 }) 1313 .width('53.6%') 1314 .height(40) 1315 } 1316 .width('100%') 1317 .justifyContent(FlexAlign.Center) 1318 .alignItems(VerticalAlign.Bottom) 1319 .layoutWeight(1) 1320 } 1321 .width('100%') 1322 .height('100%') 1323 } 1324 } 1325 ``` 1326 13272. Register the Node-API module. For details, see [Node-API Development Specifications](../napi/napi-guidelines.md). 1328 1329 ```typescript 1330 #include <hilog/log.h> 1331 #include "common/common.h" 1332 #include "manager/plugin_manager.h" 1333 namespace NativeXComponentSample { 1334 // In the napi_init.cpp file, use the Init method to register the target function to pass in the encapsulated C++ methods for the ArkTS method to call. 1335 EXTERN_C_START 1336 static napi_value Init(napi_env env, napi_value exports) { 1337 // ... 1338 // Expose the SetSurfaceId(), ChangeSurface(), and DestroySurface() APIs to the ArkTS side. 1339 // DrawPattern(),ChangeColor(),GetXComponentStatus() 1340 napi_property_descriptor desc[] = { 1341 {"SetSurfaceId", nullptr, PluginManager::SetSurfaceId, nullptr, nullptr, nullptr, napi_default, nullptr}, 1342 {"ChangeSurface", nullptr, PluginManager::ChangeSurface, nullptr, nullptr, nullptr, napi_default, nullptr}, 1343 {"DestroySurface", nullptr, PluginManager::DestroySurface, nullptr, nullptr, nullptr, napi_default, nullptr}, 1344 {"DrawPattern", nullptr, PluginManager::DrawPattern, nullptr, nullptr, nullptr, napi_default, nullptr}, 1345 {"ChangeColor", nullptr, PluginManager::ChangeColor, nullptr, nullptr, nullptr, napi_default, nullptr}, 1346 {"GetXComponentStatus", nullptr, PluginManager::GetXComponentStatus, nullptr, nullptr, nullptr, napi_default, 1347 nullptr}}; 1348 if (napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc) != napi_ok) { 1349 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Init", "napi_define_properties failed"); 1350 return nullptr; 1351 } 1352 return exports; 1353 } 1354 EXTERN_C_END 1355 // Write the API description. You can modify parameters as required. 1356 static napi_module nativerenderModule = {.nm_version = 1, 1357 .nm_flags = 0, 1358 .nm_filename = nullptr, 1359 // Entry point function 1360 .nm_register_func = Init, 1361 // Module name 1362 .nm_modname = "nativerender", 1363 .nm_priv = ((void *)0), 1364 .reserved = {0}}; 1365 } // namespace NativeXComponentSample 1366 // 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. 1367 extern "C" __attribute__((constructor)) void RegisterModule(void) { 1368 napi_module_register(&NativeXComponentSample::nativerenderModule); 1369 } 1370 ``` 1371 13723. Implement the preceding six registered functions on the native side. 1373 1374 ```cpp 1375 // Define the PluginManager class. 1376 class PluginManager { 1377 public: 1378 ~PluginManager(); 1379 static PluginRender *GetPluginRender(int64_t &id); 1380 static napi_value ChangeColor(napi_env env, napi_callback_info info); 1381 static napi_value DrawPattern(napi_env env, napi_callback_info info); 1382 static napi_value SetSurfaceId(napi_env env, napi_callback_info info); 1383 static napi_value ChangeSurface(napi_env env, napi_callback_info info); 1384 static napi_value DestroySurface(napi_env env, napi_callback_info info); 1385 static napi_value GetXComponentStatus(napi_env env, napi_callback_info info); 1386 1387 public: 1388 static std::unordered_map<int64_t, PluginRender *> pluginRenderMap_; 1389 static std::unordered_map<int64_t, OHNativeWindow *> windowMap_; 1390 }; 1391 1392 // Parse the surfaceId passed from ArkTS. Here, surfaceId is a 64-bit integer value. 1393 int64_t ParseId(napi_env env, napi_callback_info info) { 1394 if ((env == nullptr) || (info == nullptr)) { 1395 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ParseId", "env or info is null"); 1396 return -1; 1397 } 1398 size_t argc = 1; 1399 napi_value args[1] = {nullptr}; 1400 if (napi_ok != napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)) { 1401 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ParseId", "GetContext napi_get_cb_info failed"); 1402 return -1; 1403 } 1404 int64_t value = 0; 1405 bool lossless = true; 1406 if (napi_ok != napi_get_value_bigint_int64(env, args[0], &value, &lossless)) { 1407 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ParseId", "Get value failed"); 1408 return -1; 1409 } 1410 return value; 1411 } 1412 1413 // Set SurfaceId and initialize NativeWindow based on SurfaceId. 1414 napi_value PluginManager::SetSurfaceId(napi_env env, napi_callback_info info) { 1415 int64_t surfaceId = ParseId(env, info); 1416 OHNativeWindow *nativeWindow; 1417 PluginRender *pluginRender; 1418 if (windowMap_.find(surfaceId) == windowMap_.end()) { 1419 OH_NativeWindow_CreateNativeWindowFromSurfaceId(surfaceId, &nativeWindow); 1420 windowMap_[surfaceId] = nativeWindow; 1421 } 1422 if (pluginRenderMap_.find(surfaceId) == pluginRenderMap_.end()) { 1423 pluginRender = new PluginRender(surfaceId); 1424 pluginRenderMap_[surfaceId] = pluginRender; 1425 } 1426 pluginRender->InitNativeWindow(nativeWindow); 1427 return nullptr; 1428 } 1429 1430 void PluginRender::InitNativeWindow(OHNativeWindow *window) { 1431 eglCore_->EglContextInit(window); // For details about the EglContextInit implementation, see the "Native XComponent Scenario" section. 1432 } 1433 1434 // Implement surface size changes based on the passed surfaceId, width, and height. 1435 napi_value PluginManager::ChangeSurface(napi_env env, napi_callback_info info) { 1436 if ((env == nullptr) || (info == nullptr)) { 1437 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", 1438 "ChangeSurface: OnLoad env or info is null"); 1439 return nullptr; 1440 } 1441 int64_t surfaceId = 0; 1442 size_t argc = 3; 1443 napi_value args[3] = {nullptr}; 1444 1445 if (napi_ok != napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)) { 1446 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", 1447 "ChangeSurface: GetContext napi_get_cb_info failed"); 1448 return nullptr; 1449 } 1450 bool lossless = true; 1451 int index = 0; 1452 if (napi_ok != napi_get_value_bigint_int64(env, args[index++], &surfaceId, &lossless)) { 1453 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "ChangeSurface: Get value failed"); 1454 return nullptr; 1455 } 1456 double width; 1457 if (napi_ok != napi_get_value_double(env, args[index++], &width)) { 1458 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "ChangeSurface: Get width failed"); 1459 return nullptr; 1460 } 1461 double height; 1462 if (napi_ok != napi_get_value_double(env, args[index++], &height)) { 1463 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "ChangeSurface: Get height failed"); 1464 return nullptr; 1465 } 1466 auto pluginRender = GetPluginRender(surfaceId); 1467 if (pluginRender == nullptr) { 1468 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "ChangeSurface: Get pluginRender failed"); 1469 return nullptr; 1470 } 1471 pluginRender->UpdateNativeWindowSize(width, height); 1472 return nullptr; 1473 } 1474 1475 void PluginRender::UpdateNativeWindowSize(int width, int height) { 1476 eglCore_->UpdateSize(width, height); // For details about the UpdateSize implementation, see the "Native XComponent Scenario" section. 1477 if (!hasChangeColor_ && !hasDraw_) { 1478 eglCore_->Background(); // For details about the Background implementation, see the "Native XComponent Scenario" section. 1479 } 1480 } 1481 1482 // Destroy the surface. 1483 napi_value PluginManager::DestroySurface(napi_env env, napi_callback_info info) { 1484 int64_t surfaceId = ParseId(env, info); 1485 auto pluginRenderMapIter = pluginRenderMap_.find(surfaceId); 1486 if (pluginRenderMapIter != pluginRenderMap_.end()) { 1487 delete pluginRenderMapIter->second; 1488 pluginRenderMap_.erase(pluginRenderMapIter); 1489 } 1490 auto windowMapIter = windowMap_.find(surfaceId); 1491 if (windowMapIter != windowMap_.end()) { 1492 OH_NativeWindow_DestroyNativeWindow(windowMapIter->second); 1493 windowMap_.erase(windowMapIter); 1494 } 1495 return nullptr; 1496 } 1497 1498 // Implement the EGL drawing logic. 1499 napi_value PluginManager::DrawPattern(napi_env env, napi_callback_info info) { 1500 int64_t surfaceId = ParseId(env, info); 1501 auto pluginRender = GetPluginRender(surfaceId); 1502 if (pluginRender == nullptr) { 1503 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "DrawPattern: Get pluginRender failed"); 1504 return nullptr; 1505 } 1506 pluginRender->DrawPattern(); 1507 return nullptr; 1508 } 1509 1510 PluginRender *PluginManager::GetPluginRender(int64_t &id) { 1511 if (pluginRenderMap_.find(id) != pluginRenderMap_.end()) { 1512 return pluginRenderMap_[id]; 1513 } 1514 return nullptr; 1515 } 1516 1517 void PluginRender::DrawPattern() { 1518 eglCore_->Draw(hasDraw_); // For details about the Draw implementation, see the "Native XComponent Scenario" section. 1519 } 1520 1521 // Implement the feature of changing the color of the drawn graphics. 1522 napi_value PluginManager::ChangeColor(napi_env env, napi_callback_info info) { 1523 int64_t surfaceId = ParseId(env, info); 1524 auto pluginRender = GetPluginRender(surfaceId); 1525 if (pluginRender == nullptr) { 1526 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "ChangeColor: Get pluginRender failed"); 1527 return nullptr; 1528 } 1529 pluginRender->ChangeColor (); // For details about the ChangeColor implementation, see the "Native XComponent Scenario" section. 1530 return nullptr; 1531 } 1532 1533 void PluginRender::ChangeColor() { eglCore_->ChangeColor(hasChangeColor_); } 1534 1535 // Obtain the XComponent status and return it to the ArkTS side. 1536 napi_value PluginManager::GetXComponentStatus(napi_env env, napi_callback_info info) { 1537 int64_t surfaceId = ParseId(env, info); 1538 auto pluginRender = GetPluginRender(surfaceId); 1539 if (pluginRender == nullptr) { 1540 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", 1541 "GetXComponentStatus: Get pluginRender failed"); 1542 return nullptr; 1543 } 1544 napi_value hasDraw; 1545 napi_value hasChangeColor; 1546 napi_status ret = napi_create_int32(env, pluginRender->HasDraw(), &(hasDraw)); 1547 if (ret != napi_ok) { 1548 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", 1549 "GetXComponentStatus: napi_create_int32 hasDraw_ error"); 1550 return nullptr; 1551 } 1552 ret = napi_create_int32(env, pluginRender->HasChangedColor(), &(hasChangeColor)); 1553 if (ret != napi_ok) { 1554 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", 1555 "GetXComponentStatus: napi_create_int32 hasChangeColor_ error"); 1556 return nullptr; 1557 } 1558 napi_value obj; 1559 ret = napi_create_object(env, &obj); 1560 if (ret != napi_ok) { 1561 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", 1562 "GetXComponentStatus: napi_create_object error"); 1563 return nullptr; 1564 } 1565 ret = napi_set_named_property(env, obj, "hasDraw", hasDraw); 1566 if (ret != napi_ok) { 1567 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", 1568 "GetXComponentStatus: napi_set_named_property hasDraw error"); 1569 return nullptr; 1570 } 1571 ret = napi_set_named_property(env, obj, "hasChangeColor", hasChangeColor); 1572 if (ret != napi_ok) { 1573 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", 1574 "GetXComponentStatus: napi_set_named_property hasChangeColor error"); 1575 return nullptr; 1576 } 1577 return obj; 1578 } 1579 1580 int32_t PluginRender::HasDraw() { return hasDraw_; } 1581 1582 int32_t PluginRender::HasChangedColor() { return hasChangeColor_; } 1583 ``` 1584 15854. Configure the specific CMakeLists to use the CMake toolchain to compile the C++ source code into a dynamic link library file. 1586 1587 ```cmake 1588 # Set the minimum CMake version. 1589 cmake_minimum_required(VERSION 3.4.1) 1590 # Project name 1591 project(XComponent) 1592 1593 set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}) 1594 add_definitions(-DOHOS_PLATFORM) 1595 # Set the header file search directory. 1596 include_directories( 1597 ${NATIVERENDER_ROOT_PATH} 1598 ${NATIVERENDER_ROOT_PATH}/include 1599 ) 1600 # Add the **nativerender** dynamic library, with the **libnativerender.so** library file. Add the .cpp files. 1601 add_library(nativerender SHARED 1602 render/egl_core.cpp 1603 render/plugin_render.cpp 1604 manager/plugin_manager.cpp 1605 napi_init.cpp 1606 ) 1607 1608 find_library( 1609 # Sets the name of the path variable. 1610 EGL-lib 1611 # Specifies the name of the NDK library that 1612 # you want CMake to locate. 1613 EGL 1614 ) 1615 1616 find_library( 1617 # Sets the name of the path variable. 1618 GLES-lib 1619 # Specifies the name of the NDK library that 1620 # you want CMake to locate. 1621 GLESv3 1622 ) 1623 1624 find_library( 1625 # Sets the name of the path variable. 1626 hilog-lib 1627 # Specifies the name of the NDK library that 1628 # you want CMake to locate. 1629 hilog_ndk.z 1630 ) 1631 1632 find_library( 1633 # Sets the name of the path variable. 1634 libace-lib 1635 # Specifies the name of the NDK library that 1636 # you want CMake to locate. 1637 ace_ndk.z 1638 ) 1639 1640 find_library( 1641 # Sets the name of the path variable. 1642 libnapi-lib 1643 # Specifies the name of the NDK library that 1644 # you want CMake to locate. 1645 ace_napi.z 1646 ) 1647 1648 find_library( 1649 # Sets the name of the path variable. 1650 libuv-lib 1651 # Specifies the name of the NDK library that 1652 # you want CMake to locate. 1653 uv 1654 ) 1655 # Add the libraries to be linked. 1656 target_link_libraries(nativerender PUBLIC 1657 ${EGL-lib} ${GLES-lib} ${hilog-lib} ${libace-lib} ${libnapi-lib} ${libuv-lib} libnative_window.so) 1658 ``` 1659 1660## How Custom Drawing Works 1661 1662The **XComponent** provides a surface for custom drawing. To draw custom content on this surface, you can use the **NativeWindow** API to allocate and submit graphics buffers. This process pushes your custom content to the surface, and the **XComponent** then integrates the surface into the UI and displays the result. By default, the surface matches the size and position of the **XComponent**. Yet, you can adjust its position and size using the [setXComponentSurfaceRect](../reference/apis-arkui/arkui-ts/ts-basic-components-xcomponent.md#setxcomponentsurfacerect12) API if needed. 1663 1664> **NOTE** 1665> 1666> If your custom drawn content includes transparent elements, they will blend with the content below the surface. For example, if your content is fully transparent, the background of the **XComponent** is black, and the surface maintains its default size and position, the final display will be a black area. 1667 1668## Lifecycle Events 1669 1670You can use the **XComponent** to develop EGL/OpenGL ES rendering by using the following ArkTS code: 1671 1672```typescript 1673@Builder 1674function myComponent() { 1675 XComponent({ id: 'xcomponentId1', type: XComponentType.SURFACE, libraryname: 'nativerender' }) 1676 .onLoad((context) => {}) 1677 .onDestroy(() => {}) 1678} 1679``` 1680 1681### onLoad 1682 1683The **onLoad** event is triggered when the surface of the **XComponent** is ready. 1684 1685**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"**. 1686 1687**Sequence**: 1688 1689 Native XComponent scenario: 1690 1691 The triggering of the **onLoad** event is subject to the surface, and its sequence with the **OnSurfaceCreated** on the native side is shown in the figure below. 1692 1693 1694 1695 ArkTS XComponent scenario: 1696 1697 The triggering of the **onLoad** event is subject to the surface, and its sequence with the **OnSurfaceCreated** on the ArkTS side is shown in the figure below. 1698 1699 1700 1701 1702 1703### onDestroy 1704 1705The **onDestroy** event is triggered when the **XComponent** component is destroyed, which is the same as the destruction time of common ArkUI components. 1706 1707**Sequence**: 1708 1709 Native XComponent scenario: 1710 1711 The triggering of the **onDestroy** event is subject to the surface, and its sequence with the **OnSurfaceDestroyed** on the native side is shown in the figure below. 1712 1713 1714 1715 1716 1717 ArkTS XComponent scenario: 1718 1719 The triggering of the **onDestroy** event is subject to the surface, and its sequence with the **OnSurfaceDestroyed** on the ArkTS side is shown in the figure below. 1720 1721 1722 1723<!--RP1--><!--RP1End-->