rust-windowing / raw-window-handle

A common windowing interoperability library for Rust
Apache License 2.0
314 stars 49 forks source link

Add OpenHarmony OS support #164

Closed jschwe closed 4 months ago

jschwe commented 5 months ago

Add support for the OpenHarmony operating system, which is a tier-2 target in Rust starting with Rust 1.78. On OpenHarmony ArkUI is used to develop applications. ArkUI provides the XComponent type, which allows native code (e.g. Rust) to use EGL / OpenGL ES to render directly to a native window.

jschwe commented 5 months ago

Sorry for the slow reply.

I believe I was mistaken with my previous statement that the xcomponent pointer is also required - at least for my usecase of getting servo running on OpenHarmony, the APIs provided by OHNativeWindow seem to be sufficient. I adjusted my PR and removed the XComponent, but since I'm not familiar with UIs and what exactly RawWindowHandles can be used for on other systems, let me give a short introduction and link the relevant documentation.

The latest version of the XComponent Guidelines available in English, seems to be Native XComponent Guidelines 4.0 (English). On the master branch there currently is only the Chinese version, but version 4.0 is quite recent too.

To summarize the workflow (with the C++ examples in the documentation):

  1. The UI is defined in TypeScript code (ArkUI / ArkTS)
  2. The XComponent with the surface type can be used to get a native EGL surface
  3. Native code can receive a callback OnSurfaceCreatedCB(OH_NativeXComponent *component, void *window) when the XComponent is created.
  4. The documentation suggests determining eglDisplay via eglGetDisplay(EGL_DEFAULT_DISPLAY). In servo I've been using egl.GetCurrentDisplay(); (same way as the native android servo app)
  5. The void* window (OHNativeWindow / EGLNativeWindowType) argument to OnSurfaceCreatedCB, is the eglWindow needed to create an eglSurface with eglCreateWindowSurface(eglDisplay_, eglConfig_, eglWindow_, NULL)

The API provided by OHNativeWindow is quite low level, and missing the following APIs compared to the XComponent it belongs to:

I'm currently leaning towards it being fine if the OhosNdkWindowHandle would not allow usage of such APIs, but I'm also not very familiar how raw-window-handle is used in practice (besides servo), so I'm very open to feedback.

madsmtm commented 5 months ago

I spent a few minutes trying to read the docs, and I still have no idea what an "XComponent" actually is? As in, what does it correspond to visually? Is it the top-level window itself? Or can components be nested like subsurfaces/subviews? If I tried to do some GUI development in OpenHarmony, and I wanted to place a button on the screen somewhere, would that button be a "subclass" (or similar) of XComponent, of OHNativeWindow, or of something entirely different?

jschwe commented 5 months ago

On OpenHarmony ArkTS is the usual language apps including their UI are written in. ArkTS is a flavor of typescript, which adds some restrictions, like forcing initialization of variables and requiring that all types are known at compile time (i.e. any is forbidden). Additionally, ArkTS adds builtins for UI components (example).

The ArkTS XComponent can be used in the ArkTS UI code to obtain a surface, that the native C++ (or in our case Rust) code can render to using OpenGL. The size and position of the XComponent is determined by the ArkTS UI layout.

In this example the ArkTS UI definition is simply one Textbox (which is used as a URL bar) and beneath that an XComponent, which servo renders to using OpenGL.

Column() {
  TextInput(My_url_bar)
  XComponent()
}

See also Introduction to ArkUI

If I tried to do some GUI development in OpenHarmony, and I wanted to place a button on the screen somewhere, would that button be a "subclass" (or similar) of XComponent, of OHNativeWindow, or of something entirely different?

You would use the ArkTS Button builtin. The XComponent (surface type) is used when rendering directly from native code via OpenGL.

madsmtm commented 5 months ago

Can you query the size of the surface from just OHNativeWindow?

jschwe commented 5 months ago

Yes, one can use OH_NativeWindow_NativeWindowHandleOpt(window, GET_BUFFER_GEOMETRY, height, width).

Obtaining the geometry of the local window buffer. Variable arguments in the function: [Output] int32_t height and [Output] int32_t width.

jschwe commented 4 months ago

Is there anything I still need to do before this can be merged? Should I squash?

Lokathor commented 4 months ago

I can squash-merge