• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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![onLoad](./figures/onLoad.png)
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![onLoad](./figures/onLoad1.png)
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![onDestroy](./figures/onDestroy.png)
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![onDestroy](./figures/onDestroy1.png)
1722
1723<!--RP1--><!--RP1End-->