1 extern crate khronos_egl as egl;
2
3 use egl::API as egl;
4 use gl::types::{GLboolean, GLchar, GLenum, GLint, GLuint, GLvoid};
5 use std::ffi::CStr;
6 use std::ptr;
7 use std::sync::atomic::{AtomicBool, Ordering};
8 use std::sync::Arc;
9
10 use wayland_client::{
11 protocol::{wl_compositor::WlCompositor, wl_surface::WlSurface},
12 DispatchData, Display, EventQueue, Main,
13 };
14
15 use wayland_protocols::xdg_shell::client::{
16 xdg_surface::{self, XdgSurface},
17 xdg_wm_base::{self, XdgWmBase},
18 };
19
process_xdg_event(xdg: Main<XdgWmBase>, event: xdg_wm_base::Event, _dd: DispatchData)20 fn process_xdg_event(xdg: Main<XdgWmBase>, event: xdg_wm_base::Event, _dd: DispatchData) {
21 use xdg_wm_base::Event::*;
22
23 match event {
24 Ping { serial } => xdg.pong(serial),
25 _ => (),
26 }
27 }
28
29 struct DisplayConnection {
30 display: Display,
31 event_queue: EventQueue,
32 compositor: Main<WlCompositor>,
33 xdg: Main<XdgWmBase>,
34 }
35
setup_wayland() -> DisplayConnection36 fn setup_wayland() -> DisplayConnection {
37 let display =
38 wayland_client::Display::connect_to_env().expect("unable to connect to the wayland server");
39 let mut event_queue = display.create_event_queue();
40 let attached_display = display.clone().attach(event_queue.token());
41
42 let globals = wayland_client::GlobalManager::new(&attached_display);
43
44 // Roundtrip to retrieve the globals list
45 event_queue
46 .sync_roundtrip(&mut (), |_, _, _| unreachable!())
47 .unwrap();
48
49 // Get the compositor.
50 let compositor: Main<WlCompositor> = globals.instantiate_exact(1).unwrap();
51
52 // Xdg protocol.
53 let xdg: Main<XdgWmBase> = globals.instantiate_exact(1).unwrap();
54 xdg.quick_assign(process_xdg_event);
55
56 DisplayConnection {
57 display,
58 event_queue,
59 compositor,
60 xdg,
61 }
62 }
63
setup_egl(display: &Display) -> egl::Display64 fn setup_egl(display: &Display) -> egl::Display {
65 let egl_display = unsafe {
66 egl.get_display(display.get_display_ptr() as *mut std::ffi::c_void)
67 .unwrap()
68 };
69
70 egl.initialize(egl_display).unwrap();
71 egl_display
72 }
73
create_context(display: egl::Display) -> (egl::Context, egl::Config)74 fn create_context(display: egl::Display) -> (egl::Context, egl::Config) {
75 let attributes = [
76 egl::RED_SIZE,
77 8,
78 egl::GREEN_SIZE,
79 8,
80 egl::BLUE_SIZE,
81 8,
82 egl::NONE,
83 ];
84
85 let config = egl
86 .choose_first_config(display, &attributes)
87 .expect("unable to choose an EGL configuration")
88 .expect("no EGL configuration found");
89
90 let context_attributes = [
91 egl::CONTEXT_MAJOR_VERSION,
92 4,
93 egl::CONTEXT_MINOR_VERSION,
94 0,
95 egl::CONTEXT_OPENGL_PROFILE_MASK,
96 egl::CONTEXT_OPENGL_CORE_PROFILE_BIT,
97 egl::NONE,
98 ];
99
100 let context = egl
101 .create_context(display, config, None, &context_attributes)
102 .expect("unable to create an EGL context");
103
104 (context, config)
105 }
106
107 struct Surface {
108 handle: Main<WlSurface>,
109 initialized: AtomicBool,
110 }
111
create_surface( ctx: &DisplayConnection, egl_display: egl::Display, egl_context: egl::Context, egl_config: egl::Config, width: i32, height: i32, ) -> Arc<Surface>112 fn create_surface(
113 ctx: &DisplayConnection,
114 egl_display: egl::Display,
115 egl_context: egl::Context,
116 egl_config: egl::Config,
117 width: i32,
118 height: i32,
119 ) -> Arc<Surface> {
120 let wl_surface = ctx.compositor.create_surface();
121 let xdg_surface = ctx.xdg.get_xdg_surface(&wl_surface);
122
123 let xdg_toplevel = xdg_surface.get_toplevel();
124 xdg_toplevel.set_app_id("khronos-egl-test".to_string());
125 xdg_toplevel.set_title("Test".to_string());
126
127 wl_surface.commit();
128 ctx.display.flush().unwrap();
129
130 let surface = Arc::new(Surface {
131 handle: wl_surface,
132 initialized: AtomicBool::new(false),
133 });
134
135 let weak_surface = Arc::downgrade(&surface);
136
137 xdg_surface.quick_assign(
138 move |xdg_surface: Main<XdgSurface>, event: xdg_surface::Event, _dd: DispatchData| {
139 use xdg_surface::Event::*;
140
141 match event {
142 Configure { serial } => {
143 if let Some(surface) = weak_surface.upgrade() {
144 if !surface.initialized.swap(true, Ordering::Relaxed) {
145 let wl_egl_surface =
146 wayland_egl::WlEglSurface::new(&surface.handle, width, height);
147
148 let egl_surface = unsafe {
149 egl.create_window_surface(
150 egl_display,
151 egl_config,
152 wl_egl_surface.ptr() as egl::NativeWindowType,
153 None,
154 )
155 .expect("unable to create an EGL surface")
156 };
157
158 egl.make_current(
159 egl_display,
160 Some(egl_surface),
161 Some(egl_surface),
162 Some(egl_context),
163 )
164 .expect("unable to bind the context");
165
166 render();
167
168 egl.swap_buffers(egl_display, egl_surface)
169 .expect("unable to post the surface content");
170
171 xdg_surface.ack_configure(serial);
172 }
173 }
174 }
175 _ => (),
176 }
177 },
178 );
179
180 surface
181 }
182
main()183 fn main() {
184 // Setup Open GL.
185 egl.bind_api(egl::OPENGL_API)
186 .expect("unable to select OpenGL API");
187 gl::load_with(|name| egl.get_proc_address(name).unwrap() as *const std::ffi::c_void);
188
189 // Setup the Wayland client.
190 let mut ctx = setup_wayland();
191
192 // Setup EGL.
193 let egl_display = setup_egl(&ctx.display);
194 let (egl_context, egl_config) = create_context(egl_display);
195
196 // Create a surface.
197 // Note that it must be kept alive to the end of execution.
198 let _surface = create_surface(&ctx, egl_display, egl_context, egl_config, 800, 600);
199
200 loop {
201 ctx.event_queue
202 .dispatch(&mut (), |_, _, _| { /* we ignore unfiltered messages */ })
203 .unwrap();
204 }
205 }
206
207 const VERTEX: &'static [GLint; 8] = &[-1, -1, 1, -1, 1, 1, -1, 1];
208
209 const INDEXES: &'static [GLuint; 4] = &[0, 1, 2, 3];
210
211 const VERTEX_SHADER: &[u8] = b"#version 400
212 in vec2 position;
213
214 void main() {
215 gl_Position = vec4(position, 0.0f, 1.0f);
216 }
217 \0";
218
219 const FRAGMENT_SHADER: &[u8] = b"#version 400
220 out vec4 color;
221
222 void main() {
223 color = vec4(1.0f, 0.0f, 0.0f, 1.0f);
224 }
225 \0";
226
render()227 fn render() {
228 unsafe {
229 let vertex_shader = gl::CreateShader(gl::VERTEX_SHADER);
230 check_gl_errors();
231 let src = CStr::from_bytes_with_nul_unchecked(VERTEX_SHADER).as_ptr();
232 gl::ShaderSource(vertex_shader, 1, (&[src]).as_ptr(), ptr::null());
233 check_gl_errors();
234 gl::CompileShader(vertex_shader);
235 check_shader_status(vertex_shader);
236
237 let fragment_shader = gl::CreateShader(gl::FRAGMENT_SHADER);
238 check_gl_errors();
239 let src = CStr::from_bytes_with_nul_unchecked(FRAGMENT_SHADER).as_ptr();
240 gl::ShaderSource(fragment_shader, 1, (&[src]).as_ptr(), ptr::null());
241 check_gl_errors();
242 gl::CompileShader(fragment_shader);
243 check_shader_status(fragment_shader);
244
245 let program = gl::CreateProgram();
246 check_gl_errors();
247 gl::AttachShader(program, vertex_shader);
248 check_gl_errors();
249 gl::AttachShader(program, fragment_shader);
250 check_gl_errors();
251 gl::LinkProgram(program);
252 check_gl_errors();
253 gl::UseProgram(program);
254 check_gl_errors();
255
256 let mut buffer = 0;
257 gl::GenBuffers(1, &mut buffer);
258 check_gl_errors();
259 gl::BindBuffer(gl::ARRAY_BUFFER, buffer);
260 check_gl_errors();
261 gl::BufferData(
262 gl::ARRAY_BUFFER,
263 8 * 4,
264 VERTEX.as_ptr() as *const std::ffi::c_void,
265 gl::STATIC_DRAW,
266 );
267 check_gl_errors();
268
269 let mut vertex_input = 0;
270 gl::GenVertexArrays(1, &mut vertex_input);
271 check_gl_errors();
272 gl::BindVertexArray(vertex_input);
273 check_gl_errors();
274 gl::EnableVertexAttribArray(0);
275 check_gl_errors();
276 gl::VertexAttribPointer(0, 2, gl::INT, gl::FALSE as GLboolean, 0, 0 as *const GLvoid);
277 check_gl_errors();
278
279 let mut indexes = 0;
280 gl::GenBuffers(1, &mut indexes);
281 check_gl_errors();
282 gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, indexes);
283 check_gl_errors();
284 gl::BufferData(
285 gl::ELEMENT_ARRAY_BUFFER,
286 4 * 4,
287 INDEXES.as_ptr() as *const std::ffi::c_void,
288 gl::STATIC_DRAW,
289 );
290 check_gl_errors();
291
292 gl::DrawElements(gl::TRIANGLE_FAN, 4, gl::UNSIGNED_INT, std::ptr::null());
293 check_gl_errors();
294 }
295 }
296
format_error(e: GLenum) -> &'static str297 fn format_error(e: GLenum) -> &'static str {
298 match e {
299 gl::NO_ERROR => "No error",
300 gl::INVALID_ENUM => "Invalid enum",
301 gl::INVALID_VALUE => "Invalid value",
302 gl::INVALID_OPERATION => "Invalid operation",
303 gl::INVALID_FRAMEBUFFER_OPERATION => "Invalid framebuffer operation",
304 gl::OUT_OF_MEMORY => "Out of memory",
305 gl::STACK_UNDERFLOW => "Stack underflow",
306 gl::STACK_OVERFLOW => "Stack overflow",
307 _ => "Unknown error",
308 }
309 }
310
check_gl_errors()311 pub fn check_gl_errors() {
312 unsafe {
313 match gl::GetError() {
314 gl::NO_ERROR => (),
315 e => {
316 panic!("OpenGL error: {}", format_error(e))
317 }
318 }
319 }
320 }
321
check_shader_status(shader: GLuint)322 unsafe fn check_shader_status(shader: GLuint) {
323 let mut status = gl::FALSE as GLint;
324 gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut status);
325 if status != (gl::TRUE as GLint) {
326 let mut len = 0;
327 gl::GetProgramiv(shader, gl::INFO_LOG_LENGTH, &mut len);
328 if len > 0 {
329 let mut buf = Vec::with_capacity(len as usize);
330 buf.set_len((len as usize) - 1); // subtract 1 to skip the trailing null character
331 gl::GetProgramInfoLog(
332 shader,
333 len,
334 ptr::null_mut(),
335 buf.as_mut_ptr() as *mut GLchar,
336 );
337
338 let log = String::from_utf8(buf).unwrap();
339 eprintln!("shader compilation log:\n{}", log);
340 }
341
342 panic!("shader compilation failed");
343 }
344 }
345