open_ui/
lib.rs

1#[macro_use]
2extern crate glium;
3
4#[allow(unused_imports)]
5use glium::{glutin, Surface};
6use glium::glutin::dpi::LogicalSize;
7use glium::glutin::event::VirtualKeyCode;
8use glium::draw_parameters::Blend;
9use glium::glutin::event::Event::RedrawEventsCleared;
10use glium::glutin::event_loop::ControlFlow;
11
12use std::time::Duration;
13use std::time::Instant;
14use std::hash::Hasher;
15use std::hash::Hash;
16use std::collections::hash_map::DefaultHasher;
17
18/// The initial settings that a windowed application
19/// will need to initialize and display itself.
20pub struct UIBlueprint {
21    pub title: String,
22    pub dimensions: (u32, u32),
23    pub resizeable: bool,
24    pub maximized: bool,
25    pub preserve_aspect_ratio: bool,
26    pub frames_per_second: u32,
27}
28
29impl UIBlueprint {
30    pub fn default() -> UIBlueprint {
31        UIBlueprint {
32            title: "".to_string(),
33            dimensions: (800, 800),
34            resizeable: true,
35            maximized: false,
36            preserve_aspect_ratio: true,
37            frames_per_second: 60,
38        }
39    }
40
41    pub fn title(self, title: &str) -> UIBlueprint {
42        UIBlueprint { title: title.to_string(), ..self }
43    }
44
45    pub fn dimensions(self, dimensions: (u32, u32)) -> UIBlueprint {
46        UIBlueprint { dimensions, ..self }
47    }
48
49    pub fn resizeable(self, resizeable: bool) -> UIBlueprint {
50        UIBlueprint { resizeable, ..self }
51    }
52
53    pub fn maximized(self, maximized: bool) -> UIBlueprint {
54        UIBlueprint { maximized, ..self }
55    }
56
57    pub fn preserve_aspect_ratio(self, preserve_aspect_ratio: bool) -> UIBlueprint {
58        UIBlueprint { preserve_aspect_ratio, ..self }
59    }
60
61    pub fn frames_per_second(self, frames_per_second: u32) -> UIBlueprint {
62        UIBlueprint { frames_per_second, ..self }
63    }
64}
65
66pub trait UIController {
67    /// This function wil be called once before the application opens,
68    /// and determines the initial settings of the rendering window.
69    fn blueprint(&self) -> UIBlueprint;
70
71    /// This function will be called called every frame,
72    /// and returns the contents of the next render-able frame,
73    /// or `None` if the application should terminate.
74    fn next_frame(&mut self) -> Option<RgbaImageRegion>;
75
76    /// This function will be called every frame, receiving
77    /// input events, and usually responding by modifying state.
78    fn process_events(&mut self, events: &Vec<UIEvent>);
79}
80
81const VERTEX_SHADER_SRC: &str = r#"
82    #version 150
83
84    in vec2 dest;
85    in vec2 src;
86    out vec2 v_src;
87
88
89    void main() {
90        v_src = src;
91        gl_Position = vec4(dest, 0.0, 1.0);
92    }
93"#;
94
95const FRAGMENT_SHADER_SRC: &str = r#"
96    #version 150
97
98    in vec2 v_src;
99    out vec4 color;
100
101    uniform sampler2D sampler;
102
103    void main() {
104        color = texture(sampler, v_src);
105    }
106"#;
107
108
109/// A rectangular image made up of RGBA pixels
110pub struct RgbaImage {
111    width: u32,
112    height: u32,
113    bytes: Vec<u8>,
114}
115
116/// A read-only region of an `RgbaImage`
117pub struct RgbaImageRegion<'a> {
118    width: u32,
119    height: u32,
120    bytes: &'a[u8],
121}
122
123impl<'a> RgbaImageRegion<'a> {
124    pub fn width(&self) -> u32 {
125        self.width
126    }
127
128    pub fn height(&self) -> u32 {
129        self.height
130    }
131
132    /// Retrieve a single pixel at a given point.
133    pub fn get_pixel(&self, x: u32, y: u32) -> Option<RgbaPixel> {
134        let index = (((self.width * y) + x) * 4) as usize;
135    
136        if index >= self.bytes.len() {
137            return None;
138        }
139    
140        Some((
141            self.bytes[index + 0],
142            self.bytes[index + 1],
143            self.bytes[index + 2],
144            self.bytes[index + 3],
145        ))
146    }
147
148}
149
150pub type RgbaPixel = (u8,u8,u8,u8);
151
152const WHITE: RgbaPixel = (255, 255, 255, 255);
153
154// Assumes that the color beneath is pure white
155fn de_alpha(pixel: &RgbaPixel, background: &RgbaPixel) -> RgbaPixel {
156    let a = pixel.3 as f32 / 255.0;
157    let r = pixel.0 as f32;
158    let g = pixel.1 as f32;
159    let b = pixel.2 as f32;
160
161    let bg_r = background.0 as f32;
162    let bg_g = background.1 as f32;
163    let bg_b = background.2 as f32;
164
165    let r = ((1.0 - a) * bg_r + a * r).round() as u8;
166    let g = ((1.0 - a) * bg_g + a * g).round() as u8;
167    let b = ((1.0 - a) * bg_b + a * b).round() as u8;
168    (r,g,b,255)
169}
170
171#[test]
172fn _de_alpha() {
173    let rgba = (14, 18, 201, 128);
174    let after_de_alpha = de_alpha(&rgba, &WHITE);
175    assert_eq!(after_de_alpha, (134, 136, 228, 255));
176}
177
178impl RgbaImage {
179    /// Create a new `RgbaImage` with the given dimensions.
180    pub fn new(w: u32, h: u32) -> RgbaImage {
181        RgbaImage {
182            width: w,
183            height: h,
184            bytes: vec![0; (w as usize * h as usize) * 4],
185        }
186    }
187
188    pub fn width(&self) -> u32 {
189        self.width
190    }
191
192    pub fn height(&self) -> u32 {
193        self.height
194    }
195
196    /// Draw a single pixel at a given point.
197    pub fn set_pixel(&mut self, x: u32, y: u32, pixel: RgbaPixel) -> bool {
198        if x >= self.width { return false; }
199        if y >= self.height { return false; }
200
201        let index = (((self.width * y) + x) * 4) as usize;
202
203        self.bytes[index + 0] = pixel.0;
204        self.bytes[index + 1] = pixel.1;
205        self.bytes[index + 2] = pixel.2;
206        self.bytes[index + 3] = pixel.3;
207
208        true
209    }
210
211    /// Retrieve a single pixel at a given point.
212    pub fn get_pixel(&self, x: u32, y: u32) -> Option<RgbaPixel> {
213        let index = (((self.width * y) + x) * 4) as usize;
214
215        if index >= self.bytes.len() {
216            return None;
217        }
218
219        Some((
220            self.bytes[index + 0],
221            self.bytes[index + 1],
222            self.bytes[index + 2],
223            self.bytes[index + 3],
224        ))
225    }
226
227    /// Superimpose another `RgbaImage` on top of this one,
228    /// with its top-left corner at the given point.
229    pub fn draw(&mut self, img: &RgbaImage, x: i32, y: i32) {
230        for img_y in 0..img.height {
231            for img_x in 0..img.width {
232                let pixel = img.get_pixel(img_x, img_y).unwrap();
233
234                let canvas_x = x + img_x as i32;
235                let canvas_y = y + img_y as i32;
236
237                if canvas_x >= 0 && canvas_y >= 0 {
238                    let target_pixel = match self.get_pixel(canvas_x as u32, canvas_y as u32) {
239                        Some(pixel) => pixel,
240                        None => { continue }
241                    };
242
243                    // Converting both pixels to RGB before overwriting
244                    let target_pixel = de_alpha(&target_pixel, &WHITE);
245                    let pixel = de_alpha(&pixel, &target_pixel);
246                    self.set_pixel(canvas_x as u32, canvas_y as u32, pixel);
247                }
248            }
249        }
250    }
251
252    /// Fill the entire image with a single color.
253    pub fn fill(&mut self, color: RgbaPixel) {
254        for y in 0..self.height {
255            for x in 0..self.width {
256                self.set_pixel(x, y, color);
257            }
258        }
259    }
260
261    /// Expand or shrink the entire image by a given scaling factor.
262    pub fn nearest_neighbor_scale(img: &RgbaImage, factor: f32) -> RgbaImage {
263        let mut new_img = RgbaImage::new(
264            (img.width as f32 * factor) as u32,
265            (img.height as f32 * factor) as u32,
266        );
267
268        // Calculating a ratio of a single pixel's size to the whole image
269        let ratio_x = 1.0 / new_img.width as f32;
270        let ratio_y = 1.0 / new_img.height as f32;
271
272        for y in 0..new_img.height {
273            for x in 0..new_img.width {
274
275                // Determining which x and y values to sample from
276                let progress_x = ratio_x * x as f32;
277                let progress_y = ratio_y * y as f32;
278
279                let src_x = progress_x * img.width as f32;
280                let src_y = progress_y * img.height as f32;
281
282                // Applying the sampled pixel to the output image
283                let pixel = img.get_pixel(src_x as u32, src_y as u32).unwrap();
284                new_img.set_pixel(x, y, pixel);
285            }
286        }
287
288        new_img
289    }
290
291
292    pub fn as_region(&self) -> RgbaImageRegion {
293        self.get_region(
294            (0, 0),
295            (self.width() - 1, self.height() - 1),
296        ).unwrap()
297    }
298
299    pub fn get_region(&self, top_left: (u32, u32), bottom_right: (u32, u32)) -> Option<RgbaImageRegion> {
300        let (start_x, start_y) = top_left;
301        let start_index = (((self.width * start_y) + start_x) * 4) as usize;
302
303        let (end_x, end_y) = bottom_right;
304        let end_index = (((self.width * end_y) + end_x) * 4) as usize + 3;
305
306        if end_x < start_x { return None; }
307        if end_y < start_y { return None; }
308
309        if start_index >= self.bytes.len() { return None; }
310        if end_index > self.bytes.len() { return None; }
311
312        let width = 1 + end_x - start_x;
313        let height = 1 + end_y - start_y;
314        let bytes =  &self.bytes[start_index..end_index+1];
315
316        Some(RgbaImageRegion {
317            width,
318            height,
319            bytes,
320        })
321    }
322}
323
324#[derive(Copy, Clone, Debug)]
325struct Vertex {
326    // The vector denoting the area of incoming textures that will be
327    // used for drawing. This could be used to crop incoming textures.
328    src: [f32; 2],
329    // The vector denoting the area of the window that incoming textures
330    // will be drawn onto. This could be used to only draw textures on
331    // a partial area of the output.
332    dest: [f32; 2],
333}
334
335fn calculate_vertices(size: &LogicalSize<f32>, pixels: &RgbaImageRegion) -> Vec<Vertex> {
336    let ui_h = size.height;
337    let ui_w = size.width;
338
339    // Defining the number that the image will be scaled by
340    // to fit nicely on the UI
341    let scalar = {
342        if ui_w > ui_h { ui_h / pixels.height as f32 }
343        else { ui_w / pixels.width as f32 }
344    };
345
346    // Defining "actual image width / height"
347    let img_w = pixels.width as f32 * scalar;
348    let img_h = pixels.height as f32 * scalar;
349
350    // Defining vector magnitudes that will correctly
351    // position the 4 vertices.
352    let mag_x = img_w / ui_w;
353    let mag_y = img_h / ui_h;
354
355    vec![
356        Vertex { dest: [-mag_x, -mag_y ], src: [0.0, 0.0] },
357        Vertex { dest: [ mag_x, -mag_y ], src: [1.0, 0.0] },
358        Vertex { dest: [ mag_x,  mag_y ], src: [1.0, 1.0] },
359        Vertex { dest: [-mag_x,  mag_y ], src: [0.0, 1.0] },
360    ]
361}
362
363/// A data-less struct that manages the application.
364/// Users of this library define the application's behavior
365/// by creating a type that implements the `UIController` trait.
366pub struct UI;
367
368impl UI {
369    /// Start the application using the given `UIController` 
370    pub fn launch<T: 'static + UIController>(mut controller: T) {
371        implement_vertex!(Vertex, dest, src);
372
373        let blueprint = controller.blueprint();
374        let event_loop = glutin::event_loop::EventLoop::new();
375
376        let (width, height) = blueprint.dimensions;
377        let mut size = LogicalSize::new(width as f32, height as f32);
378        let preserve_aspect_ratio = blueprint.preserve_aspect_ratio;
379
380        let wb = glutin::window::WindowBuilder::new()
381            .with_title(blueprint.title)
382            .with_inner_size(size)
383            .with_maximized(blueprint.maximized)
384            .with_resizable(blueprint.resizeable);
385
386        let cb = glutin::ContextBuilder::new();
387        let display = glium::Display::new(wb, cb, &event_loop).unwrap();
388
389        let indices: [u16; 6] = [0,1,2,2,3,0];
390        let indices = glium::IndexBuffer::new(
391            &display,
392            glium::index::PrimitiveType::TrianglesList,
393            &indices
394        ).unwrap();
395    
396        let program = glium::Program::from_source(
397            &display,
398            VERTEX_SHADER_SRC,
399            FRAGMENT_SHADER_SRC,
400            None
401        ).unwrap();
402
403        let shape = vec![
404            Vertex { dest: [-1.0, -1.0 ], src: [0.0, 0.0] },
405            Vertex { dest: [ 1.0, -1.0 ], src: [1.0, 0.0] },
406            Vertex { dest: [ 1.0,  1.0 ], src: [1.0, 1.0] },
407            Vertex { dest: [-1.0,  1.0 ], src: [0.0, 1.0] },
408        ];
409
410        let mut vertex_buffer = glium::VertexBuffer::new(&display, &shape).unwrap();
411
412        // The extra parameters that will be used when drawing frames.
413        // Blend is important because it allows the alpha channel of
414        // RGBA to work.
415        let draw_params = glium::DrawParameters {
416            blend: Blend::alpha_blending(),
417            .. Default::default()
418        };
419
420        // Setting up timekeeping
421        let fps = blueprint.frames_per_second;
422        let refresh_interval = Duration::from_nanos(1_000_000_000 / fps as u64);
423
424        let mut ui_events = vec![];
425
426        event_loop.run(move |event, _, control_flow| {
427            let ready_for_redraw = event == RedrawEventsCleared;
428
429            if ready_for_redraw {
430                let pixels = match controller.next_frame() {
431                    None => return *control_flow = glutin::event_loop::ControlFlow::Exit,
432                    Some(pixels) => pixels,
433                };
434
435                let image = glium::texture::RawImage2d::from_raw_rgba_reversed(
436                    pixels.bytes,
437                    (pixels.width, pixels.height),
438                );
439
440                // If the aspect ratio of the UI doesn't match that of `image`
441                // imposing letterboxing to leave the aspect ratio of `image` unchanged.
442                if preserve_aspect_ratio {
443                    let shape = calculate_vertices(&size, &pixels);
444                    vertex_buffer = glium::VertexBuffer::new(&display, &shape).unwrap();
445                }
446
447                let texture = glium::texture::Texture2d::new(&display, image).unwrap();
448
449                let uniforms = uniform! {
450                    // Applying filters to prevent unwanted image smoothing
451                    sampler: texture.sampled()
452                        .magnify_filter(glium::uniforms::MagnifySamplerFilter::Nearest)
453                        .minify_filter(glium::uniforms::MinifySamplerFilter::Nearest)
454                };
455
456                let mut frame = display.draw();
457
458                // Erasing the previous frame
459                frame.clear_color(0.0,0.0,0.0,255.0);
460
461                // Drawing on the next frame
462                frame.draw(&vertex_buffer, &indices, &program, &uniforms,
463                    &draw_params).unwrap();
464
465                // Committing the drawn frame
466                frame.finish().unwrap();
467
468                // Waiting until the next frame
469                let next_frame_time = Instant::now() + refresh_interval;
470                *control_flow = ControlFlow::WaitUntil(next_frame_time);
471
472                // Processing and flushing events
473                // Should this happen at the beginning or end of each frame?
474                controller.process_events(&ui_events);
475                ui_events = vec![];
476            }
477
478            // Responding to UI events
479            match event {
480                glutin::event::Event::WindowEvent { event, .. } => match event {
481                    glutin::event::WindowEvent::CloseRequested => {
482                        *control_flow = glutin::event_loop::ControlFlow::Exit;
483                        return;
484                    },
485                    glutin::event::WindowEvent::KeyboardInput { device_id, input, .. } => {
486                        apply_keyboard_event(&device_id, &input, &mut ui_events);
487                    },
488                    glutin::event::WindowEvent::MouseInput { device_id, state, button, .. } => {
489                        apply_mouse_button_event(&device_id, &state, &button, &mut ui_events);
490                    },
491                    glutin::event::WindowEvent::Resized(phys_size) => {
492                        size = phys_size.to_logical(1.0);
493                        apply_resize_event(&size, &mut ui_events);
494                    },
495                    glutin::event::WindowEvent::CursorMoved { device_id, position, .. } => {
496                        apply_cursor_movement_event(&device_id, &position, &mut ui_events);
497                    },
498                    _ => return,
499                },
500                _ => {}
501            }
502
503
504
505        });
506
507    }
508}
509
510fn hash<T: Hash>(value: T) -> u64 {
511    let mut hasher = DefaultHasher::new();
512    value.hash(&mut hasher);
513    hasher.finish()
514}
515
516fn apply_resize_event(
517    size: &glutin::dpi::LogicalSize<f32>,
518    ui_events: &mut Vec<UIEvent>,
519) {
520    ui_events.push(UIEvent::Resize(ResizeEvent {
521        width: size.width as u32,
522        height: size.height as u32,
523    }));
524}
525
526
527fn apply_cursor_movement_event(
528    device_id: &glutin::event::DeviceId,
529    position:  &glutin::dpi::PhysicalPosition<f64>,
530    ui_events: &mut Vec<UIEvent>,
531) {
532    let position = position.to_logical::<f32>(1.0);
533
534    ui_events.push(UIEvent::CursorMovement(CursorMovementEvent {
535        device_id: hash(device_id),
536        x: position.x as u32,
537        y: position.y as u32,
538    }));
539}
540
541
542fn apply_keyboard_event(
543    device_id: &glutin::event::DeviceId,
544    input: &glutin::event::KeyboardInput,
545    ui_events: &mut Vec<UIEvent>
546) {
547    let device_id = hash(device_id);
548
549    let action = match input.state {
550        glutin::event::ElementState::Pressed => KeyboardAction::Press,
551        glutin::event::ElementState::Released => KeyboardAction::Release,
552    };
553
554    let key = match input.virtual_keycode {
555        Some(VirtualKeyCode::Key0) => KeyboardKey::Num0,
556        Some(VirtualKeyCode::Key1) => KeyboardKey::Num1,
557        Some(VirtualKeyCode::Key2) => KeyboardKey::Num2,
558        Some(VirtualKeyCode::Key3) => KeyboardKey::Num3,
559        Some(VirtualKeyCode::Key4) => KeyboardKey::Num4,
560        Some(VirtualKeyCode::Key5) => KeyboardKey::Num5,
561        Some(VirtualKeyCode::Key6) => KeyboardKey::Num6,
562        Some(VirtualKeyCode::Key7) => KeyboardKey::Num7,
563        Some(VirtualKeyCode::Key8) => KeyboardKey::Num8,
564        Some(VirtualKeyCode::Key9) => KeyboardKey::Num9,
565        Some(VirtualKeyCode::A) => KeyboardKey::A,
566        Some(VirtualKeyCode::B) => KeyboardKey::B,
567        Some(VirtualKeyCode::C) => KeyboardKey::C,
568        Some(VirtualKeyCode::D) => KeyboardKey::D,
569        Some(VirtualKeyCode::E) => KeyboardKey::E,
570        Some(VirtualKeyCode::F) => KeyboardKey::F,
571        Some(VirtualKeyCode::G) => KeyboardKey::G,
572        Some(VirtualKeyCode::H) => KeyboardKey::H,
573        Some(VirtualKeyCode::I) => KeyboardKey::I,
574        Some(VirtualKeyCode::J) => KeyboardKey::J,
575        Some(VirtualKeyCode::K) => KeyboardKey::K,
576        Some(VirtualKeyCode::L) => KeyboardKey::L,
577        Some(VirtualKeyCode::M) => KeyboardKey::N,
578        Some(VirtualKeyCode::N) => KeyboardKey::M,
579        Some(VirtualKeyCode::O) => KeyboardKey::O,
580        Some(VirtualKeyCode::P) => KeyboardKey::P,
581        Some(VirtualKeyCode::Q) => KeyboardKey::Q,
582        Some(VirtualKeyCode::R) => KeyboardKey::R,
583        Some(VirtualKeyCode::S) => KeyboardKey::S,
584        Some(VirtualKeyCode::T) => KeyboardKey::T,
585        Some(VirtualKeyCode::U) => KeyboardKey::U,
586        Some(VirtualKeyCode::V) => KeyboardKey::V,
587        Some(VirtualKeyCode::W) => KeyboardKey::W,
588        Some(VirtualKeyCode::X) => KeyboardKey::X,
589        Some(VirtualKeyCode::Y) => KeyboardKey::Y,
590        Some(VirtualKeyCode::Z) => KeyboardKey::Z,
591        Some(VirtualKeyCode::Escape) => KeyboardKey::Escape,
592        Some(VirtualKeyCode::F1) => KeyboardKey::F1,
593        Some(VirtualKeyCode::F2) => KeyboardKey::F2,
594        Some(VirtualKeyCode::F3) => KeyboardKey::F3,
595        Some(VirtualKeyCode::F4) => KeyboardKey::F4,
596        Some(VirtualKeyCode::F5) => KeyboardKey::F5,
597        Some(VirtualKeyCode::F6) => KeyboardKey::F6,
598        Some(VirtualKeyCode::F7) => KeyboardKey::F7,
599        Some(VirtualKeyCode::F8) => KeyboardKey::F8,
600        Some(VirtualKeyCode::F9) => KeyboardKey::F9,
601        Some(VirtualKeyCode::F10) => KeyboardKey::F10,
602        Some(VirtualKeyCode::F11) => KeyboardKey::F11,
603        Some(VirtualKeyCode::F12) => KeyboardKey::F12,
604        Some(VirtualKeyCode::F13) => KeyboardKey::F13,
605        Some(VirtualKeyCode::F14) => KeyboardKey::F14,
606        Some(VirtualKeyCode::F15) => KeyboardKey::F15,
607        Some(VirtualKeyCode::F16) => KeyboardKey::F16,
608        Some(VirtualKeyCode::F17) => KeyboardKey::F17,
609        Some(VirtualKeyCode::F18) => KeyboardKey::F18,
610        Some(VirtualKeyCode::F19) => KeyboardKey::F19,
611        Some(VirtualKeyCode::F20) => KeyboardKey::F20,
612        Some(VirtualKeyCode::F21) => KeyboardKey::F21,
613        Some(VirtualKeyCode::F22) => KeyboardKey::F22,
614        Some(VirtualKeyCode::F23) => KeyboardKey::F23,
615        Some(VirtualKeyCode::F24) => KeyboardKey::F24,
616        Some(VirtualKeyCode::Snapshot) => KeyboardKey::Snapshot,
617        Some(VirtualKeyCode::Scroll) => KeyboardKey::Scroll,
618        Some(VirtualKeyCode::Pause) => KeyboardKey::Pause,
619        Some(VirtualKeyCode::Insert) => KeyboardKey::Insert,
620        Some(VirtualKeyCode::Home) => KeyboardKey::Home,
621        Some(VirtualKeyCode::Delete) => KeyboardKey::Delete,
622        Some(VirtualKeyCode::End) => KeyboardKey::Delete,
623        Some(VirtualKeyCode::PageDown) => KeyboardKey::Delete,
624        Some(VirtualKeyCode::PageUp) => KeyboardKey::Delete,
625        Some(VirtualKeyCode::Left) => KeyboardKey::Left,
626        Some(VirtualKeyCode::Up) => KeyboardKey::Up,
627        Some(VirtualKeyCode::Right) => KeyboardKey::Right,
628        Some(VirtualKeyCode::Down) => KeyboardKey::Down,
629        Some(VirtualKeyCode::Back) => KeyboardKey::Back,
630        Some(VirtualKeyCode::Return) => KeyboardKey::Return,
631        Some(VirtualKeyCode::Space) => KeyboardKey::Space,
632        Some(VirtualKeyCode::Compose) => KeyboardKey::Compose,
633        Some(VirtualKeyCode::Caret) => KeyboardKey::Caret,
634        Some(VirtualKeyCode::Numlock) => KeyboardKey::Numlock,
635        Some(VirtualKeyCode::Numpad1) => KeyboardKey::Numpad1,
636        Some(VirtualKeyCode::Numpad2) => KeyboardKey::Numpad2,
637        Some(VirtualKeyCode::Numpad3) => KeyboardKey::Numpad3,
638        Some(VirtualKeyCode::Numpad4) => KeyboardKey::Numpad4,
639        Some(VirtualKeyCode::Numpad5) => KeyboardKey::Numpad5,
640        Some(VirtualKeyCode::Numpad6) => KeyboardKey::Numpad6,
641        Some(VirtualKeyCode::Numpad7) => KeyboardKey::Numpad7,
642        Some(VirtualKeyCode::Numpad8) => KeyboardKey::Numpad8,
643        Some(VirtualKeyCode::Numpad9) => KeyboardKey::Numpad9,
644        Some(VirtualKeyCode::NumpadAdd) => KeyboardKey::NumpadAdd,
645        Some(VirtualKeyCode::NumpadDivide) => KeyboardKey::NumpadDivide,
646        Some(VirtualKeyCode::NumpadDecimal) => KeyboardKey::NumpadDecimal,
647        Some(VirtualKeyCode::NumpadComma) => KeyboardKey::NumpadComma,
648        Some(VirtualKeyCode::NumpadEnter) => KeyboardKey::NumpadEnter,
649        Some(VirtualKeyCode::NumpadEquals) => KeyboardKey::NumpadEquals,
650        Some(VirtualKeyCode::NumpadMultiply) => KeyboardKey::NumpadMultiply,
651        Some(VirtualKeyCode::NumpadSubtract) => KeyboardKey::NumpadSubtract,
652        Some(VirtualKeyCode::AbntC1) => KeyboardKey::AbntC1,
653        Some(VirtualKeyCode::AbntC2) => KeyboardKey::AbntC2,
654        Some(VirtualKeyCode::Apostrophe) => KeyboardKey::Apostrophe,
655        Some(VirtualKeyCode::Apps) => KeyboardKey::Apps,
656        Some(VirtualKeyCode::Asterisk) => KeyboardKey::Asterisk,
657        Some(VirtualKeyCode::At) => KeyboardKey::At,
658        Some(VirtualKeyCode::Ax) => KeyboardKey::Ax,
659        Some(VirtualKeyCode::Backslash) => KeyboardKey::Backslash,
660        Some(VirtualKeyCode::Calculator) => KeyboardKey::Calculator,
661        Some(VirtualKeyCode::Capital) => KeyboardKey::Capital,
662        Some(VirtualKeyCode::Colon) => KeyboardKey::Colon,
663        Some(VirtualKeyCode::Comma) => KeyboardKey::Comma,
664        Some(VirtualKeyCode::Convert) => KeyboardKey::Convert,
665        Some(VirtualKeyCode::Equals) => KeyboardKey::Equals,
666        Some(VirtualKeyCode::Grave) => KeyboardKey::Grave,
667        Some(VirtualKeyCode::Kana) => KeyboardKey::Kana,
668        Some(VirtualKeyCode::Kanji) => KeyboardKey::Kanji,
669        Some(VirtualKeyCode::LAlt) => KeyboardKey::LAlt,
670        Some(VirtualKeyCode::LBracket) => KeyboardKey::LBracket,
671        Some(VirtualKeyCode::LControl) => KeyboardKey::LControl,
672        Some(VirtualKeyCode::LShift) => KeyboardKey::LShift,
673        Some(VirtualKeyCode::LWin) => KeyboardKey::LWin,
674        Some(VirtualKeyCode::Mail) => KeyboardKey::Mail,
675        Some(VirtualKeyCode::MediaSelect) => KeyboardKey::MediaSelect,
676        Some(VirtualKeyCode::MediaStop) => KeyboardKey::MediaStop,
677        Some(VirtualKeyCode::Minus) => KeyboardKey::Minus,
678        Some(VirtualKeyCode::Mute) => KeyboardKey::Mute,
679        Some(VirtualKeyCode::MyComputer) => KeyboardKey::MyComputer,
680        Some(VirtualKeyCode::NavigateForward) => KeyboardKey::NavigateForward,
681        Some(VirtualKeyCode::NavigateBackward) => KeyboardKey::NavigateBackward,
682        Some(VirtualKeyCode::NextTrack) => KeyboardKey::NextTrack,
683        Some(VirtualKeyCode::NoConvert) => KeyboardKey::NoConvert,
684        Some(VirtualKeyCode::OEM102) => KeyboardKey::OEM102,
685        Some(VirtualKeyCode::Period) => KeyboardKey::Period,
686        Some(VirtualKeyCode::PlayPause) => KeyboardKey::PlayPause,
687        Some(VirtualKeyCode::Plus) => KeyboardKey::Plus,
688        Some(VirtualKeyCode::Power) => KeyboardKey::Power,
689        Some(VirtualKeyCode::PrevTrack) => KeyboardKey::PrevTrack,
690        Some(VirtualKeyCode::RAlt) => KeyboardKey::RAlt,
691        Some(VirtualKeyCode::RBracket) => KeyboardKey::RBracket,
692        Some(VirtualKeyCode::RControl) => KeyboardKey::RControl,
693        Some(VirtualKeyCode::RShift) => KeyboardKey::RShift,
694        Some(VirtualKeyCode::RWin) => KeyboardKey::RWin,
695        Some(VirtualKeyCode::Semicolon) => KeyboardKey::Semicolon,
696        Some(VirtualKeyCode::Slash) => KeyboardKey::Slash,
697        Some(VirtualKeyCode::Sleep) => KeyboardKey::Sleep,
698        Some(VirtualKeyCode::Stop) => KeyboardKey::Stop,
699        Some(VirtualKeyCode::Sysrq) => KeyboardKey::Sysrq,
700        Some(VirtualKeyCode::Tab) => KeyboardKey::Tab,
701        Some(VirtualKeyCode::Underline) => KeyboardKey::Underline,
702        Some(VirtualKeyCode::Unlabeled) => KeyboardKey::Unlabeled,
703        Some(VirtualKeyCode::VolumeDown) => KeyboardKey::VolumeDown,
704        Some(VirtualKeyCode::VolumeUp) => KeyboardKey::VolumeUp,
705        Some(VirtualKeyCode::Wake) => KeyboardKey::Wake,
706        Some(VirtualKeyCode::WebBack) => KeyboardKey::WebBack,
707        Some(VirtualKeyCode::WebFavorites) => KeyboardKey::WebFavorites,
708        Some(VirtualKeyCode::WebForward) => KeyboardKey::WebForward,
709        Some(VirtualKeyCode::WebHome) => KeyboardKey::WebHome,
710        Some(VirtualKeyCode::WebRefresh) => KeyboardKey::WebRefresh,
711        Some(VirtualKeyCode::WebSearch) => KeyboardKey::WebSearch,
712        Some(VirtualKeyCode::WebStop) => KeyboardKey::WebStop,
713        Some(VirtualKeyCode::Yen) => KeyboardKey::Yen,
714        Some(VirtualKeyCode::Copy) => KeyboardKey::Copy,
715        Some(VirtualKeyCode::Paste) => KeyboardKey::Paste,
716        Some(VirtualKeyCode::Cut) => KeyboardKey::Cut,
717        _ => return,
718    };
719
720    let keyboard_event = KeyboardEvent {
721        device_id,
722        action,
723        key,
724    };
725
726    ui_events.push(UIEvent::Keyboard(keyboard_event));
727}
728
729// Converting glutin mouse events to native mouse button events
730fn apply_mouse_button_event(
731    device_id: &glutin::event::DeviceId,
732    state: &glutin::event::ElementState,
733    button: &glutin::event::MouseButton,
734    ui_events: &mut Vec<UIEvent>,
735) {
736    let device_id = hash(device_id);
737
738    // Determining button pressed/released
739    let button = match button {
740        glutin::event::MouseButton::Left => MouseButton::Left,
741        glutin::event::MouseButton::Right => MouseButton::Right,
742        glutin::event::MouseButton::Middle => MouseButton::Middle,
743        glutin::event::MouseButton::Other(num) => MouseButton::Other(*num),
744    };
745
746    let action = match state {
747        glutin::event::ElementState::Pressed => MouseButtonAction::Press,
748        glutin::event::ElementState::Released => MouseButtonAction::Release,
749    };
750
751    let event = MouseButtonEvent {
752        device_id,
753        button,
754        action,
755    };
756
757    ui_events.push(UIEvent::MouseButton(event));
758}
759
760#[derive(Debug, Copy, Clone, PartialEq)]
761/// Whether a keyboard key was pressed or released.
762pub enum KeyboardAction {
763    Press,
764    Release,
765}
766
767#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
768/// A physical key on a keyboard device.
769pub enum KeyboardKey {
770    Num0,
771    Num1,
772    Num2,
773    Num3,
774    Num4,
775    Num5,
776    Num6,
777    Num7,
778    Num8,
779    Num9,
780    A,
781    B,
782    C,
783    D,
784    E,
785    F,
786    G,
787    H,
788    I,
789    J,
790    K,
791    L,
792    M,
793    N,
794    O,
795    P,
796    Q,
797    R,
798    S,
799    T,
800    U,
801    V,
802    W,
803    X,
804    Y,
805    Z,
806    Escape,
807    F1,
808    F2,
809    F3,
810    F4,
811    F5,
812    F6,
813    F7,
814    F8,
815    F9,
816    F10,
817    F11,
818    F12,
819    F13,
820    F14,
821    F15,
822    F16,
823    F17,
824    F18,
825    F19,
826    F20,
827    F21,
828    F22,
829    F23,
830    F24,
831    Snapshot,
832    Scroll,
833    Pause,
834    Insert,
835    Home,
836    Delete,
837    End,
838    PageDown,
839    PageUp,
840    Left,
841    Up,
842    Right,
843    Down,
844    Back,
845    Return,
846    Space,
847    Compose,
848    Caret,
849    Numlock,
850    Numpad0,
851    Numpad1,
852    Numpad2,
853    Numpad3,
854    Numpad4,
855    Numpad5,
856    Numpad6,
857    Numpad7,
858    Numpad8,
859    Numpad9,
860    NumpadAdd,
861    NumpadDivide,
862    NumpadDecimal,
863    NumpadComma,
864    NumpadEnter,
865    NumpadEquals,
866    NumpadMultiply,
867    NumpadSubtract,
868    AbntC1,
869    AbntC2,
870    Apostrophe,
871    Apps,
872    Asterisk,
873    At,
874    Ax,
875    Backslash,
876    Calculator,
877    Capital,
878    Colon,
879    Comma,
880    Convert,
881    Equals,
882    Grave,
883    Kana,
884    Kanji,
885    LAlt,
886    LBracket,
887    LControl,
888    LShift,
889    LWin,
890    Mail,
891    MediaSelect,
892    MediaStop,
893    Minus,
894    Mute,
895    MyComputer,
896    NavigateForward,
897    NavigateBackward,
898    NextTrack,
899    NoConvert,
900    OEM102,
901    Period,
902    PlayPause,
903    Plus,
904    Power,
905    PrevTrack,
906    RAlt,
907    RBracket,
908    RControl,
909    RShift,
910    RWin,
911    Semicolon,
912    Slash,
913    Sleep,
914    Stop,
915    Sysrq,
916    Tab,
917    Underline,
918    Unlabeled,
919    VolumeDown,
920    VolumeUp,
921    Wake,
922    WebBack,
923    WebFavorites,
924    WebForward,
925    WebHome,
926    WebRefresh,
927    WebSearch,
928    WebStop,
929    Yen,
930    Copy,
931    Paste,
932    Cut,
933}
934
935#[derive(Debug, Copy, Clone, PartialEq)]
936/// An interaction that was created using a keyboard.
937pub struct KeyboardEvent {
938    pub device_id: u64,
939    pub key: KeyboardKey,
940    pub action: KeyboardAction,
941}
942
943#[derive(Debug, Copy, Clone, PartialEq)]
944/// An interaction that was created using a mouse.
945pub struct MouseButtonEvent {
946    pub device_id: u64,
947    pub button: MouseButton,
948    pub action: MouseButtonAction,
949}
950
951#[derive(Debug, Copy, Clone, PartialEq)]
952/// A physical button on a mouse device.
953pub enum MouseButton {
954    Left,
955    Right,
956    Middle,
957    Other(u16),
958}
959
960#[derive(Debug, Copy, Clone, PartialEq)]
961/// Whether a mouse button was pressed or released.
962pub enum MouseButtonAction {
963    Press,
964    Release,
965}
966
967#[derive(Debug, Copy, Clone, PartialEq)]
968/// The identity and new location of a recently moved mouse device.
969pub struct CursorMovementEvent {
970    pub device_id: u64,
971    pub x: u32,
972    pub y: u32,
973}
974
975#[derive(Debug, Copy, Clone, PartialEq)]
976/// The new size of the window after being resized.
977pub struct ResizeEvent {
978    pub width: u32,
979    pub height: u32,
980}
981
982#[derive(Debug, Copy, Clone)]
983/// An action that an end-user takes
984/// to interact with the application.
985pub enum UIEvent {
986    Keyboard(KeyboardEvent),
987    MouseButton(MouseButtonEvent),
988    CursorMovement(CursorMovementEvent),
989    Resize(ResizeEvent)
990}