1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
//! wwind is a cross-platform Rust UI library that is meant to provide a
//! nice programming experience and significantly reduce runtime errors and
//! undefined/unexpected behavior. It is meant to do all of this while still
//! providing reasonable performance and platform consistency.
//! # Example
//! ```rust,no_run
//! use wwind::{WWindInstance, WWindState, Window, Color, RectRegion};
//!
//! fn main() {
//!     let instance = WWindInstance::new(|state: &mut WWindState| {
//!         let mut window = state.add_window(100, 100, 500, 500, "test title");
//!
//!         window.on_redraw(|_, window, _| {
//!             let (w, h) = window.get_size();
//!             let window_rect = RectRegion { x: 0, y: 0, width: w, height: h };
//!
//!             let mut context = window.get_drawing_context();
//!
//!             // Draw background //
//!
//!             context.set_draw_color(Color::from_hex(0xffffff));
//!             context.draw_rectangle(window_rect);
//!             
//!             // Draw lines //
//!
//!             context.set_draw_color(Color::from_rgb(0, 255, 0));
//!             context.draw_line(0, 0, w, h);
//!             
//!             context.set_draw_color(Color::from_rgb(255,0,0));
//!             context.draw_line(w, 0, 0, h);
//!         });
//!     });
//!
//!     instance.unwrap().run();
//! }
//! ```
//! ![Expected Output](https://github.com/wr7/wwind/raw/master/docs/example.png)

use std::{marker::PhantomData, mem::MaybeUninit};

//  TODO:
// -  fix Win32Data leak
// -  add support for color modes besides TrueColor

#[derive(Clone, Copy, Debug)]
pub struct Color {
    pub red: u8,
    pub green: u8,
    pub blue: u8,
}

impl From<Color> for u32 {
    fn from(value: Color) -> Self {
        value.red as u32 | ((value.green as u32) << 8) | ((value.blue as u32) << 16)
    }
}

impl From<u32> for Color {
    fn from(value: u32) -> Self {
        Self::from_hex(value)
    }
}

impl Color {
    pub const fn from_hex(hex: u32) -> Self {
        Self {
            blue: (hex & 0x000000FF) as u8,
            green: ((hex & 0x0000FF00) >> 8) as u8,
            red: ((hex & 0x00FF0000) >> 16) as u8,
        }
    }
    pub const fn from_rgb(red: u8, green: u8, blue: u8) -> Self {
        Self { red, green, blue }
    }
}

use util::PhantomUnsend;

mod core;
mod drawing_context;
mod state;
mod util;
mod window;

pub use drawing_context::DrawingContext;
pub use state::WWindInitState;
pub use state::WWindState;
pub use window::Window;

static mut SHOULD_EXIT: bool = false;

#[derive(Clone, Copy, Debug)]
pub struct RectRegion {
    pub x: u16,
    pub y: u16,
    pub width: u16,
    pub height: u16,
}

impl RectRegion {
    pub fn get_bottom_y(&self) -> u16 {
        self.height + self.y
    }
    pub fn get_right_x(&self) -> u16 {
        self.x + self.width
    }
}

pub struct WWindInstance<OnInit: FnOnce(&mut WWindInitState<UserData>) -> UserData, UserData = ()> {
    state: WWindInitState<UserData>,
    on_init: OnInit,
    _unsend: PhantomUnsend,
}

impl<OnInit: FnOnce(&mut WWindInitState<UserData>) -> UserData, UserData>
    WWindInstance<OnInit, UserData>
{
    pub fn new(on_init: OnInit) -> Option<Self> {
        let state = WWindInitState::new()?;
        let _unsend = Default::default();

        Some(Self {
            on_init,
            state,
            _unsend,
        })
    }

    pub fn run(mut self) {
        let userdata = (self.on_init)(&mut self.state);
        let userdata = Box::into_raw(Box::new(userdata));

        unsafe { state::USERDATA = userdata as *mut _ };

        unsafe {
            while !SHOULD_EXIT && self.state.do_windows_exist() {
                self.state.wait_for_events();
                self.state.destroy_pending_windows();
            }

            self.state.destroy();
        }
    }
}