Skip to main content

app/
app.rs

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
46//pub use nb;
47use dirty::{
48	WResponse,
49	void,
50	String,
51	Vec
52};
53
54pub use dirty::{SurfaceWrapper as Surface, Color};
55
56/// The default structure to handle and manage apps
57#[allow(dead_code)]
58pub struct App<H>
59where
60	H: EventHandler + Send + Sync,
61{
62	/// List of the program windows
63	pub windows: Vec<Window>,
64	/// Cursor information
65	pub cursor: Cursor,
66	theme: ThemeDefault,
67	handler: H,
68	name: String,
69}
70
71/// This is the bridge between system events and the lib events
72pub trait EventHandler: Send + Sync
73{
74	/// handle_events is the only function for the trait and it results a non blocking Event object
75	fn handle_events(event: Event); //-> nb::Result<(), nb::Error<()>>;
76}
77
78impl<H: EventHandler> App<H>
79{
80	/// Create a new `App`
81	#[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	/// Returns the global theme defined as Self::theme_get_default()
102	pub fn get_global_theme(&self) -> ThemeDefault
103		{ self.theme.clone() }
104
105	/// Modify the current window theme
106	/// If alread set as the value provided, it does nothing
107	pub fn set_global_theme(&mut self, theme: ThemeDefault)
108		{ self.theme = theme }
109
110	/// Creates a new Window element and pushes to the App
111	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	/// init event handler
123	pub fn init(&self)
124	{
125		/*let _event = thread::spawn(move || {
126			nb::block!(H::handle_events(Event::Generic)).unwrap();
127		});*/
128
129		//dirty::Thread::default(event_thread).run();
130
131		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/// Theme struct
145#[derive(Debug, Clone, PartialEq)]
146pub struct ThemeDefault {
147	/// set the alpha value of the window
148	pub blur: bool,
149	/// default color scheme dark/light
150	pub dark: bool,
151	/// the default accent color of higlight text, buttons, etc
152	pub accent_color: Color,
153	/// the background of the window (not of the renderer)
154	pub background_color: Color,
155	/// Titlebar must be rendered or not
156	pub has_title: bool,
157}
158
159/// NativeDecoration provides the necessary abstraction used inside the `platform` modules
160pub trait NativeDecoration
161{
162	/// executes the application window
163	fn run(&self);
164	/// creates a new decoration on the system
165	fn new(title: String, width: f64, height: f64, theme: ThemeDefault) -> Result<Self, WResponse> where Self: Sized;
166	/// Apply blur to window
167	fn apply_blur(&mut self) -> Result<(), WResponse>;
168	/// exit handler
169	fn exit(&self) -> Result<(), WResponse>;
170	/// App Menu Controls
171	fn create_app_menu(&self, app_name: String) -> Result<(), WResponse>;
172}
173
174/// Detect if the current system prefers CSDs or SSDs
175/// By default, prefer server side decorations
176#[derive(Clone, PartialEq, Debug)]
177pub enum DecorationMode {
178	/// Render the window decorations on the compositor
179	ClientSide,
180	/// Render the window decorations in the window surface
181	ServerSide,
182}
183
184/// Default struct for window Decorations
185#[derive(Debug, PartialEq, Clone)]
186pub struct Decoration {
187	frame: *const void,
188	backend: Wrapper,
189	mode: DecorationMode,
190}
191
192/// OS specific. Check platform apple, nt, linux, etc
193impl Decoration {}
194
195/// Window interface
196#[derive(Clone, PartialEq, Debug)]
197pub struct Window {
198	/// Window title
199	pub title: String,
200	/// The graphical backend (on our case, vulkan)
201	pub surface: Option<Surface>,
202	/// The native window frame
203	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	/// Get system specific window backend (for renderer)
214	#[must_use]
215	pub fn get_backend(&self) -> *mut void
216		{ void::to_handle(self.decoration.backend.clone()) }
217
218	/// Connects a specified vulkan surface with the current window
219	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	/// Returns if window does have a surface or not
231	#[must_use]
232	pub fn has_surface(&self) -> bool
233		{ self.surface.is_some() }
234
235	/// Detects if the window is focused
236	#[must_use]
237	pub fn is_active(&self) -> bool { self.active }
238
239	/// Changes the `window.resizable` argument to a specific bool val
240	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/// List of possible types for the cursor
289#[derive(Debug, PartialEq, Clone)]
290pub enum CursorType {
291	/// The generic arrow cursor
292	Default,
293	/// The pointer cursor
294	Pointer,
295	/// Text selection cursor
296	TextBox,
297	/// Loading cursor
298	Loading,
299	/// Forbidden cursor
300	Forbidden,
301	/*/// Custom cursor sprite
302	 Custom(Box<Path>)*/
303}
304
305/// Default cursor struct
306#[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// transform this into a trait?
316#[allow(dead_code)]
317impl Cursor {
318	/// Get the cursor object
319	#[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	/// Returns the current position of the cursor relative to the window
331	pub fn get_relative_position() {}
332
333	/// Returns the current position of the cursor
334	#[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	/// Modify the cursor position
350	pub fn change_position(&mut self, _new_pos: (f64, f64)) {}
351
352	/// Modify the cursor position relative to the window
353	pub fn change_relative_position(&mut self, _new_pos: (f64, f64)) {}
354
355	/// Hides the Cursor
356	/// If the cursor is already hidden, it does nothing
357	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	/// Shows the cursor
367	/// If the cursor is already visible, it does nothing
368	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	/// Locks the cursor in the current place and hides it
378	pub fn disable(&mut self)
379	{
380		info!("disabling cursor");
381		self.disabled = true;
382		self.hide();
383	}
384
385	/// Set the cursor type
386	/// For example: `CursorType::Pointer` for click actions or `CursorType::Custom(Path)` for custom textures
387	pub fn set_type(&mut self, mode: CursorType)
388		{ self.mode = mode; }
389
390	/// Detects if the cursor is visible or not
391	#[must_use]
392	pub fn is_visible(&self) -> bool
393		{ self.visible }
394}