summaryrefslogtreecommitdiffstats
path: root/third_party/rust/khronos-egl/examples/wayland-dynamic.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/rust/khronos-egl/examples/wayland-dynamic.rs
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/khronos-egl/examples/wayland-dynamic.rs')
-rw-r--r--third_party/rust/khronos-egl/examples/wayland-dynamic.rs348
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");
+ }
+}