1#![no_std]
2#![deny(
3 deprecated,
4 rust_2018_idioms,
5 clippy::shadow_unrelated,
6 unreachable_code,
7 unused_imports,
8 unused_variables,
9 unsafe_op_in_unsafe_fn,
10 clippy::unwrap_used,
11 clippy::expect_used,
12 clippy::shadow_reuse,
13 clippy::shadow_same,
14 clippy::dbg_macro,
15 clippy::print_stdout,
16 clippy::print_stderr,
17 clippy::panic,
18 clippy::indexing_slicing,
19 clippy::arithmetic_side_effects,
20 clippy::float_arithmetic,
21 clippy::unwrap_in_result,
22 clippy::exit,
23 clippy::wildcard_imports,
24 missing_docs,
25 clippy::all,
26 trivial_casts,
27 trivial_numeric_casts,
28 unused_extern_crates,
29 unused_import_braces,
30 unused_qualifications,
31 unused_results,
32)]
33#![allow(clippy::tabs_in_doc_comments)]
34#![doc = include_str!("../README.md")]
35
36#[cfg(target_os = "none")]
37compile_error!("no bare metal support");
38
39mod platform;
40mod events;
41
42pub use events::Event;
43use platform::Wrapper;
44use log::{warn, info, debug, error};
45
46use dirty::{
48 WResponse,
49 void,
50 String,
51 Vec
52};
53
54pub use dirty::{SurfaceWrapper as Surface, Color};
55
56#[allow(dead_code)]
58pub struct App<H>
59where
60 H: EventHandler + Send + Sync,
61{
62 pub windows: Vec<Window>,
64 pub cursor: Cursor,
66 theme: ThemeDefault,
67 handler: H,
68 name: String,
69}
70
71pub trait EventHandler: Send + Sync
73{
74 fn handle_events(event: Event); }
77
78impl<H: EventHandler> App<H>
79{
80 #[must_use]
82 pub fn new(handler: H, name: &'static str) -> Self
83 {
84 let theme = ThemeDefault {
85 blur: false,
86 dark: false,
87 accent_color: Color::from(255, 255, 255, 255),
88 background_color: Color::from(255, 255, 255, 255),
89 has_title: true,
90 };
91
92 Self {
93 windows: Vec::new(),
94 cursor: Cursor::get_cursor(),
95 theme,
96 handler,
97 name: String::from(name),
98 }
99 }
100
101 pub fn get_global_theme(&self) -> ThemeDefault
103 { self.theme.clone() }
104
105 pub fn set_global_theme(&mut self, theme: ThemeDefault)
108 { self.theme = theme }
109
110 pub fn new_window(
112 &mut self,
113 title: &'static str,
114 size: (f64, f64),
115 ) -> Result<Window, WResponse>
116 {
117 let window = Window::new(self.name.clone(), title, self.theme.clone(), size)?;
118 self.windows.push(window.clone());
119 Ok(window)
120 }
121
122 pub fn init(&self)
124 {
125 if let Some(window) = self.windows.first() {
132 window.decoration.run();
133 };
134 }
135}
136
137#[unsafe(no_mangle)]
138extern "C" fn event_thread(p: *mut void) -> *mut void
139{
140 debug!("creating event thread!");
141 p
142}
143
144#[derive(Debug, Clone, PartialEq)]
146pub struct ThemeDefault {
147 pub blur: bool,
149 pub dark: bool,
151 pub accent_color: Color,
153 pub background_color: Color,
155 pub has_title: bool,
157}
158
159pub trait NativeDecoration
161{
162 fn run(&self);
164 fn new(title: String, width: f64, height: f64, theme: ThemeDefault) -> Result<Self, WResponse> where Self: Sized;
166 fn apply_blur(&mut self) -> Result<(), WResponse>;
168 fn exit(&self) -> Result<(), WResponse>;
170 fn create_app_menu(&self, app_name: String) -> Result<(), WResponse>;
172}
173
174#[derive(Clone, PartialEq, Debug)]
177pub enum DecorationMode {
178 ClientSide,
180 ServerSide,
182}
183
184#[derive(Debug, PartialEq, Clone)]
186pub struct Decoration {
187 frame: *const void,
188 backend: Wrapper,
189 mode: DecorationMode,
190}
191
192impl Decoration {}
194
195#[derive(Clone, PartialEq, Debug)]
197pub struct Window {
198 pub title: String,
200 pub surface: Option<Surface>,
202 decoration: Decoration,
204 resizable: bool,
205 position: (f32, f32),
206 active: bool,
207 theme: ThemeDefault,
208}
209
210#[forbid(unsafe_code)]
211impl Window
212{
213 #[must_use]
215 pub fn get_backend(&self) -> *mut void
216 { void::to_handle(self.decoration.backend.clone()) }
217
218 pub fn connect_surface(&mut self, surface: Surface) -> Result<(), WResponse>
220 {
221 if !self.has_surface() {
222 self.surface = Some(surface);
223 return Ok(());
224 }
225 warn!("this window is already connected to a surface!");
226 info!("to connect to another surface, please remove the current one");
227 Err(WResponse::ChannelInUse)
228 }
229
230 #[must_use]
232 pub fn has_surface(&self) -> bool
233 { self.surface.is_some() }
234
235 #[must_use]
237 pub fn is_active(&self) -> bool { self.active }
238
239 pub fn resizable(&mut self, arg: bool) { self.resizable = arg }
241}
242
243trait PrivateWindow {
244 fn new(app_name: String, title: &'static str, theme: ThemeDefault, size: (f64, f64)) ->
245 Result<Window, WResponse>;
246}
247
248impl PrivateWindow for Window {
249 fn new(
250 app_name: String,
251 title: &'static str,
252 theme: ThemeDefault,
253 size: (f64, f64)
254 ) -> Result<Self, WResponse>
255 {
256 #[allow(unused_mut)]
257 let mut decoration = match Decoration::new(
258 String::from(title),
259 size.0,
260 size.1,
261 theme.clone()
262 ) {
263 Ok(v) => v,
264 Err(e) => {
265 error!("Unexpected decoration error: {:?}", e);
266 return Err(WResponse::UnexpectedError)
267 },
268 };
269
270 let _menu = decoration.create_app_menu(app_name);
271
272 if theme.blur
273 && let Err(response) = decoration.apply_blur()
274 { warn!("blur: {response:?}") }
275
276 Ok(Window {
277 decoration,
278 surface: None,
279 active: false,
280 resizable: true,
281 position: (0.0, 0.0),
282 title: String::from(title),
283 theme,
284 })
285 }
286}
287
288#[derive(Debug, PartialEq, Clone)]
290pub enum CursorType {
291 Default,
293 Pointer,
295 TextBox,
297 Loading,
299 Forbidden,
301 }
304
305#[allow(dead_code)]
307#[derive(Debug, PartialEq, Clone)]
308pub struct Cursor {
309 position: (f64, f64),
310 mode: CursorType,
311 visible: bool,
312 disabled: bool,
313}
314
315#[allow(dead_code)]
317impl Cursor {
318 #[must_use]
320 pub fn get_cursor() -> Self
321 {
322 Cursor {
323 position: Self::get_position(),
324 mode: CursorType::Default,
325 visible: true,
326 disabled: false,
327 }
328 }
329
330 pub fn get_relative_position() {}
332
333 #[must_use]
335 pub fn get_position() -> (f64, f64)
336 {
337 #[cfg(target_os = "macos")]
338 let pos = objc2_app_kit::NSEvent::mouseLocation();
339
340 #[cfg(not(target_os = "macos"))]
341 let (x, y) = (0.0, 0.0);
342
343 #[cfg(target_os = "macos")]
344 let (x, y) = (pos.x, pos.y);
345
346 (x, y)
347 }
348
349 pub fn change_position(&mut self, _new_pos: (f64, f64)) {}
351
352 pub fn change_relative_position(&mut self, _new_pos: (f64, f64)) {}
354
355 pub fn hide(&mut self)
358 {
359 #[cfg(target_os = "macos")]
360 let _err: objc2_core_graphics::CGError =
361 objc2_core_graphics::CGDisplayHideCursor(0);
362
363 self.visible = false;
364 }
365
366 pub fn show(&mut self)
369 {
370 #[cfg(target_os = "macos")]
371 let _err: objc2_core_graphics::CGError =
372 objc2_core_graphics::CGDisplayShowCursor(0);
373
374 self.visible = true;
375 }
376
377 pub fn disable(&mut self)
379 {
380 info!("disabling cursor");
381 self.disabled = true;
382 self.hide();
383 }
384
385 pub fn set_type(&mut self, mode: CursorType)
388 { self.mode = mode; }
389
390 #[must_use]
392 pub fn is_visible(&self) -> bool
393 { self.visible }
394}