cgl_rs/window.rs
1//! The window manager module is responsible for creating and managing windows. It is also responsible for handling input events and passing them to the appropriate window.
2
3#![allow(non_camel_case_types)]
4use libc::{c_void, c_int, c_char, c_double};
5
6/// The internal window handle used by CGL
7#[repr(C)]
8pub(crate) struct CGL_window {
9 pub(crate) _private: c_void
10}
11
12/// The internal window handle used by GLFW
13#[repr(C)]
14pub struct GLFWwindow {
15 _private: c_void
16}
17
18type CGL_window_key_callback = extern "C" fn(window: *mut CGL_window, key: c_int, scancode: c_int, action: c_int, mods: c_int) -> c_void;
19type CGL_window_mouse_button_callback = extern "C" fn(window: *mut CGL_window, button: c_int, action: c_int, mods: c_int) -> c_void;
20type CGL_window_mouse_position_callback = extern "C" fn(window: *mut CGL_window, x: c_double, y: c_double) -> c_void;
21type CGL_window_mouse_scroll_callback = extern "C" fn(window: *mut CGL_window, x: c_double, y: c_double) -> c_void;
22type CGL_window_framebuffer_size_callback = extern "C" fn(window: *mut CGL_window, width: c_int, height: c_int) -> c_void;
23type CGL_window_close_callback = extern "C" fn(window: *mut CGL_window) -> c_void;
24type CGL_window_drag_n_drop_callback = extern "C" fn(window: *mut CGL_window, paths: *const *const c_char, count: c_int) -> c_void;
25
26
27
28
29extern {
30 fn CGL_window_create(width: c_int, height: c_int, title: *const c_char) -> *mut CGL_window;
31 fn CGL_window_create_undecorated(width: c_int, height: c_int, title: *const c_char) -> *mut CGL_window;
32 fn CGL_window_destroy(window: *mut CGL_window) -> c_void;
33 fn CGL_window_poll_events(window: *mut CGL_window) -> c_void;
34 fn CGL_window_swap_buffers(window: *mut CGL_window) -> c_void;
35 fn CGL_window_should_close(window: *mut CGL_window) -> c_int;
36 fn CGL_window_set_title(window: *mut CGL_window, title: *const c_char) -> c_void;
37 fn CGL_window_set_size(window: *mut CGL_window, width: c_int, height: c_int) -> c_void;
38 fn CGL_window_set_position(window: *mut CGL_window, x: c_int, y: c_int) -> c_void;
39 fn CGL_window_set_hidden(window: *mut CGL_window, hidden: c_int) -> c_void;
40 fn CGL_window_set_user_data(window: *mut CGL_window, user_data: *mut c_void) -> c_void;
41 fn CGL_window_get_user_data(window: *mut CGL_window) -> *mut c_void;
42 fn CGL_window_get_size(window: *mut CGL_window, width: *mut c_int, height: *mut c_int) -> c_void;
43 fn CGL_window_get_position(window: *mut CGL_window, x: *mut c_int, y: *mut c_int) -> c_void;
44 fn CGL_window_get_framebuffer_size(window: *mut CGL_window, width: *mut c_int, height: *mut c_int) -> c_void;
45
46 fn CGL_window_set_key_callback(window: *mut CGL_window, callback: CGL_window_key_callback) -> c_void;
47 fn CGL_window_set_mouse_button_callback(window: *mut CGL_window, callback: CGL_window_mouse_button_callback) -> c_void;
48 fn CGL_window_set_mouse_position_callback(window: *mut CGL_window, callback: CGL_window_mouse_position_callback) -> c_void;
49 fn CGL_window_set_mouse_scroll_callback(window: *mut CGL_window, callback: CGL_window_mouse_scroll_callback) -> c_void;
50 fn CGL_window_set_framebuffer_size_callback(window: *mut CGL_window, callback: CGL_window_framebuffer_size_callback) -> c_void;
51 fn CGL_window_set_close_callback(window: *mut CGL_window, callback: CGL_window_close_callback) -> c_void;
52 fn CGL_window_set_drag_n_drop_callback(window: *mut CGL_window, callback: CGL_window_drag_n_drop_callback) -> c_void;
53
54 fn CGL_window_resecure_callbacks(window: *mut CGL_window) -> c_void;
55 fn CGL_window_make_context_current(window: *mut CGL_window) -> c_void;
56 fn CGL_window_get_glfw_handle(window: *mut CGL_window) -> *mut GLFWwindow;
57
58 fn CGL_window_get_key(window: *mut CGL_window, key: c_int) -> c_int;
59 fn CGL_window_is_key_pressed(window: *mut CGL_window, key: c_int) -> c_int;
60 fn CGL_window_get_mouse_button(window: *mut CGL_window, button: c_int) -> c_int;
61 fn CGL_window_get_mouse_position(window: *mut CGL_window, x: *mut c_double, y: *mut c_double) -> c_void;
62
63 fn CGL_utils_sleep(duration: usize) -> c_void; // temporary
64}
65
66
67/// Represents a keyboard key, with each variant corresponding to a specific key code.
68#[repr(C)] #[derive(Debug)]
69pub enum Key {
70 Unknown = -1,
71 Space = 32,
72 Apostrophe = 39,
73 Comma = 44,
74 Minus = 45,
75 Period = 46,
76 Slash = 47,
77 K0 = 48,
78 K1 = 49,
79 K2 = 50,
80 K3 = 51,
81 K4 = 52,
82 K5 = 53,
83 K6 = 54,
84 K7 = 55,
85 K8 = 56,
86 K9 = 57,
87 Semicolon = 59,
88 Equal = 61,
89 A = 65,
90 B = 66,
91 C = 67,
92 D = 68,
93 E = 69,
94 F = 70,
95 G = 71,
96 H = 72,
97 I = 73,
98 J = 74,
99 K = 75,
100 L = 76,
101 M = 77,
102 N = 78,
103 O = 79,
104 P = 80,
105 Q = 81,
106 R = 82,
107 S = 83,
108 T = 84,
109 U = 85,
110 V = 86,
111 W = 87,
112 X = 88,
113 Y = 89,
114 Z = 90,
115 LeftBracket = 91,
116 Backslash = 92,
117 RightBracket = 93,
118 GraveAccent = 96,
119 Escape = 256,
120 Enter = 257,
121 Tab = 258,
122 Backspace = 259,
123 Insert = 260,
124 Delete = 261,
125 Right = 262,
126 Left = 263,
127 Down = 264,
128 Up = 265,
129 PageUp = 266,
130 PageDown = 267,
131 Home = 268,
132 End = 269,
133 CapsLock = 280,
134 ScrollLock = 281,
135 NumLock = 282,
136 PrintScreen = 283,
137 Pause = 284,
138 F1 = 290,
139 F2 = 291,
140 F3 = 292,
141 F4 = 293,
142 F5 = 294,
143 F6 = 295,
144 F7 = 296,
145 F8 = 297,
146 F9 = 298,
147 F10 = 299,
148 F11 = 300,
149 F12 = 301,
150 F13 = 302,
151 F14 = 303,
152 F15 = 304,
153 F16 = 305,
154 F17 = 306,
155 F18 = 307,
156 F19 = 308,
157 F20 = 309,
158 F21 = 310,
159 F22 = 311,
160 F23 = 312,
161 F24 = 313,
162 F25 = 314,
163 KeyPad0 = 320,
164 KeyPad1 = 321,
165 KeyPad2 = 322,
166 KeyPad3 = 323,
167 KeyPad4 = 324,
168 KeyPad5 = 325,
169 KeyPad6 = 326,
170 KeyPad7 = 327,
171 KeyPad8 = 328,
172 KeyPad9 = 329,
173 KeyPadDecimal = 330,
174 KeyPadDivide = 331,
175 KeyPadMultiply = 332,
176 KeyPadSubtract = 333,
177 KeyPadAdd = 334,
178 KeyPadEnter = 335,
179 KeyPadEqual = 336,
180 LeftShift = 340,
181 LeftControl = 341,
182 LeftAlt = 342,
183 LeftSuper = 343,
184 RightShift = 344,
185 RightControl = 345,
186 RightAlt = 346,
187 RightSuper = 347,
188 Menu = 348
189}
190
191/// Represents a mouse button, with each variant corresponding to a specific button code.
192#[derive(Debug)]
193pub enum MouseButton {
194 Left = 0,
195 Right = 1,
196 Middle = 2,
197 B4 = 3,
198 B5 = 4,
199 B6 = 5,
200 B7 = 6,
201 B8 = 7,
202 Last = 8
203}
204
205/// Represents an action, with each variant corresponding to a specific action code.
206#[derive(Debug, PartialEq)]
207pub enum Action {
208 Release = 0,
209 Press = 1,
210 Repeat = 2
211}
212
213/// Represents an event that can be received by a window, with each variant corresponding to a specific type of event.
214#[derive(Debug)]
215pub enum Event {
216 Key(Key, Action, i32, i32),
217 MouseButton(MouseButton, Action, i32),
218 MousePosition(f64, f64),
219 MouseScroll(f64, f64),
220 FramebufferSize(i32, i32),
221 WindowClose,
222 DragNDrop(Vec<String>)
223}
224
225/// Represents a function that handles a window event.
226/// Takes a reference to the window and the event, and returns a boolean indicating whether the event was handled successfully.
227/// If this function returns false, the event will be passed to the next handler in the chain, or if there are no more handlers, the event will be ignored.
228/// If this function returns true, the event will not be passed to any more handlers.
229pub type EventFunction = dyn Fn(&Window, &Event) -> bool;
230
231/// Represents a window, with a handle to the underlying CGL window and a flag indicating whether it has been destroyed.
232pub struct Window {
233 handle: *mut CGL_window,
234 has_been_destroyed: bool,
235 event_handlers: std::collections::HashMap<String, Box<EventFunction>>
236}
237
238const MAX_ACTIVE_WINDOWS: usize = 1024;
239static mut ACTIVE_WINDOWS: [*const Window; MAX_ACTIVE_WINDOWS] = [0 as *const Window; MAX_ACTIVE_WINDOWS];
240
241// this is just a temporary hack, will be replaced with a proper solution later
242extern "C" fn fn_returning_c_void() -> c_void {
243 unsafe { CGL_utils_sleep(1) }
244}
245
246fn cgl_window_i32_to_action(action: i32) -> Action {
247 match action {
248 0 => Action::Release,
249 1 => Action::Press,
250 2 => Action::Repeat,
251 _ => panic!("Invalid action code")
252 }
253}
254
255fn cgl_window_i32_to_key(key: i32) -> Key {
256 match key {
257 32 => Key::Space,
258 39 => Key::Apostrophe,
259 44 => Key::Comma,
260 45 => Key::Minus,
261 46 => Key::Period,
262 47 => Key::Slash,
263 48 => Key::K0,
264 49 => Key::K1,
265 50 => Key::K2,
266 51 => Key::K3,
267 52 => Key::K4,
268 53 => Key::K5,
269 54 => Key::K6,
270 55 => Key::K7,
271 56 => Key::K8,
272 57 => Key::K9,
273 59 => Key::Semicolon,
274 61 => Key::Equal,
275 65 => Key::A,
276 66 => Key::B,
277 67 => Key::C,
278 68 => Key::D,
279 69 => Key::E,
280 70 => Key::F,
281 71 => Key::G,
282 72 => Key::H,
283 73 => Key::I,
284 74 => Key::J,
285 75 => Key::K,
286 76 => Key::L,
287 77 => Key::M,
288 78 => Key::N,
289 79 => Key::O,
290 80 => Key::P,
291 81 => Key::Q,
292 82 => Key::R,
293 83 => Key::S,
294 84 => Key::T,
295 85 => Key::U,
296 86 => Key::V,
297 87 => Key::W,
298 88 => Key::X,
299 89 => Key::Y,
300 90 => Key::Z,
301 91 => Key::LeftBracket,
302 92 => Key::Backslash,
303 93 => Key::RightBracket,
304 96 => Key::GraveAccent,
305 256 => Key::Escape,
306 257 => Key::Enter,
307 258 => Key::Tab,
308 259 => Key::Backspace,
309 260 => Key::Insert,
310 261 => Key::Delete,
311 262 => Key::Right,
312 263 => Key::Left,
313 264 => Key::Down,
314 265 => Key::Up,
315 266 => Key::PageUp,
316 267 => Key::PageDown,
317 268 => Key::Home,
318 269 => Key::End,
319 280 => Key::CapsLock,
320 281 => Key::ScrollLock,
321 282 => Key::NumLock,
322 283 => Key::PrintScreen,
323 284 => Key::Pause,
324 290 => Key::F1,
325 291 => Key::F2,
326 292 => Key::F3,
327 293 => Key::F4,
328 294 => Key::F5,
329 295 => Key::F6,
330 296 => Key::F7,
331 297 => Key::F8,
332 298 => Key::F9,
333 299 => Key::F10,
334 300 => Key::F11,
335 301 => Key::F12,
336 302 => Key::F13,
337 303 => Key::F14,
338 304 => Key::F15,
339 305 => Key::F16,
340 306 => Key::F17,
341 307 => Key::F18,
342 308 => Key::F19,
343 309 => Key::F20,
344 310 => Key::F21,
345 311 => Key::F22,
346 312 => Key::F23,
347 313 => Key::F24,
348 314 => Key::F25,
349 320 => Key::KeyPad0,
350 321 => Key::KeyPad1,
351 322 => Key::KeyPad2,
352 323 => Key::KeyPad3,
353 324 => Key::KeyPad4,
354 325 => Key::KeyPad5,
355 326 => Key::KeyPad6,
356 327 => Key::KeyPad7,
357 328 => Key::KeyPad8,
358 329 => Key::KeyPad9,
359 330 => Key::KeyPadDecimal,
360 331 => Key::KeyPadDivide,
361 332 => Key::KeyPadMultiply,
362 333 => Key::KeyPadSubtract,
363 334 => Key::KeyPadAdd,
364 335 => Key::KeyPadEnter,
365 336 => Key::KeyPadEqual,
366 340 => Key::LeftShift,
367 341 => Key::LeftControl,
368 342 => Key::LeftAlt,
369 343 => Key::LeftSuper,
370 344 => Key::RightShift,
371 345 => Key::RightControl,
372 346 => Key::RightAlt,
373 347 => Key::RightSuper,
374 348 => Key::Menu,
375 _ => panic!("Invalid key code")
376 }
377}
378
379fn cgl_window_i32_to_mouse_button(button: i32) -> MouseButton {
380 match button {
381 0 => MouseButton::Left,
382 1 => MouseButton::Right,
383 2 => MouseButton::Middle,
384 3 => MouseButton::B4,
385 4 => MouseButton::B5,
386 5 => MouseButton::B6,
387 6 => MouseButton::B7,
388 7 => MouseButton::B8,
389 _ => panic!("Invalid mouse button code")
390 }
391}
392
393fn cgl_window_dispatch_event(window: *mut CGL_window, event: Event) {
394 unsafe {
395 let window_id = (window as usize) % MAX_ACTIVE_WINDOWS;
396 if ACTIVE_WINDOWS[window_id].is_null() {
397 return;
398 }
399 let window = &*ACTIVE_WINDOWS[window_id];
400 for (_, handler) in &window.event_handlers {
401 if handler(window, &event) {
402 break;
403 }
404 }
405 }
406}
407
408extern "C" fn cgl_window_key_callback(window: *mut CGL_window, key: i32, scancode: i32, action: i32, mods: i32) -> c_void {
409 cgl_window_dispatch_event(window, Event::Key(cgl_window_i32_to_key(key), cgl_window_i32_to_action(action), scancode, mods));
410 fn_returning_c_void()
411}
412
413extern "C" fn cgl_window_mouse_button_callback(window: *mut CGL_window, button: i32, action: i32, mods: i32) -> c_void {
414 cgl_window_dispatch_event(window, Event::MouseButton(cgl_window_i32_to_mouse_button(button), cgl_window_i32_to_action(action), mods));
415 fn_returning_c_void()
416}
417
418extern "C" fn cgl_window_mouse_position_callback(window: *mut CGL_window, xpos: f64, ypos: f64) -> c_void {
419 cgl_window_dispatch_event(window, Event::MousePosition(xpos, ypos));
420 fn_returning_c_void()
421}
422
423extern "C" fn cgl_window_mouse_scroll_callback(window: *mut CGL_window, xoffset: f64, yoffset: f64) -> c_void {
424 cgl_window_dispatch_event(window, Event::MouseScroll(xoffset, yoffset));
425 fn_returning_c_void()
426}
427
428extern "C" fn cgl_window_framebuffer_size_callback(window: *mut CGL_window, width: i32, height: i32) -> c_void {
429 cgl_window_dispatch_event(window, Event::FramebufferSize(width, height));
430 fn_returning_c_void()
431}
432
433extern "C" fn cgl_window_close_callback(window: *mut CGL_window) -> c_void {
434 cgl_window_dispatch_event(window, Event::WindowClose);
435 fn_returning_c_void()
436}
437
438extern "C" fn cgl_window_drag_n_drop_callback(window: *mut CGL_window, paths: *const *const c_char, count: i32) -> c_void {
439 let mut paths_vec = Vec::new();
440 for i in 0..count {
441 unsafe {
442 let path = std::ffi::CStr::from_ptr(*paths.offset(i as isize)).to_str().unwrap();
443 paths_vec.push(path.to_string());
444 }
445 }
446 cgl_window_dispatch_event(window, Event::DragNDrop(paths_vec));
447 fn_returning_c_void()
448}
449
450impl Window {
451 /// Creates a new window with the given title, width, and height.
452 ///
453 /// # Arguments
454 ///
455 /// * `title` - The title of the window.
456 /// * `width` - The width of the window.
457 /// * `height` - The height of the window.
458 ///
459 /// # Returns
460 ///
461 /// Returns a `Result` containing the created `Window` if successful, or an error message if the window creation failed.
462 ///
463 /// # Panics
464 ///
465 /// Panics if the width or height is not positive.
466 ///
467 ///
468 /// # Example
469 ///
470 /// ```
471 /// cgl_rs::init();
472 /// let mut window = cgl_rs::Window::new("My Window", 800, 600).unwrap();
473 /// window.destroy(); // we must destroy the window before shutting down
474 /// cgl_rs::shutdown();
475 /// ```
476 pub fn new(title: &str, width: i32, height: i32) -> Result<Window, &'static str> {
477 assert!(width > 0 && height > 0, "Window dimensions must be positive");
478 let title = std::ffi::CString::new(title).unwrap();
479 let handle = unsafe {
480 let created_window = CGL_window_create(width, height, title.as_ptr());
481 created_window
482 };
483 if handle.is_null() {
484 Err("Failed to create window")
485 } else {
486 unsafe { CGL_window_make_context_current(handle); }
487 Ok(Window {
488 handle: handle,
489 has_been_destroyed: false,
490 event_handlers: std::collections::HashMap::new()
491 })
492 }
493 }
494
495 /// Creates a new undecorated window with the given title, width, and height.
496 ///
497 /// An undecorated window is a window without any borders or title bar.
498 ///
499 /// # Arguments
500 ///
501 /// * `title` - The title of the window.
502 /// * `width` - The width of the window.
503 /// * `height` - The height of the window.
504 ///
505 /// # Returns
506 ///
507 /// Returns a `Result` containing the created `Window` if successful, or an error message if the window creation failed.
508 ///
509 /// # Panics
510 ///
511 /// Panics if the width or height is not positive.
512 ///
513 ///
514 /// # Example
515 ///
516 /// ```
517 /// cgl_rs::init();
518 /// {
519 /// let mut window = cgl_rs::Window::new_undecorated("My Window", 800, 600).unwrap();
520 /// // window.destroy(); // optional
521 /// }
522 /// cgl_rs::shutdown();
523 /// ```
524 pub fn new_undecorated(title: &str, width: i32, height: i32) -> Result<Window, &'static str> {
525 assert!(width > 0 && height > 0, "Window dimensions must be positive");
526 let title = std::ffi::CString::new(title).unwrap();
527 let handle = unsafe {
528 let created_window = CGL_window_create_undecorated(width, height, title.as_ptr());
529 created_window
530 };
531 if handle.is_null() {
532 Err("Failed to create window")
533 } else {
534 unsafe { CGL_window_make_context_current(handle); }
535 Ok(Window {
536 handle: handle,
537 has_been_destroyed: false,
538 event_handlers: std::collections::HashMap::new()
539 })
540 }
541 }
542
543 /// Destroys the window, freeing any resources associated with it.
544 ///
545 /// NOTE: This is called automatically when the `Window` goes out of scope, so it is not necessary to call this manually.
546 /// However, it is safe to call this multiple times, and the subsequent calls will have no effect.
547 /// But one thing to be noted is that this function must be called before `cgl_rs::shutdown()` is called.
548 ///
549 /// # Example
550 ///
551 /// ```
552 /// cgl_rs::init();
553 /// {
554 /// let mut window = cgl_rs::Window::new("My Window", 800, 600).unwrap();
555 /// window.destroy(); // optional
556 /// }
557 /// cgl_rs::shutdown();
558 /// ```
559 pub fn destroy(&mut self) {
560 if !self.has_been_destroyed {
561 unsafe {
562 CGL_window_destroy(self.handle);
563 ACTIVE_WINDOWS[(self.handle as usize) % MAX_ACTIVE_WINDOWS] = 0 as *const Window;
564 self.has_been_destroyed = true;
565 }
566 }
567 }
568
569
570 /// Registers the window to receive events by setting the appropriate callbacks.
571 ///
572 /// This function should be called after creating the window and before entering the event loop.
573 ///
574 /// # Example
575 ///
576 /// ```
577 /// cgl_rs::init();
578 /// {
579 /// // window inside scope so that it is dropped before shutdown is called
580 /// let mut window = cgl_rs::Window::new("My Window", 800, 600).unwrap();
581 /// window.register_for_events();
582 /// }
583 /// cgl_rs::shutdown();
584 /// ```
585 pub fn register_for_events(&self) {
586 unsafe {
587 let window_id = (self.handle as usize) % MAX_ACTIVE_WINDOWS;
588 ACTIVE_WINDOWS[window_id] = self as *const Window;
589 CGL_window_set_key_callback(self.handle, cgl_window_key_callback);
590 CGL_window_set_mouse_button_callback(self.handle, cgl_window_mouse_button_callback);
591 CGL_window_set_mouse_position_callback(self.handle, cgl_window_mouse_position_callback);
592 CGL_window_set_mouse_scroll_callback(self.handle, cgl_window_mouse_scroll_callback);
593 CGL_window_set_framebuffer_size_callback(self.handle, cgl_window_framebuffer_size_callback);
594 CGL_window_set_close_callback(self.handle, cgl_window_close_callback);
595 CGL_window_set_drag_n_drop_callback(self.handle, cgl_window_drag_n_drop_callback);
596 }
597 }
598
599
600 /// Attaches an named event handler to the window.
601 ///
602 /// The `name` parameter is a string that identifies the event handler. This can be any string that is unique to the event handler.
603 ///
604 /// The `handler` parameter is a reference to a function that will be called when the event occurs. The function must have the following signature:
605 ///
606 /// ```no_run
607 /// fn my_event_handler(window: &cgl_rs::Window, event: &cgl_rs::Event) {
608 /// println!("Event occurred!");
609 /// }
610 ///
611 /// ```
612 /// # Example
613 ///
614 /// ```no_run
615 /// cgl_rs::init();
616 /// {
617 /// let mut window = cgl_rs::Window::new("My Window", 800, 600).unwrap();
618 ///
619 /// // window.attach_event_handler("my_event", &my_event_handler);
620 /// }
621 /// cgl_rs::shutdown();
622 /// ```
623 pub fn attach_event_handler(&mut self, name: &str, handler: &'static EventFunction) {
624 self.event_handlers.insert(name.to_string(), Box::new(handler));
625 }
626
627
628 /// Detaches a named event handler from the window.
629 ///
630 /// The `name` parameter is a string that identifies the event handler. This should be the same string that was used to attach the event handler.
631 ///
632 /// # Example
633 ///
634 /// ```no_run
635 /// cgl_rs::init();
636 /// {
637 /// let mut window = cgl_rs::Window::new("My Window", 800, 600).unwrap();
638 ///
639 /// window.detach_event_handler("my_event");
640 /// }
641 /// cgl_rs::shutdown();
642 /// ```
643 pub fn detach_event_handler(&mut self, name: &str) {
644 self.event_handlers.remove(name);
645 }
646
647 /// Polls for events on the window.
648 ///
649 /// This function should be called in a loop to continuously poll for events on the window.
650 ///
651 /// # Example
652 ///
653 /// ```
654 /// cgl_rs::init();
655 /// {
656 /// // window inside scope so that it is dropped before shutdown is called
657 /// let mut window = cgl_rs::Window::new("My Window", 800, 600).unwrap();
658 ///
659 /// while true {
660 /// window.poll_events();
661 /// // Handle events here
662 /// // ...
663 /// break; // break out of the loop for testing purposes
664 /// }
665 ///
666 /// }
667 /// cgl_rs::shutdown();
668 /// ```
669 pub fn poll_events(&self) {
670 unsafe {
671 CGL_window_poll_events(self.handle);
672 }
673 }
674
675 /// Swaps the front and back buffers of the window.
676 ///
677 /// This function should be called after rendering to the back buffer to display the rendered image on the screen.
678 ///
679 /// # Example
680 ///
681 /// ```
682 /// cgl_rs::init();
683 /// {
684 /// // window inside scope so that it is dropped before shutdown is called
685 /// let mut window = cgl_rs::Window::new("My Window", 800, 600).unwrap();
686 ///
687 /// while true {
688 /// window.swap_buffers();
689 /// // Handle events here
690 /// // ...
691 /// break; // break out of the loop for testing purposes
692 /// }
693 ///
694 /// }
695 /// cgl_rs::shutdown();
696 /// ```
697 pub fn swap_buffers(&self) {
698 unsafe {
699 CGL_window_swap_buffers(self.handle);
700 }
701 }
702
703 /// Returns whether or not the window should be closed.
704 ///
705 /// This function should be called in a loop to continuously check if the window should be closed.
706 ///
707 /// # Returns
708 ///
709 /// A boolean value indicating whether or not the window should be closed.
710 ///
711 /// # Example
712 ///
713 /// ```
714 /// cgl_rs::init();
715 /// {
716 /// // window inside scope so that it is dropped before shutdown is called
717 /// let mut window = cgl_rs::Window::new("My Window", 800, 600).unwrap();
718 ///
719 /// while !window.should_close() {
720 /// // ...
721 /// break; // break out of the loop for testing purposes
722 /// }
723 ///
724 /// }
725 /// cgl_rs::shutdown();
726 /// ```
727 pub fn should_close(&self) -> bool {
728 unsafe {
729 CGL_window_should_close(self.handle) != 0
730 }
731 }
732
733 /// Sets the title of the window.
734 ///
735 /// # Arguments
736 ///
737 /// * `title` - A string slice containing the new title of the window.
738 ///
739 /// # Example
740 ///
741 /// ```
742 /// cgl_rs::init();
743 /// {
744 /// // window inside scope so that it is dropped before shutdown is called
745 /// let mut window = cgl_rs::Window::new("My Window", 800, 600).unwrap();
746 /// window.set_title("New Title");
747 /// }
748 /// cgl_rs::shutdown();
749 /// ```
750 pub fn set_title(&self, title: &str) {
751 let title = std::ffi::CString::new(title).unwrap();
752 unsafe {
753 CGL_window_set_title(self.handle, title.as_ptr());
754 }
755 }
756
757 /// Sets the size of the window.
758 ///
759 /// # Arguments
760 ///
761 /// * `width` - The new width of the window.
762 /// * `height` - The new height of the window.
763 ///
764 /// # Example
765 ///
766 /// ```
767 /// cgl_rs::init();
768 /// {
769 /// // window inside scope so that it is dropped before shutdown is called
770 /// let mut window = cgl_rs::Window::new("My Window", 800, 600).unwrap();
771 /// window.set_size(1024, 768);
772 /// }
773 /// cgl_rs::shutdown();
774 /// ```
775 pub fn set_size(&self, width: i32, height: i32) {
776 unsafe {
777 CGL_window_set_size(self.handle, width, height);
778 }
779 }
780
781 /// Sets the position of the window.
782 ///
783 /// # Arguments
784 ///
785 /// * `x` - The new x position of the window.
786 /// * `y` - The new y position of the window.
787 ///
788 /// # Example
789 ///
790 /// ```
791 /// cgl_rs::init();
792 /// {
793 /// // window inside scope so that it is dropped before shutdown is called
794 /// let mut window = cgl_rs::Window::new("My Window", 800, 600).unwrap();
795 /// window.set_position(100, 100);
796 /// }
797 /// cgl_rs::shutdown();
798 /// ```
799 pub fn set_position(&self, x: i32, y: i32) {
800 unsafe {
801 CGL_window_set_position(self.handle, x, y);
802 }
803 }
804
805 /// Sets the visibility of the window.
806 ///
807 /// # Arguments
808 ///
809 /// * `hidden` - A boolean indicating whether the window should be hidden or not.
810 ///
811 /// # Example
812 ///
813 /// ```
814 /// cgl_rs::init();
815 /// {
816 /// // window inside scope so that it is dropped before shutdown is called
817 /// let mut window = cgl_rs::Window::new("My Window", 800, 600).unwrap();
818 /// window.set_hidden(true);
819 /// }
820 /// cgl_rs::shutdown();
821 /// ```
822 pub fn set_hidden(&mut self, hidden: bool) {
823 unsafe {
824 CGL_window_set_hidden(self.handle, hidden as i32);
825 }
826 }
827
828 /// Sets the user data associated with the window.
829 ///
830 /// # Arguments
831 ///
832 /// * `user_data` - A pointer to the user data to associate with the window.
833 ///
834 /// # Example
835 ///
836 /// ```
837 /// cgl_rs::init();
838 /// {
839 /// // window inside scope so that it is dropped before shutdown is called
840 /// let mut window = cgl_rs::Window::new("My Window", 800, 600).unwrap();
841 /// let user_data = Box::into_raw(Box::new(42));
842 /// window.set_user_data(user_data);
843 /// }
844 /// cgl_rs::shutdown();
845 /// ```
846 pub fn set_user_data<T>(&self, user_data: *mut T) {
847 unsafe {
848 CGL_window_set_user_data(self.handle, user_data as *mut c_void);
849 }
850 }
851
852 /// Gets the user data associated with the window.
853 ///
854 /// # Example
855 ///
856 /// ```
857 /// cgl_rs::init();
858 /// {
859 /// // window inside scope so that it is dropped before shutdown is called
860 /// let mut window = cgl_rs::Window::new("My Window", 800, 600).unwrap();
861 /// let user_data = Box::into_raw(Box::new(42));
862 /// window.set_user_data(user_data);
863 /// let retrieved_user_data = window.get_user_data();
864 /// assert_eq!(retrieved_user_data, user_data);
865 /// }
866 /// cgl_rs::shutdown();
867 /// ```
868 pub fn get_user_data<T>(&self) -> *mut T {
869 unsafe {
870 CGL_window_get_user_data(self.handle) as *mut T
871 }
872 }
873
874 /// Gets the size of the window.
875 ///
876 /// # Example
877 ///
878 /// ```
879 /// cgl_rs::init();
880 /// {
881 /// // window inside scope so that it is dropped before shutdown is called
882 /// let mut window = cgl_rs::Window::new("My Window", 800, 600).unwrap();
883 /// let (width, height) = window.get_size();
884 /// assert_eq!(width, 800);
885 /// assert_eq!(height, 600);
886 /// }
887 /// cgl_rs::shutdown();
888 /// ```
889 pub fn get_size(&self) -> (i32, i32) {
890 unsafe {
891 let (mut width, mut height) = (0, 0);
892 CGL_window_get_size(self.handle, &mut width, &mut height);
893 (width, height)
894 }
895 }
896
897 // Gets the position of the window.
898 ///
899 /// # Example
900 ///
901 /// ```
902 /// cgl_rs::init();
903 /// {
904 /// // window inside scope so that it is dropped before shutdown is called
905 /// let mut window = cgl_rs::Window::new("My Window", 800, 600).unwrap();
906 /// let (x, y) = window.get_position();
907 /// }
908 /// cgl_rs::shutdown();
909 /// ```
910 pub fn get_position(&self) -> (i32, i32) {
911 unsafe {
912 let (mut x, mut y) = (0, 0);
913 CGL_window_get_position(self.handle, &mut x, &mut y);
914 (x, y)
915 }
916 }
917
918 // Gets the size of the framebuffer of the window.
919 ///
920 /// # Example
921 ///
922 /// ```
923 /// cgl_rs::init();
924 /// {
925 /// // window inside scope so that it is dropped before shutdown is called
926 /// let mut window = cgl_rs::Window::new("My Window", 800, 600).unwrap();
927 /// let (width, height) = window.get_framebuffer_size();
928 /// assert_eq!(width, 800);
929 /// assert_eq!(height, 600);
930 /// }
931 /// cgl_rs::shutdown();
932 /// ```
933 pub fn get_framebuffer_size(&self) -> (i32, i32) {
934 unsafe {
935 let (mut width, mut height) = (0, 0);
936 CGL_window_get_framebuffer_size(self.handle, &mut width, &mut height);
937 (width, height)
938 }
939 }
940
941 /// Rescues the callbacks of the window.
942 /// This is usefule whn usiong cgl::window along with any third party library
943 /// that internally uses glfw with cgl::window.
944 ///
945 /// # Example
946 ///
947 /// ```
948 /// cgl_rs::init();
949 /// {
950 /// // window inside scope so that it is dropped before shutdown is called
951 /// let mut window = cgl_rs::Window::new("My Window", 800, 600).unwrap();
952 /// window.rescure_callbacks();
953 /// }
954 /// cgl_rs::shutdown();
955 /// ```
956 pub fn rescure_callbacks(&self) {
957 unsafe {
958 CGL_window_resecure_callbacks(self.handle);
959 }
960 }
961
962
963 /// Makes the OpenGL context of the window current.
964 ///
965 /// # Example
966 ///
967 /// ```
968 /// cgl_rs::init();
969 /// {
970 /// // window inside scope so that it is dropped before shutdown is called
971 /// let mut window = cgl_rs::Window::new("My Window", 800, 600).unwrap();
972 /// window.make_context_current();
973 /// }
974 /// cgl_rs::shutdown();
975 /// ```
976 pub fn make_context_current(&self) {
977 unsafe {
978 CGL_window_make_context_current(self.handle);
979 }
980 }
981
982 // Gets the GLFW handle of the window.
983 ///
984 /// # Example
985 ///
986 /// ```
987 /// cgl_rs::init();
988 /// {
989 /// // window inside scope so that it is dropped before shutdown is called
990 /// let mut window = cgl_rs::Window::new("My Window", 800, 600).unwrap();
991 /// let handle = window.get_glfw_handle();
992 /// }
993 /// cgl_rs::shutdown();
994 /// ```
995 pub fn get_glfw_handle(&self) -> *mut GLFWwindow {
996 unsafe {
997 CGL_window_get_glfw_handle(self.handle)
998 }
999 }
1000
1001 // Gets the state of a keyboard key of the window.
1002 ///
1003 /// # Arguments
1004 ///
1005 /// * `key` - A `Key` enum value representing the key to get the state of.
1006 ///
1007 /// # Returns
1008 ///
1009 /// An `Action` enum value representing the state of the key.
1010 ///
1011 /// # Example
1012 ///
1013 /// ```
1014 /// cgl_rs::init();
1015 /// {
1016 /// // window inside scope so that it is dropped before shutdown is called
1017 /// let mut window = cgl_rs::Window::new("My Window", 800, 600).unwrap();
1018 /// let action = window.get_key(cgl_rs::Key::A);
1019 /// match action {
1020 /// cgl_rs::Action::Release => println!("Key A was released"),
1021 /// cgl_rs::Action::Press => println!("Key A was pressed"),
1022 /// cgl_rs::Action::Repeat => println!("Key A was repeated"),
1023 /// }
1024 /// }
1025 /// cgl_rs::shutdown();
1026 /// ```
1027 pub fn get_key(&self, key: Key) -> Action {
1028 unsafe {
1029 cgl_window_i32_to_action(CGL_window_get_key(self.handle, key as i32))
1030 }
1031 }
1032
1033 /// Checks if a keyboard key of the window is pressed.
1034 ///
1035 /// # Arguments
1036 ///
1037 /// * `key` - A `Key` enum value representing the key to check.
1038 ///
1039 /// # Returns
1040 ///
1041 /// A `bool` value representing if the key is pressed.
1042 ///
1043 /// # Example
1044 ///
1045 /// ```
1046 /// cgl_rs::init();
1047 /// {
1048 /// // window inside scope so that it is dropped before shutdown is called
1049 /// let mut window = cgl_rs::Window::new("My Window", 800, 600).unwrap();
1050 /// if window.is_key_pressed(cgl_rs::Key::A) {
1051 /// println!("Key A is pressed");
1052 /// }
1053 /// }
1054 /// cgl_rs::shutdown();
1055 /// ```
1056 pub fn is_key_pressed(&self, key: Key) -> bool {
1057 unsafe {
1058 CGL_window_is_key_pressed(self.handle, key as i32) == 1
1059 }
1060 }
1061
1062 /// Gets the state of a mouse button of the window.
1063 ///
1064 /// # Arguments
1065 ///
1066 /// * `button` - A `MouseButton` enum value representing the mouse button to get the state of.
1067 ///
1068 /// # Returns
1069 ///
1070 /// An `Action` enum value representing the state of the mouse button.
1071 ///
1072 /// # Example
1073 ///
1074 /// ```
1075 /// cgl_rs::init();
1076 /// {
1077 /// // window inside scope so that it is dropped before shutdown is called
1078 /// let mut window = cgl_rs::Window::new("My Window", 800, 600).unwrap();
1079 /// let action = window.get_mouse_button(cgl_rs::MouseButton::Left);
1080 /// match action {
1081 /// cgl_rs::Action::Release => println!("Left mouse button was released"),
1082 /// cgl_rs::Action::Press => println!("Left mouse button was pressed"),
1083 /// cgl_rs::Action::Repeat => println!("Left mouse button was repeated"),
1084 /// }
1085 /// }
1086 /// cgl_rs::shutdown();
1087 /// ```
1088 pub fn get_mouse_button(&self, button: MouseButton) -> Action {
1089 unsafe {
1090 cgl_window_i32_to_action(CGL_window_get_mouse_button(self.handle, button as i32))
1091 }
1092 }
1093
1094 /// Checks if a mouse button of the window is pressed.
1095 ///
1096 /// # Arguments
1097 ///
1098 /// * `button` - A `MouseButton` enum value representing the mouse button to check.
1099 ///
1100 /// # Returns
1101 ///
1102 /// A `bool` value representing if the mouse button is pressed.
1103 ///
1104 /// # Example
1105 ///
1106 /// ```
1107 /// cgl_rs::init();
1108 /// {
1109 /// // window inside scope so that it is dropped before shutdown is called
1110 /// let mut window = cgl_rs::Window::new("My Window", 800, 600).unwrap();
1111 /// if window.is_mouse_button_pressed(cgl_rs::MouseButton::Left) {
1112 /// println!("Left mouse button is pressed");
1113 /// }
1114 /// }
1115 /// cgl_rs::shutdown();
1116 /// ```
1117 pub fn is_mouse_button_pressed(&self, button: MouseButton) -> bool {
1118 match self.get_mouse_button(button) {
1119 Action::Press => true,
1120 _ => false
1121 }
1122 }
1123
1124 /// Gets the position of the mouse cursor relative to the top-left corner of the window.
1125 ///
1126 /// # Returns
1127 ///
1128 /// A tuple containing the x and y coordinates of the mouse cursor.
1129 ///
1130 /// # Example
1131 ///
1132 /// ```
1133 /// cgl_rs::init();
1134 /// {
1135 /// // window inside scope so that it is dropped before shutdown is called
1136 /// let mut window = cgl_rs::Window::new("My Window", 800, 600).unwrap();
1137 /// let (x, y) = window.get_mouse_position();
1138 /// println!("Mouse position: ({}, {})", x, y);
1139 /// }
1140 /// cgl_rs::shutdown();
1141 /// ```
1142 pub fn get_mouse_position(&self) -> (f64, f64) {
1143 unsafe {
1144 let (mut x, mut y) = (0.0, 0.0);
1145 CGL_window_get_mouse_position(self.handle, &mut x, &mut y);
1146 (x, y)
1147 }
1148 }
1149
1150 /// Gets the internal handle of the window.
1151 ///
1152 /// # Returns
1153 ///
1154 /// A pointer to the internal handle of the window.
1155 pub(crate) fn get_cgl_handle(&self) -> *mut CGL_window {
1156 self.handle
1157 }
1158
1159}
1160
1161impl Drop for Window {
1162 fn drop(&mut self) {
1163 self.destroy();
1164 }
1165}
1166
1167impl Clone for Window {
1168 /// Clones the current window instance.
1169 ///
1170 /// NOTE: The new instance will have the same handle, `has_been_destroyed` flag and empty `event_handlers` map.
1171 /// This means that the new instance will not be able to receive events nor will the internal window handle be
1172 /// destroyed when the new instance is dropped. The internal window handle will be destroyed when the original
1173 /// instance is dropped.
1174 ///
1175 /// # Returns
1176 ///
1177 /// A new `Window` instance with the same handle, `has_been_destroyed` flag and empty `event_handlers` map.
1178 ///
1179 /// # Example
1180 ///
1181 /// ```
1182 /// cgl_rs::init();
1183 /// {
1184 /// // window inside scope so that it is dropped before shutdown is called
1185 /// let mut window = cgl_rs::Window::new("My Window", 800, 600).unwrap();
1186 /// let mut window_clone = window.clone();
1187 /// }
1188 /// cgl_rs::shutdown();
1189 fn clone(&self) -> Self {
1190 Window {
1191 handle: self.handle,
1192 has_been_destroyed: true,
1193 event_handlers: std::collections::HashMap::new()
1194 }
1195 }
1196}