cafe/
lib.rs

1extern crate sdl2;
2extern crate tea;
3extern crate latte;
4
5pub mod audio;
6pub mod render;
7pub mod keyboard;
8pub mod mouse;
9pub mod joystick;
10
11use keyboard::Scancode;
12pub use tea::vec::*;
13use render::Render;
14use sdl2::{video::{Window, WindowBuilder, GLContext, GLProfile}, EventPump, event::{Event, WindowEvent}, TimerSubsystem, keyboard::{Keycode, Mod}};
15
16pub use sdl2::joystick::HatState;
17pub use sdl2::controller::{Axis, Button};
18
19pub type Point2D = Vec2;
20pub type Point3D = Vec3;
21
22#[derive(Default, Debug, Copy, Clone, PartialOrd, PartialEq)]
23pub struct Rect {
24    pub x: f32, pub y: f32,
25    pub w: f32, pub h: f32
26}
27
28impl Rect {
29    pub fn new(x: f32, y: f32, w: f32, h: f32) -> Rect {
30        Rect { x, y, w, h }
31    }
32}
33
34#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
35pub struct Color(u8, u8, u8, u8);
36
37impl Color {
38    pub fn rgb(r: u8, g: u8, b: u8) -> Color { Color(r, g, b, 255) }
39    pub fn rgba(r: u8, g: u8, b: u8, a: u8) -> Color { Color(r, g, b, a) }
40
41    pub fn black() -> Self { Self::rgb(0, 0, 0) }
42    pub fn white() -> Self { Self::rgb(255, 255, 255) }
43
44    pub fn to_vec4(&self) -> Vec4 {
45        let r = self.0 as f32 / 255.0;
46        let g = self.1 as f32 / 255.0;
47        let b = self.2 as f32 / 255.0;
48        let a = self.3 as f32 / 255.0;
49        Vec4::new(r, g, b, a)
50    }
51}
52
53pub struct Cafe<T: Game> {
54    running: bool,
55    window: Window,
56    timer: TimerSubsystem,
57    _gl_context: GLContext,
58    render: Option<T::Render>,
59    event_pump: EventPump,
60
61    current_time: u32,
62        // game: dyn Game
63}
64
65#[derive(Default, Debug)]
66pub struct GameSettings {
67    pub window: WindowSettings
68}
69
70#[derive(Debug)]
71pub struct WindowSettings {
72    pub title: String,
73    pub width: i32, pub height: i32,
74    pub resizable: bool,
75    pub borderless: bool
76}
77
78impl Default for WindowSettings {
79    fn default() -> Self {
80        WindowSettings {
81            title: "cafe 0.1.0".to_string(),
82            width: 640, height: 380,
83            resizable: false,
84            borderless: false,
85        }
86    }
87}
88
89pub struct CafeBuilder {
90    sdl: sdl2::Sdl,
91    video: sdl2::VideoSubsystem,
92    window: WindowBuilder
93}
94
95impl CafeBuilder {
96    pub fn new(title: &str, width: u32, height: u32) -> Self {
97        let sdl = sdl2::init().unwrap();
98        let video = sdl.video().unwrap();
99
100        let mut window = video.window(title, width, height);
101        window.position_centered().opengl();
102
103        let gl_attr = video.gl_attr();
104        gl_attr.set_context_profile(GLProfile::Core);
105        gl_attr.set_context_version(3, 3);
106
107        CafeBuilder {
108            sdl,
109            video,
110            window,
111        }
112    }
113
114    #[doc = "Resizable window"]
115    pub fn resizable(&mut self) -> &mut Self {
116        self.window.resizable();
117        self
118    }
119
120    #[doc = "Borderless window"]
121    pub fn borderless(&mut self) -> &mut Self {
122        self.window.borderless();
123        self
124    }
125
126    #[doc = "Start window as hidden"]
127    pub fn hidden(&mut self) -> &mut Self {
128        self.window.hidden();
129        self
130    }
131
132    #[doc = "Real fullscreen mode"]
133    pub fn fullscreen(&mut self) -> &mut Self {
134        self.window.fullscreen();
135        self
136    }
137    
138    #[doc = "Fake fullscreen mode (borderless maximized window)"]
139    pub fn fullscreen_desktop(&mut self) -> &mut Self {
140        self.window.fullscreen_desktop();
141        self
142    }
143
144    #[doc = "Build Cafe context"]
145    pub fn build<T: Game>(&mut self) -> Result<Cafe<T>, String> {
146
147        let sdl_context = &self.sdl;
148        let video_subsystem = &self.video;
149
150        let window = self.window.build().unwrap();
151
152        let sz = window.size();
153        let size = (sz.0 as i32, sz.1 as i32);
154        
155        let _gl_context = window.gl_create_context().expect("Failed to create GLContext");
156        tea::load_with(|name| video_subsystem.gl_get_proc_address(name) as *const _);
157
158        let event_pump = sdl_context.event_pump().unwrap();
159
160        let mut render = Some(T::create_render());
161        render.as_mut().expect("Failed to create render").on_resize(size.0, size.1);
162
163        tea::viewport(0, 0, size.0, size.1);
164
165        crate::keyboard::init();
166        crate::audio::init();
167
168        let timer = sdl_context.timer().unwrap();
169        
170        // mocha::init();
171        Ok(Cafe {
172            running: true,
173            window,
174            render,
175            _gl_context,
176            event_pump,
177            timer,
178            current_time: 0
179        })
180    }
181}
182
183pub trait Game {
184    type Render: Render;
185    fn new() -> Self;
186    fn create_render() -> Self::Render { Self::Render::new() }
187    fn run(&mut self, _dt: f32, _render: &mut Self::Render) -> bool { false }
188    // fn update(&mut self, _dt: f32) {}
189    // fn draw(&mut self, _render: &mut Self::Render) {}
190
191    fn quit(&self) -> bool { true }
192    fn window_close(&self) {}
193    fn window_resized(&mut self, _width: i32, _height: i32) {}
194    fn window_moved(&mut self, _x: i32, _y: i32) {}
195    fn window_minimized(&mut self) {}
196    fn window_maximized(&mut self) {}
197
198    fn key_down(&mut self, _keycode: Option<Keycode>, _scancode: Option<Scancode>, _repeat: bool, _keymod: Mod) {}
199    fn key_up(&mut self, _keycode: Option<Keycode>, _scancode: Option<Scancode>, _repeat: bool, _keymod: Mod) {}
200    fn joystick_added(&mut self, _which: u32) {}
201    fn joystick_removed(&mut self, _which: u32) {}
202    fn controller_added(&mut self, _which: u32) {}
203    fn controller_removed(&mut self, _which: u32) {}
204
205    fn mouse_motion(&mut self, _x: i32, _y: i32, _xrel: i32, _yrel: i32) {}
206    fn mouse_wheel(&mut self, _scroll_x: f32, _scroll_y: f32, _flipped: bool) {}
207
208    fn text_editing(&mut self, _text: String, _start: i32, _length: i32) {}
209    fn text_input(&mut self, _text: String) {}
210
211    fn joy_button_up(&mut self, _which: u32, _button: u8) {}
212    fn joy_button_down(&mut self, _which: u32, _button: u8) {}
213    fn joy_hat_motion(&mut self, _which: u32, _hat: u8, _state: HatState) {}
214    fn joy_ball_motion(&mut self, _which: u32, _ball: u8, _xrel: i16, _yrel: i16) {}
215    fn joy_axis_motion(&mut self, _which: u32, _axis: u8, _value: i16) {}
216
217    fn controller_button_up(&mut self, _which: u32, _button: Button) {}
218    fn controller_button_down(&mut self, _which: u32, _button: Button) {}
219    fn controller_axis_motion(&mut self, _which: u32, _axis: Axis, _value: i16) {}
220    fn controller_remapped(&mut self, _which: u32) {}
221
222    fn drop_file(&mut self, _filename: String) {}
223    fn drop_text(&mut self, _text: String) {}
224    fn drop_begin(&mut self) {}
225    fn drop_complete(&mut self) {}
226
227    fn finger_up(&mut self, _touch_id: i64, _finger_id: i64, _x: f32, _y: f32, _dx: f32, _dy: f32, _pressure: f32) {}
228    fn finger_down(&mut self, _touch_id: i64, _finger_id: i64, _x: f32, _y: f32, _dx: f32, _dy: f32, _pressure: f32) {}
229    fn finger_motion(&mut self, _touch_id: i64, _finger_id: i64, _x: f32, _y: f32, _dx: f32, _dy: f32, _pressure: f32) {}
230    fn dollar_record(&mut self, _touch_id: i64, _gesture_id: i64, _num_fingers: u32, _error: f32, _x: f32, _y: f32) {}
231    fn dollar_gesture(&mut self, _touch_id: i64, _gesture_id: i64, _num_fingers: u32, _error: f32, _x: f32, _y: f32) {}
232    fn clipboard_update(&mut self) {}
233}
234
235impl<T: Game> Cafe<T> {
236    pub fn run(&mut self) {
237        let mut game = T::new();
238        let render = self.render.as_mut().unwrap();
239        let mut running = true;
240        while running {
241            let last_time = self.current_time;
242            self.current_time = self.timer.ticks();
243
244            let delta = (self.current_time - last_time) as f32 / 1000.0;
245
246            running = game.run(delta, render);
247
248            for event in self.event_pump.poll_iter() {
249                match event {
250                    Event::Quit {..} => { running = !game.quit(); },
251                    Event::Window { win_event, .. } => {
252                        match win_event {
253                            WindowEvent::Close => {
254                                game.window_close();
255                            },
256                            WindowEvent::Moved(x, y) => {
257                                game.window_moved(x, y);
258                            },
259                            WindowEvent::Resized(w, h) => {
260                                render.on_resize(w, h);
261                                game.window_resized(w, h);
262                            },
263                            WindowEvent::Minimized => {
264                                game.window_minimized();
265                            },
266                            WindowEvent::Maximized => {
267                                game.window_maximized();
268                            },
269                            _ => {}
270                        }
271                    },
272                    Event::KeyDown { keycode, scancode, repeat, keymod, .. } => { game.key_down(keycode, scancode, repeat, keymod); },
273                    Event::KeyUp { keycode, scancode, repeat, keymod, .. } => { game.key_up(keycode, scancode, repeat, keymod); },
274                    Event::JoyDeviceAdded { which, .. } => { game.joystick_added(which); },
275                    Event::JoyDeviceRemoved { which, .. } => { game.joystick_removed(which); },
276                    Event::JoyButtonUp { which, button_idx, .. } => { game.joy_button_up(which, button_idx); },
277                    Event::JoyButtonDown { which, button_idx, .. } => { game.joy_button_down(which, button_idx); },
278                    Event::JoyHatMotion { which, hat_idx, state, .. } => { game.joy_hat_motion(which, hat_idx, state); },
279                    Event::JoyBallMotion { which, ball_idx, xrel, yrel, .. } => { game.joy_ball_motion(which, ball_idx, xrel, yrel); },
280                    Event::JoyAxisMotion { which, axis_idx, value, .. } => { game.joy_axis_motion(which, axis_idx, value); },
281                    Event::ControllerDeviceAdded { which, .. } => { game.controller_added(which); },
282                    Event::ControllerDeviceRemoved { which, .. } => { game.controller_removed(which); },
283                    Event::ControllerButtonUp { which, button, .. } => { game.controller_button_up(which, button); },
284                    Event::ControllerButtonDown { which, button, .. } => { game.controller_button_down(which, button); },
285                    Event::ControllerAxisMotion { which, axis, value, .. } => { game.controller_axis_motion(which, axis, value); },
286                    Event::ControllerDeviceRemapped { which, .. } => { game.controller_remapped(which); },
287                    Event::MouseMotion { x, y, xrel, yrel, .. } => { game.mouse_motion(x, y, xrel, yrel); },
288                    Event::MouseWheel { direction, x, y, .. } => { game.mouse_wheel(x as f32, y as f32, direction == sdl2::mouse::MouseWheelDirection::Flipped); },
289                    Event::TextEditing { text, start, length, .. } => { game.text_editing(text, start, length); },
290                    Event::TextInput { text, .. } => { game.text_input(text); },
291                    Event::DropFile { filename, .. } => { game.drop_file(filename); },
292                    Event::DropText { filename, .. } => { game.drop_text(filename); },
293                    Event::DropBegin { .. } => { game.drop_begin(); },
294                    Event::DropComplete { .. } => { game.drop_complete(); },
295                    Event::FingerUp { touch_id, finger_id, x, y, dx, dy, pressure, .. } => { game.finger_up(touch_id, finger_id, x, y, dx, dy, pressure); },
296                    Event::FingerDown { touch_id, finger_id, x, y, dx, dy, pressure, .. } => { game.finger_down(touch_id, finger_id, x, y, dx, dy, pressure); },
297                    Event::FingerMotion { touch_id, finger_id, x, y, dx, dy, pressure, .. } => { game.finger_motion(touch_id, finger_id, x, y, dx, dy, pressure); },
298                    Event::DollarRecord { touch_id, gesture_id, num_fingers, error, x, y, .. } => { game.dollar_record(touch_id, gesture_id, num_fingers, error, x, y); },
299                    Event::DollarGesture { touch_id, gesture_id, num_fingers, error, x, y, .. } => { game.dollar_gesture(touch_id, gesture_id, num_fingers, error, x, y); },
300                    Event::ClipboardUpdate { .. } => { game.clipboard_update(); },
301                    _ => {}
302                }
303            }
304
305            // self.game.update(delta);
306            // self.game.draw(render);
307            self.window.gl_swap_window();
308        }
309    }
310    
311    pub fn get_window(&self) -> &Window { &self.window }
312    pub fn get_render(&mut self) -> &mut T::Render { self.render.as_mut().unwrap() }
313
314    pub fn is_running(&self) -> bool { self.running }
315}
316
317impl<T: Game> Drop for Cafe<T> {
318    fn drop(&mut self) {
319        crate::audio::deinit();
320    }
321}