# winit_surface_window
A helper library for using existing Android Surfaces (like Presentations) as windows within the Rust ecosystem, compatible with raw-window-handle.
This library allows you to create a window from an existing Android Surface object, which is particularly useful when working with Android Presentation APIs or other scenarios where you need to render to a secondary display or a specific surface.
## Features
- Create [`SurfaceWindow`](src/window.rs) from Android Surface objects
- Compatible with [`raw-window-handle`](src/window.rs) for integration with various graphics libraries
- Deep integration with winit event loop
- Thread-safe implementation
- Support for Android Presentation API for multi-display management
## Core Components
### Window Management
- [`SurfaceWindow`](src/window.rs) - Core window abstraction, created from Android Surface
- [`PresentationWindow`](src/presentation.rs) - Android Presentation window management
### Winit Integration
- [`winit_ext`](src/winit_ext.rs) - Winit event loop extensions (requires `winit-integration` feature)
## Usage
Add this to your `Cargo.toml`:
```toml
[dependencies]
winit_surface_window = { path = "path/to/winit_surface_window" }
# Enable feature support
[features]
winit-integration = ["winit_surface_window/winit-integration"] # Winit integration support
```
## Complete Usage Example
Here's a complete example using winit event loop:
```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 => {
// Clean up Presentation resources
if let Some(mut presentation) = presentation_window.take() {
let _ = presentation.dismiss();
}
presentation_window = None;
presentation_initialized = false;
event_loop.exit();
}
winit::event::WindowEvent::RedrawRequested => {
// Perform rendering operations
if let Some(presentation) = &presentation_window {
if let Some(surface_window) = presentation.surface_window() {
// Add your rendering code here
println!("Rendering to surface: {}x{}",
surface_window.width(), surface_window.height());
}
}
}
_ => (),
},
winit::event::Event::Resumed => {
// Create main window
let win = event_loop
.create_window(WindowAttributes::default())
.unwrap();
window = Some(win);
// Initialize Presentation in Resumed event
if !presentation_initialized {
// Add delay to ensure Activity is fully ready
std::thread::sleep(std::time::Duration::from_millis(500));
// Initialize Presentation system
match event_loop.init_presentation_android_app(&app) {
Ok(_) => {
println!("Presentation system initialized successfully");
// List available displays
if let Ok(displays) = event_loop.get_available_displays() {
println!("Available displays: {:?}", displays);
}
// Create Presentation
match event_loop.create_presentation_on_secondary_display(10000) {
Ok(presentation) => {
println!("Presentation created successfully!");
// Get SurfaceWindow for rendering
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 Reference
### Core Types
#### `SurfaceWindow`
The main struct representing an Android Surface as a window. Implements `HasRawWindowHandle` and `HasRawDisplayHandle` for compatibility with various graphics libraries.
```rust
// Create window from Android Surface
let surface_window = unsafe { SurfaceWindow::from_surface(&mut env, surface)? };
// Get window dimensions
let width = surface_window.width();
let height = surface_window.height();
```
#### `PresentationWindow`
Android Presentation window manager for creating windows on secondary displays or specific displays.
```rust
// Create Presentation on secondary display
let presentation = PresentationWindow::new_on_secondary_display(5000)?;
// Create Presentation on specific display
let presentation = PresentationWindow::new_on_display(1, 5000)?;
// Get available display list
let displays = PresentationWindow::get_available_displays()?;
// Get underlying SurfaceWindow
if let Some(surface_window) = presentation.surface_window() {
// Perform rendering operations
}
```
### Winit Integration (requires `winit-integration` feature)
#### `EventLoopInitExt`
EventLoop extension for initializing the Presentation system.
```rust
// Initialize using AndroidApp
event_loop.init_presentation_android_app(&app)?;
// Initialize using NativeActivity
event_loop.init_presentation_system_ndk(&activity)?;
```
#### `ActiveEventLoopPresentationExt`
Extension for creating Presentations within event handlers.
```rust
// Create Presentation on secondary display
let presentation = event_loop.create_presentation_on_secondary_display(5000)?;
// Create Presentation on specific display
let presentation = event_loop.create_presentation_on_display(1, 5000)?;
// Get available displays
let displays = event_loop.get_available_displays()?;
```
## Build Configuration
### Android Target
```bash
# Basic build
cargo build --target aarch64-linux-android
# Enable Winit integration
cargo build --target aarch64-linux-android --features winit-integration
```
## Important Notes
1. **Thread Safety**: All Presentation-related operations must be executed on the main UI thread.
2. **Initialization Timing**: Ensure Presentation initialization in `Event::Resumed` event, not in `RedrawRequested`.
3. **Resource Cleanup**: Always call `presentation.dismiss()` when the application closes to clean up resources.
4. **Android Version**: Requires Android API level 23+ support.
5. **Permission Requirements**: May require `SYSTEM_ALERT_WINDOW` permission for secondary display.
## License
This project is licensed under either of
* Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option.