diff options
Diffstat (limited to 'third_party/rust/khronos-egl/examples/wayland-dynamic.rs')
-rw-r--r-- | third_party/rust/khronos-egl/examples/wayland-dynamic.rs | 348 |
1 files changed, 348 insertions, 0 deletions
diff --git a/third_party/rust/khronos-egl/examples/wayland-dynamic.rs b/third_party/rust/khronos-egl/examples/wayland-dynamic.rs new file mode 100644 index 0000000000..cb9748cb68 --- /dev/null +++ b/third_party/rust/khronos-egl/examples/wayland-dynamic.rs @@ -0,0 +1,348 @@ +extern crate gl; +extern crate khronos_egl as egl; +extern crate wayland_client; +extern crate wayland_egl; +extern crate wayland_protocols; +extern crate libloading; + +use std::sync::Arc; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::ptr; +use std::ffi::CStr; +use gl::types::{GLint, GLuint, GLchar, GLenum, GLboolean, GLvoid}; + +use wayland_client::{ + protocol::{wl_compositor::WlCompositor, wl_surface::WlSurface}, + DispatchData, Display, EventQueue, Main +}; + +use wayland_protocols::xdg_shell::client::{ + xdg_surface::{self, XdgSurface}, + xdg_wm_base::{self, XdgWmBase}, +}; + +fn process_xdg_event(xdg: Main<XdgWmBase>, event: xdg_wm_base::Event, _dd: DispatchData) { + use xdg_wm_base::Event::*; + + match event { + Ping { serial } => xdg.pong(serial), + _ => (), + } +} + +struct DisplayConnection { + display: Display, + event_queue: EventQueue, + compositor: Main<WlCompositor>, + xdg: Main<XdgWmBase>, +} + +fn setup_wayland() -> DisplayConnection { + let display = + wayland_client::Display::connect_to_env().expect("unable to connect to the wayland server"); + let mut event_queue = display.create_event_queue(); + let attached_display = display.clone().attach(event_queue.token()); + + let globals = wayland_client::GlobalManager::new(&attached_display); + + // Roundtrip to retrieve the globals list + event_queue + .sync_roundtrip(&mut (), |_, _, _| unreachable!()) + .unwrap(); + + // Get the compositor. + let compositor: Main<WlCompositor> = globals.instantiate_exact(1).unwrap(); + + // Xdg protocol. + let xdg: Main<XdgWmBase> = globals.instantiate_exact(1).unwrap(); + xdg.quick_assign(process_xdg_event); + + DisplayConnection { + display, + event_queue, + compositor, + xdg, + } +} + +fn setup_egl(egl: &egl::DynamicInstance, display: &Display) -> egl::Display { + let egl_display = egl.get_display(display.get_display_ptr() as *mut std::ffi::c_void).unwrap(); + egl.initialize(egl_display).unwrap(); + + egl_display +} + +fn create_context(egl: &egl::DynamicInstance, display: egl::Display) -> (egl::Context, egl::Config) { + let attributes = [ + egl::RED_SIZE, + 8, + egl::GREEN_SIZE, + 8, + egl::BLUE_SIZE, + 8, + egl::NONE, + ]; + + let config = egl.choose_first_config(display, &attributes) + .expect("unable to choose an EGL configuration") + .expect("no EGL configuration found"); + + let context_attributes = [ + egl::CONTEXT_MAJOR_VERSION, + 4, + egl::CONTEXT_MINOR_VERSION, + 0, + egl::CONTEXT_OPENGL_PROFILE_MASK, + egl::CONTEXT_OPENGL_CORE_PROFILE_BIT, + egl::NONE, + ]; + + let context = egl.create_context(display, config, None, &context_attributes) + .expect("unable to create an EGL context"); + + (context, config) +} + +struct Surface { + handle: Main<WlSurface>, + initialized: AtomicBool +} + +fn create_surface( + egl: &Arc<egl::DynamicInstance>, + ctx: &DisplayConnection, + egl_display: egl::Display, + egl_context: egl::Context, + egl_config: egl::Config, + width: i32, + height: i32, +) -> Arc<Surface> { + let wl_surface = ctx.compositor.create_surface(); + let xdg_surface = ctx.xdg.get_xdg_surface(&wl_surface); + + let xdg_toplevel = xdg_surface.get_toplevel(); + xdg_toplevel.set_app_id("khronos-egl-test".to_string()); + xdg_toplevel.set_title("Test".to_string()); + + wl_surface.commit(); + ctx.display.flush().unwrap(); + + let surface = Arc::new(Surface { + handle: wl_surface, + initialized: AtomicBool::new(false) + }); + + let weak_surface = Arc::downgrade(&surface); + + let egl = egl.clone(); + xdg_surface.quick_assign(move |xdg_surface: Main<XdgSurface>, event: xdg_surface::Event, _dd: DispatchData| { + use xdg_surface::Event::*; + + match event { + Configure { serial } => { + if let Some(surface) = weak_surface.upgrade() { + if !surface.initialized.swap(true, Ordering::Relaxed) { + let wl_egl_surface = wayland_egl::WlEglSurface::new(&surface.handle, width, height); + + let egl_surface = unsafe { + egl.create_window_surface( + egl_display, + egl_config, + wl_egl_surface.ptr() as egl::NativeWindowType, + None, + ) + .expect("unable to create an EGL surface") + }; + + egl.make_current(egl_display, Some(egl_surface), Some(egl_surface), Some(egl_context)) + .expect("unable to bind the context"); + + render(); + + egl.swap_buffers(egl_display, egl_surface) + .expect("unable to post the surface content"); + + xdg_surface.ack_configure(serial); + } + } + }, + _ => (), + } + }); + + surface +} + +fn main() { + let egl = unsafe { Arc::new(egl::DynamicInstance::<egl::EGL1_5>::load_required().expect("unable to load libEGL.so.1")) }; + + // Setup Open GL. + egl.bind_api(egl::OPENGL_API) + .expect("unable to select OpenGL API"); + gl::load_with(|name| egl.get_proc_address(name).unwrap() as *const std::ffi::c_void); + + // Setup the Wayland client. + let mut ctx = setup_wayland(); + + // Setup EGL. + let egl_display = setup_egl(&egl, &ctx.display); + let (egl_context, egl_config) = create_context(&egl, egl_display); + + // Create a surface. + // Note that it must be kept alive to the end of execution. + let _surface = create_surface(&egl, &ctx, egl_display, egl_context, egl_config, 800, 600); + + loop { + ctx.event_queue + .dispatch(&mut (), |_, _, _| { /* we ignore unfiltered messages */ }) + .unwrap(); + } +} + +const VERTEX: &'static [GLint; 8] = &[ + -1, -1, + 1, -1, + 1, 1, + -1, 1 +]; + +const INDEXES: &'static [GLuint; 4] = &[ + 0, 1, 2, 3 +]; + +const VERTEX_SHADER: &[u8] = b"#version 400 +in vec2 position; + +void main() { + gl_Position = vec4(position, 0.0f, 1.0f); +} +\0"; + +const FRAGMENT_SHADER: &[u8] = b"#version 400 +out vec4 color; + +void main() { + color = vec4(1.0f, 0.0f, 0.0f, 1.0f); +} +\0"; + +fn render() { + unsafe { + let vertex_shader = gl::CreateShader(gl::VERTEX_SHADER); + check_gl_errors(); + let src = CStr::from_bytes_with_nul_unchecked(VERTEX_SHADER).as_ptr(); + gl::ShaderSource(vertex_shader, 1, (&[src]).as_ptr(), ptr::null()); + check_gl_errors(); + gl::CompileShader(vertex_shader); + check_shader_status(vertex_shader); + + let fragment_shader = gl::CreateShader(gl::FRAGMENT_SHADER); + check_gl_errors(); + let src = CStr::from_bytes_with_nul_unchecked(FRAGMENT_SHADER).as_ptr(); + gl::ShaderSource(fragment_shader, 1, (&[src]).as_ptr(), ptr::null()); + check_gl_errors(); + gl::CompileShader(fragment_shader); + check_shader_status(fragment_shader); + + let program = gl::CreateProgram(); + check_gl_errors(); + gl::AttachShader(program, vertex_shader); + check_gl_errors(); + gl::AttachShader(program, fragment_shader); + check_gl_errors(); + gl::LinkProgram(program); + check_gl_errors(); + gl::UseProgram(program); + check_gl_errors(); + + let mut buffer = 0; + gl::GenBuffers(1, &mut buffer); + check_gl_errors(); + gl::BindBuffer(gl::ARRAY_BUFFER, buffer); + check_gl_errors(); + gl::BufferData( + gl::ARRAY_BUFFER, + 8 * 4, + VERTEX.as_ptr() as *const std::ffi::c_void, + gl::STATIC_DRAW + ); + check_gl_errors(); + + let mut vertex_input = 0; + gl::GenVertexArrays(1, &mut vertex_input); + check_gl_errors(); + gl::BindVertexArray(vertex_input); + check_gl_errors(); + gl::EnableVertexAttribArray(0); + check_gl_errors(); + gl::VertexAttribPointer( + 0, 2, gl::INT, gl::FALSE as GLboolean, 0, 0 as *const GLvoid + ); + check_gl_errors(); + + let mut indexes = 0; + gl::GenBuffers(1, &mut indexes); + check_gl_errors(); + gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, indexes); + check_gl_errors(); + gl::BufferData( + gl::ELEMENT_ARRAY_BUFFER, + 4 * 4, + INDEXES.as_ptr() as *const std::ffi::c_void, + gl::STATIC_DRAW + ); + check_gl_errors(); + + gl::DrawElements(gl::TRIANGLE_FAN, 4, gl::UNSIGNED_INT, std::ptr::null()); + check_gl_errors(); + } +} + +fn format_error(e: GLenum) -> &'static str { + match e { + gl::NO_ERROR => "No error", + gl::INVALID_ENUM => "Invalid enum", + gl::INVALID_VALUE => "Invalid value", + gl::INVALID_OPERATION => "Invalid operation", + gl::INVALID_FRAMEBUFFER_OPERATION => "Invalid framebuffer operation", + gl::OUT_OF_MEMORY => "Out of memory", + gl::STACK_UNDERFLOW => "Stack underflow", + gl::STACK_OVERFLOW => "Stack overflow", + _ => "Unknown error" + } +} + +pub fn check_gl_errors() { + unsafe { + match gl::GetError() { + gl::NO_ERROR => (), + e => { + panic!("OpenGL error: {}", format_error(e)) + } + } + } +} + +unsafe fn check_shader_status(shader: GLuint) { + let mut status = gl::FALSE as GLint; + gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut status); + if status != (gl::TRUE as GLint) { + let mut len = 0; + gl::GetProgramiv(shader, gl::INFO_LOG_LENGTH, &mut len); + if len > 0 { + let mut buf = Vec::with_capacity(len as usize); + buf.set_len((len as usize) - 1); // subtract 1 to skip the trailing null character + gl::GetProgramInfoLog( + shader, + len, + ptr::null_mut(), + buf.as_mut_ptr() as *mut GLchar, + ); + + let log = String::from_utf8(buf).unwrap(); + eprintln!("shader compilation log:\n{}", log); + } + + panic!("shader compilation failed"); + } +} |