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