Skip to content

Commit

Permalink
Add Android example
Browse files Browse the repository at this point in the history
  • Loading branch information
MarijnS95 committed Sep 6, 2022
1 parent a8122c2 commit da28d56
Show file tree
Hide file tree
Showing 5 changed files with 158 additions and 9 deletions.
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ cargo run --example window
Glutin is an OpenGL context creation library and doesn't directly provide
OpenGL bindings for you.

For examples, please look [here.](https://github.com/rust-windowing/glutin/tree/master/glutin_examples)
For examples, please look [here](https://github.com/rust-windowing/glutin/tree/master/glutin_examples).

Note that glutin aims at being a low-level brick in your rendering
infrastructure. You are encouraged to write another layer of abstraction
Expand All @@ -52,6 +52,10 @@ The minimum rust version target by glutin is `1.57.0`.

### Android

To compile the examples for android, you have to use the `cargo apk` utility.
Be sure to handle Android's lifecycle correctly when using a `winit` window by only creating a GL surface (currently entire context) after `winit` raises `Event::Resumed`, and destroy it again upon receiving `Event::Suspended`. See this in action in [the `android.rs` example](./glutin_examples/examples/android.rs).

See [`cargo-apk` in the `android-ndk-rs` repository](https://github.com/rust-windowing/android-ndk-rs/tree/master/cargo-apk) for instructions.
To compile and run the Android example on your device, install [`cargo-apk`](https://crates.io/crates/cargo-apk) and start the app using:

```console
$ cargo apk r -p glutin_examples --example android
```
7 changes: 7 additions & 0 deletions glutin_examples/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ glutin = { path = "../glutin", default-features = false }
winit = { version = "0.27.2", default-features = false }
raw-window-handle = "0.5.0"

[target.'cfg(target_os = "android")'.dependencies]
ndk-glue = "0.7" # Keep in sync with winit dependency

[build-dependencies]
gl_generator = "0.14"
cfg_aliases = "0.1.1"

[[example]]
name = "android"
crate-type = ["cdylib"]
136 changes: 136 additions & 0 deletions glutin_examples/examples/android.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
#![cfg(target_os = "android")]

use std::ffi::CString;
use std::num::NonZeroU32;

use raw_window_handle::{
AndroidNdkWindowHandle, HasRawDisplayHandle, HasRawWindowHandle, RawWindowHandle,
};

use winit::event::{Event, WindowEvent};
use winit::event_loop::{ControlFlow, EventLoop};
use winit::window::WindowBuilder;

use glutin::context::ContextAttributesBuilder;
use glutin::prelude::*;
use glutin::surface::SwapInterval;

mod support;

use support::*;

#[cfg_attr(target_os = "android", ndk_glue::main(backtrace = "on"))]
fn main() {
println!("HELLO WTF HELLO");
let event_loop = EventLoop::new();

let raw_display = event_loop.raw_display_handle();

// TODO: On Android this is can only be retrieved _AFTER_ Event::Resumed, but it's not
// needed to create an EGL display. Make this an Option?
// let raw_window_handle = window.raw_window_handle();

// Create the GL display. This will create display automatically for the
// underlying GL platform. See support module on how it's being done.
let gl_display =
create_display(raw_display, RawWindowHandle::AndroidNdk(AndroidNdkWindowHandle::empty()));

let mut state = None;

event_loop.run(move |event, event_loop_window_target, control_flow| {
*control_flow = ControlFlow::Wait;
match event {
Event::Resumed => {
println!("Android window available");

let window = WindowBuilder::new().build(event_loop_window_target).unwrap();

let raw_window_handle = window.raw_window_handle();

// Create the config we'll be used for window. We'll use the native window
// raw-window-handle for it to get the right visual and use proper hdc. Note
// that you can likely use it for other windows using the same config.
let template = config_template(window.raw_window_handle());
let config = unsafe { gl_display.find_configs(template).unwrap().next().unwrap() };

// Create a wrapper for GL window and surface.
let gl_window = GlWindow::from_existing(&gl_display, window, &config);

// The context creation part. It can be created before surface and that's how
// it's expected in multithreaded + multiwindow operation mode, since you
// can send NotCurrentContext, but not Surface.
let context_attributes =
ContextAttributesBuilder::new().build(Some(raw_window_handle));
let gl_context =
unsafe { gl_display.create_context(&config, &context_attributes).unwrap() };

// Make it current and load symbols.
let gl_context = gl_context.make_current(&gl_window.surface).unwrap();

// let wb = WindowBuilder::new().with_title("Hello world!");
// let windowed_context =
// glutin::ContextBuilder::new().build_windowed(wb, el).unwrap();

// let windowed_context = unsafe { windowed_context.make_current().unwrap() };

// let gl = support::load(windowed_context.context());

// state = Some((windowed_context, gl));

gl::load_with(|symbol| {
let symbol = CString::new(symbol).unwrap();
gl_context.get_proc_address(symbol.as_c_str()) as *const _
});

// Try setting vsync.
if let Err(res) = gl_window
.surface
.set_swap_interval(&gl_context, SwapInterval::Wait(NonZeroU32::new(1).unwrap()))
{
eprintln!("Error setting vsync: {:?}", res);
}

state = Some((gl_context, gl_window));
},
Event::Suspended => {
println!("Android window removed");
// Destroy the GL context before ndk-glue releases the window back to the system
state = None;
},

Event::WindowEvent { event, .. } => match event {
WindowEvent::Resized(size) => {
if size.width != 0 && size.height != 0 {
// Some platforms like EGL require resizing GL surface to update the size
// Notable platforms here are Wayland and macOS, other don't require it
// and the function is no-op, but it's wise to resize it for portability
// reasons.
if let Some((gl_context, gl_window)) = &state {
gl_window.surface.resize(
gl_context,
NonZeroU32::new(size.width).unwrap(),
NonZeroU32::new(size.height).unwrap(),
);
}
}
},
WindowEvent::CloseRequested => {
*control_flow = ControlFlow::Exit;
},
_ => (),
},
Event::RedrawEventsCleared => {
if let Some((gl_context, gl_window)) = &state {
unsafe {
gl::ClearColor(0., 0.3, 0.3, 0.8);
gl::Clear(gl::COLOR_BUFFER_BIT);
gl_window.window.request_redraw();
}

gl_window.surface.swap_buffers(gl_context).unwrap();
}
},
_ => (),
}
});
}
2 changes: 1 addition & 1 deletion glutin_examples/examples/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ fn main() {

let raw_display = event_loop.raw_display_handle();

// We create a window before the display to accomodate for WGL, since it
// We create a window before the display to accommodate for WGL, since it
// requires creating HDC for properly loading the WGL and it should be taken
// from the window you'll be rendering into.
let window = WindowBuilder::new().with_transparent(true).build(&event_loop).unwrap();
Expand Down
12 changes: 7 additions & 5 deletions glutin_wgl_sys/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
#![cfg(any(target_os = "windows"))]
#![allow(clippy::too_many_arguments)]
#![allow(clippy::missing_safety_doc)]
#![allow(clippy::manual_non_exhaustive)]
#![allow(clippy::unnecessary_cast)]
#![cfg(target_os = "windows")]
#![allow(
clippy::too_many_arguments,
clippy::missing_safety_doc,
clippy::manual_non_exhaustive,
clippy::unnecessary_cast
)]

/// WGL bindings
pub mod wgl {
Expand Down

0 comments on commit da28d56

Please sign in to comment.