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