• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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