Skip to content

Commit 668e3ca

Browse files
committed
feat: add OpenHarmony support
1 parent ba60228 commit 668e3ca

File tree

11 files changed

+218
-5
lines changed

11 files changed

+218
-5
lines changed

.github/CODEOWNERS

+4
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,7 @@
2121

2222
# X11
2323
/src/x11.rs @notgull
24+
25+
26+
# OpenHarmony
27+
/src/ohos.rs @richerfu

Cargo.toml

+14-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,10 @@ tracing = { version = "0.1.41", default-features = false }
4848
bytemuck = "1.12.3"
4949
ndk = "0.9.0"
5050

51-
[target.'cfg(all(unix, not(any(target_vendor = "apple", target_os = "android", target_os = "redox"))))'.dependencies]
51+
[target.'cfg(target_env = "ohos")'.dependencies]
52+
ohos-native-window-binding = { version = "0.1" }
53+
54+
[target.'cfg(all(unix, not(any(target_vendor = "apple", target_os = "android", target_os = "redox", target_env = "ohos"))))'.dependencies]
5255
as-raw-xcb-connection = { version = "1.0.0", optional = true }
5356
bytemuck = { version = "1.12.3", optional = true }
5457
drm = { version = "0.14.1", default-features = false, optional = true }
@@ -145,6 +148,11 @@ winit = "0.30.0"
145148
winit = { version = "0.30.0", features = ["android-native-activity"] }
146149
android-activity = "0.6"
147150

151+
[target.'cfg(target_env = "ohos")'.dev-dependencies]
152+
winit = { version = "0.30" }
153+
openharmony-ability = { version = "0.0.4" }
154+
openharmony-ability-derive = { version = "0.0.3" }
155+
148156
[dev-dependencies.image]
149157
version = "0.25.0"
150158
# Disable rayon on web
@@ -169,6 +177,11 @@ members = ["run-wasm"]
169177
name = "winit_android"
170178
crate-type = ["cdylib"]
171179

180+
[[example]]
181+
# Run with `ohrs build -- --example winit_android`
182+
name = "winit_ohos"
183+
crate-type = ["cdylib"]
184+
172185
[[example]]
173186
# Run with `cargo apk r --example winit_multithread_android`
174187
name = "winit_multithread_android"

benches/buffer_mut.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ fn buffer_mut(c: &mut Criterion) {
99
let _ = c;
1010
}
1111

12-
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
12+
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64", target_env = "ohos")))]
1313
{
1414
use criterion::black_box;
1515
use softbuffer::{Context, Surface};

build.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ fn main() {
55
println!("cargo:rustc-check-cfg=cfg(wayland_platform)");
66

77
cfg_aliases::cfg_aliases! {
8-
free_unix: { all(unix, not(any(target_vendor = "apple", target_os = "android", target_os = "redox"))) },
8+
free_unix: { all(unix, not(any(target_vendor = "apple", target_os = "android", target_os = "redox", target_env = "ohos"))) },
99
kms_platform: { all(feature = "kms", free_unix, not(target_arch = "wasm32")) },
1010
x11_platform: { all(feature = "x11", free_unix, not(target_arch = "wasm32")) },
1111
wayland_platform: { all(feature = "wayland", free_unix, not(target_arch = "wasm32")) },

examples/utils/winit_app.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,14 @@ use winit::window::{Window, WindowAttributes, WindowId};
1010
/// Run a Winit application.
1111
#[allow(unused_mut)]
1212
pub(crate) fn run_app(event_loop: EventLoop<()>, mut app: impl ApplicationHandler<()> + 'static) {
13-
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
13+
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64", target_env = "ohos")))]
1414
event_loop.run_app(&mut app).unwrap();
1515

1616
#[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))]
1717
winit::platform::web::EventLoopExtWebSys::spawn_app(event_loop, app);
18+
19+
#[cfg(target_env = "ohos")]
20+
winit::platform::ohos::EventLoopExtOpenHarmony::spawn_app(event_loop, app);
1821
}
1922

2023
/// Create a window from a set of window attributes.

examples/winit.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,14 @@ use winit::keyboard::{Key, NamedKey};
66
#[path = "utils/winit_app.rs"]
77
mod winit_app;
88

9-
#[cfg(not(target_os = "android"))]
9+
#[cfg(not(any(target_os = "android", target_env = "ohos")))]
1010
fn main() {
1111
entry(EventLoop::new().unwrap())
1212
}
1313

14+
#[cfg(any(target_os = "android", target_env = "ohos"))]
15+
fn main() {}
16+
1417
pub(crate) fn entry(event_loop: EventLoop<()>) {
1518
let app = winit_app::WinitAppBuilder::with_init(
1619
|elwt| {

examples/winit_ohos.rs

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#![cfg(target_env = "ohos")]
2+
3+
pub use winit::platform::ohos::{ability::OpenHarmonyApp, EventLoopBuilderExtOpenHarmony};
4+
use winit::{event_loop::EventLoop, platform::ohos::ability::ability};
5+
6+
#[path = "winit.rs"]
7+
mod desktop_example;
8+
9+
/// Run with `ohrs build -- --example winit_ohos`
10+
#[ability]
11+
fn openharmony(app: OpenHarmonyApp) {
12+
let mut builder = EventLoop::builder();
13+
14+
// Install the Android event loop extension if necessary.
15+
builder.with_openharmony_app(app);
16+
17+
desktop_example::entry(builder.build().unwrap())
18+
}

src/backend_dispatch.rs

+2
Original file line numberDiff line numberDiff line change
@@ -194,4 +194,6 @@ make_dispatch! {
194194
Web(backends::web::WebDisplayImpl<D>, backends::web::WebImpl<D, W>, backends::web::BufferImpl<'a, D, W>),
195195
#[cfg(target_os = "redox")]
196196
Orbital(D, backends::orbital::OrbitalImpl<D, W>, backends::orbital::BufferImpl<'a, D, W>),
197+
#[cfg(target_env = "ohos")]
198+
Ohos(D,backends::ohos::OpenHarmonyImpl<D, W>, backends::ohos::BufferImpl<'a, D, W>),
197199
}

src/backends/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ pub(crate) mod android;
77
pub(crate) mod cg;
88
#[cfg(kms_platform)]
99
pub(crate) mod kms;
10+
#[cfg(target_env = "ohos")]
11+
pub(crate) mod ohos;
1012
#[cfg(target_os = "redox")]
1113
pub(crate) mod orbital;
1214
#[cfg(wayland_platform)]

src/backends/ohos.rs

+166
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
//! Implementation of software buffering for OpenHarmony.
2+
3+
use std::marker::PhantomData;
4+
use std::num::{NonZeroI32, NonZeroU32};
5+
6+
#[cfg(doc)]
7+
use raw_window_handle::OhosNdkWindowHandle;
8+
use raw_window_handle::{HasDisplayHandle, HasWindowHandle, RawWindowHandle};
9+
10+
use crate::error::InitError;
11+
use crate::{BufferInterface, Rect, SoftBufferError, SurfaceInterface};
12+
use ohos_native_window_binding::{NativeBufferFormat, NativeWindow, NativeWindowBuffer};
13+
14+
/// The handle to a window for software buffering.
15+
pub struct OpenHarmonyImpl<D, W> {
16+
native_window: NativeWindow,
17+
window: W,
18+
_display: PhantomData<D>,
19+
}
20+
21+
impl<D: HasDisplayHandle, W: HasWindowHandle> SurfaceInterface<D, W> for OpenHarmonyImpl<D, W> {
22+
type Context = D;
23+
type Buffer<'a>
24+
= BufferImpl<'a, D, W>
25+
where
26+
Self: 'a;
27+
28+
/// Create a new [`OpenHarmonyImpl`] from an [`OhosNdkWindowHandle`].
29+
fn new(window: W, _display: &Self::Context) -> Result<Self, InitError<W>> {
30+
let raw = window.window_handle()?.as_raw();
31+
let RawWindowHandle::OhosNdk(a) = raw else {
32+
return Err(InitError::Unsupported(window));
33+
};
34+
35+
// Acquire a new owned reference to the window, that will be freed on drop.
36+
// SAFETY: We have confirmed that the window handle is valid.
37+
let native_window = NativeWindow::clone_from_ptr(a.native_window.as_ptr());
38+
39+
Ok(Self {
40+
native_window,
41+
_display: PhantomData,
42+
window,
43+
})
44+
}
45+
46+
#[inline]
47+
fn window(&self) -> &W {
48+
&self.window
49+
}
50+
51+
/// Also changes the pixel format to [`HardwareBufferFormat::R8G8B8A8_UNORM`].
52+
fn resize(&mut self, width: NonZeroU32, height: NonZeroU32) -> Result<(), SoftBufferError> {
53+
let (width, height) = (|| {
54+
let width = NonZeroI32::try_from(width).ok()?;
55+
let height = NonZeroI32::try_from(height).ok()?;
56+
Some((width, height))
57+
})()
58+
.ok_or(SoftBufferError::SizeOutOfRange { width, height })?;
59+
60+
self.native_window
61+
.set_buffer_geometry(width.into(), height.into())
62+
.map_err(|err| {
63+
SoftBufferError::PlatformError(
64+
Some("Failed to set buffer geometry on NativeWindow".to_owned()),
65+
Some(Box::new(err)),
66+
)
67+
})
68+
}
69+
70+
fn buffer_mut(&mut self) -> Result<BufferImpl<'_, D, W>, SoftBufferError> {
71+
let native_window_buffer = self.native_window.request_buffer(None).map_err(|err| {
72+
SoftBufferError::PlatformError(
73+
Some("Failed to request native window buffer".to_owned()),
74+
Some(Box::new(err)),
75+
)
76+
})?;
77+
78+
if !matches!(
79+
native_window_buffer.format(),
80+
// These are the only formats we support
81+
NativeBufferFormat::RGBA_8888 | NativeBufferFormat::RGBX_8888
82+
) {
83+
return Err(SoftBufferError::PlatformError(
84+
Some(format!(
85+
"Unexpected buffer format {:?}, please call \
86+
.resize() first to change it to RGBx8888",
87+
native_window_buffer.format()
88+
)),
89+
None,
90+
));
91+
}
92+
let size = (native_window_buffer.width() * native_window_buffer.height())
93+
.try_into()
94+
.map_err(|e| {
95+
SoftBufferError::PlatformError(
96+
Some("Failed to convert width to u32".to_owned()),
97+
Some(Box::new(e)),
98+
)
99+
})?;
100+
let buffer = vec![0; size];
101+
102+
Ok(BufferImpl {
103+
native_window_buffer,
104+
buffer,
105+
marker: PhantomData,
106+
})
107+
}
108+
109+
/// Fetch the buffer from the window.
110+
fn fetch(&mut self) -> Result<Vec<u32>, SoftBufferError> {
111+
Err(SoftBufferError::Unimplemented)
112+
}
113+
}
114+
115+
pub struct BufferImpl<'a, D: ?Sized, W> {
116+
native_window_buffer: NativeWindowBuffer<'a>,
117+
buffer: Vec<u32>,
118+
marker: PhantomData<(&'a D, &'a W)>,
119+
}
120+
121+
unsafe impl<'a, D, W> Send for BufferImpl<'a, D, W> {}
122+
123+
impl<'a, D: HasDisplayHandle, W: HasWindowHandle> BufferInterface for BufferImpl<'a, D, W> {
124+
#[inline]
125+
fn pixels(&self) -> &[u32] {
126+
&self.buffer
127+
}
128+
129+
#[inline]
130+
fn pixels_mut(&mut self) -> &mut [u32] {
131+
&mut self.buffer
132+
}
133+
134+
#[inline]
135+
fn age(&self) -> u8 {
136+
0
137+
}
138+
139+
// TODO: This function is pretty slow this way
140+
fn present(mut self) -> Result<(), SoftBufferError> {
141+
let input_lines = self.buffer.chunks(self.native_window_buffer.width() as _);
142+
for (output, input) in self
143+
.native_window_buffer
144+
.lines()
145+
// Unreachable as we checked before that this is a valid, mappable format
146+
.unwrap()
147+
.zip(input_lines)
148+
{
149+
// .lines() removed the stride
150+
assert_eq!(output.len(), input.len() * 4);
151+
152+
for (i, pixel) in input.iter().enumerate() {
153+
// Swizzle colors from RGBX to BGR
154+
let [b, g, r, _] = pixel.to_le_bytes();
155+
output[i * 4].write(b);
156+
output[i * 4 + 1].write(g);
157+
output[i * 4 + 2].write(r);
158+
}
159+
}
160+
Ok(())
161+
}
162+
163+
fn present_with_damage(self, _damage: &[Rect]) -> Result<(), SoftBufferError> {
164+
self.present()
165+
}
166+
}

src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,7 @@ fn window_handle_type_name(handle: &RawWindowHandle) -> &'static str {
303303
RawWindowHandle::Drm(_) => "DRM",
304304
RawWindowHandle::Gbm(_) => "GBM",
305305
RawWindowHandle::Haiku(_) => "Haiku",
306+
RawWindowHandle::OhosNdk(_) => "OhosNdk",
306307
_ => "Unknown Name", //don't completely fail to compile if there is a new raw window handle type that's added at some point
307308
}
308309
}
@@ -321,6 +322,7 @@ fn display_handle_type_name(handle: &RawDisplayHandle) -> &'static str {
321322
RawDisplayHandle::Haiku(_) => "Haiku",
322323
RawDisplayHandle::Windows(_) => "Windows",
323324
RawDisplayHandle::Android(_) => "Android",
325+
RawDisplayHandle::Ohos(_) => "Ohos",
324326
_ => "Unknown Name", //don't completely fail to compile if there is a new raw window handle type that's added at some point
325327
}
326328
}

0 commit comments

Comments
 (0)