# winit_surface_window
一个用于在 Rust 生态系统中使用现有 Android Surface(如 Presentation)作为窗口的辅助库,兼容 raw-window-handle 标准。
该库允许您从现有的 Android Surface 对象创建窗口,这在使用 Android Presentation API 或需要在副屏或特定 Surface 上进行渲染的场景中特别有用。
## 功能特性
- 从 Android Surface 对象创建 [`SurfaceWindow`](src/window.rs)
- 兼容 [`raw-window-handle`](src/window.rs) 标准,便于与各种图形库集成
- 与 winit 事件循环的深度集成
- 线程安全的实现
- 支持 Android Presentation API 用于多显示器管理
## 核心组件
### 窗口管理
- [`SurfaceWindow`](src/window.rs) - 核心窗口抽象,从 Android Surface 创建
- [`PresentationWindow`](src/presentation.rs) - Android Presentation 窗口管理
### Winit 集成
- [`winit_ext`](src/winit_ext.rs) - Winit 事件循环扩展(需要 `winit-integration` 特性)
## 使用方法
在 `Cargo.toml` 中添加依赖:
```toml
[dependencies]
winit_surface_window = { path = "path/to/winit_surface_window" }
# 启用特性支持
[features]
winit-integration = ["winit_surface_window/winit-integration"] # Winit 集成支持
```
## 完整使用示例
以下是一个使用 winit 事件循环的完整示例:
```rust
use android_activity::AndroidApp;
use winit::event_loop::{EventLoop, EventLoopBuilder};
use winit::platform::android::EventLoopBuilderExtAndroid;
use winit::window::WindowAttributes;
use winit_surface_window::{
ActiveEventLoopPresentationExt, EventLoopInitExt, PresentationWindow
};
#[no_mangle]
fn android_main(app: AndroidApp) {
android_logger::init_once(
android_logger::Config::default().with_max_level(log::LevelFilter::Debug),
);
let event_loop: EventLoop<()> = EventLoopBuilder::new()
.with_android_app(app.clone())
.build()
.unwrap();
let mut window = None;
let mut presentation_window: Option<PresentationWindow> = None;
let mut presentation_initialized = false;
event_loop.run(move |event, event_loop| match event {
winit::event::Event::WindowEvent { event, .. } => match event {
winit::event::WindowEvent::CloseRequested => {
// 清理 Presentation 资源
if let Some(mut presentation) = presentation_window.take() {
let _ = presentation.dismiss();
}
presentation_window = None;
presentation_initialized = false;
event_loop.exit();
}
winit::event::WindowEvent::RedrawRequested => {
// 执行渲染操作
if let Some(presentation) = &presentation_window {
if let Some(surface_window) = presentation.surface_window() {
// 在这里添加您的渲染代码
println!("Rendering to surface: {}x{}",
surface_window.width(), surface_window.height());
}
}
}
_ => (),
},
winit::event::Event::Resumed => {
// 创建主窗口
let win = event_loop
.create_window(WindowAttributes::default())
.unwrap();
window = Some(win);
// 在 Resumed 事件中初始化 Presentation
if !presentation_initialized {
// 添加延迟确保 Activity 完全准备好
std::thread::sleep(std::time::Duration::from_millis(500));
// 初始化 Presentation 系统
match event_loop.init_presentation_android_app(&app) {
Ok(_) => {
println!("Presentation system initialized successfully");
// 列出可用显示器
if let Ok(displays) = event_loop.get_available_displays() {
println!("Available displays: {:?}", displays);
}
// 创建 Presentation
match event_loop.create_presentation_on_secondary_display(10000) {
Ok(presentation) => {
println!("Presentation created successfully!");
// 获取 SurfaceWindow 进行渲染
if let Some(surface_window) = presentation.surface_window() {
println!("SurfaceWindow available: {}x{}",
surface_window.width(), surface_window.height());
}
presentation_window = Some(presentation);
}
Err(err) => {
println!("Failed to create presentation: {}", err);
}
}
presentation_initialized = true;
}
Err(err) => {
println!("Failed to initialize presentation system: {}", err);
}
}
}
}
winit::event::Event::AboutToWait => {
std::thread::sleep(std::time::Duration::from_millis(16));
if let Some(window) = &window {
window.request_redraw();
}
}
_ => (),
}).unwrap();
}
```
## API 参考
### 核心类型
#### `SurfaceWindow`
主要结构体,表示 Android Surface 窗口。实现了 `HasRawWindowHandle` 和 `HasRawDisplayHandle` 接口,可与各种图形库兼容。
```rust
// 从 Android Surface 创建窗口
let surface_window = unsafe { SurfaceWindow::from_surface(&mut env, surface)? };
// 获取窗口尺寸
let width = surface_window.width();
let height = surface_window.height();
```
#### `PresentationWindow`
Android Presentation 窗口管理器,用于在副屏或指定显示器上创建窗口。
```rust
// 在副屏创建 Presentation
let presentation = PresentationWindow::new_on_secondary_display(5000)?;
// 在指定显示器创建 Presentation
let presentation = PresentationWindow::new_on_display(1, 5000)?;
// 获取可用显示器列表
let displays = PresentationWindow::get_available_displays()?;
// 获取底层 SurfaceWindow
if let Some(surface_window) = presentation.surface_window() {
// 进行渲染操作
}
```
### Winit 集成 (需要 `winit-integration` 特性)
#### `EventLoopInitExt`
用于初始化 Presentation 系统的 EventLoop 扩展。
```rust
// 使用 AndroidApp 初始化
event_loop.init_presentation_android_app(&app)?;
// 使用 NativeActivity 初始化
event_loop.init_presentation_system_ndk(&activity)?;
```
#### `ActiveEventLoopPresentationExt`
在事件处理器中创建 Presentation 的扩展。
```rust
// 在副屏创建 Presentation
let presentation = event_loop.create_presentation_on_secondary_display(5000)?;
// 在指定显示器创建 Presentation
let presentation = event_loop.create_presentation_on_display(1, 5000)?;
// 获取可用显示器
let displays = event_loop.get_available_displays()?;
```
## 构建配置
### Android 目标
```bash
# 基础构建
cargo build --target aarch64-linux-android
# 启用 Winit 集成
cargo build --target aarch64-linux-android --features winit-integration
```
## 注意事项
1. **线程安全**:所有 Presentation 相关操作必须在主 UI 线程中执行。
2. **初始化时机**:确保在 `Event::Resumed` 事件中初始化 Presentation,而不是在 `RedrawRequested` 中。
3. **资源清理**:在应用关闭时务必调用 `presentation.dismiss()` 清理资源。
4. **Android 版本**:需要 Android API level 23+ 支持。
5. **权限要求**:可能需要 `SYSTEM_ALERT_WINDOW` 权限用于副屏显示。
## 许可证
本项目使用以下许可证之一:
* Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) 或 http://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](LICENSE-MIT) 或 http://opensource.org/licenses/MIT)
您可以选择其中任何一种许可证。