Skip to main content

dirty/
common.rs

1#![no_std]
2#![feature(core_intrinsics, stmt_expr_attributes)]
3#![deny(
4	deprecated,
5	rust_2018_idioms,
6	unreachable_code,
7	unused_imports,
8	unused_variables,
9	unsafe_op_in_unsafe_fn,
10	missing_docs,
11	warnings,
12	clippy::all,
13	clippy::shadow_unrelated,
14	clippy::pedantic,
15	clippy::unwrap_used,
16	clippy::expect_used,
17	clippy::panic,
18	clippy::todo,
19	clippy::unimplemented,
20	clippy::shadow_reuse,
21	clippy::shadow_same,
22	clippy::dbg_macro,
23	clippy::print_stdout,
24	clippy::print_stderr,
25	clippy::indexing_slicing,
26	clippy::unwrap_in_result,
27	clippy::exit,
28	clippy::wildcard_imports,
29	clippy::missing_docs_in_private_items,
30	clippy::doc_markdown,
31	clippy::empty_docs,
32	clippy::unwrap_or_default,
33	clippy::match_wild_err_arm,
34	clippy::needless_pass_by_value,
35	clippy::redundant_closure,
36	clippy::large_stack_arrays,
37	missing_debug_implementations,
38	trivial_casts,
39	trivial_numeric_casts,
40	unused_extern_crates,
41	unused_import_braces,
42	unused_qualifications,
43	unused_results,
44	macro_use_extern_crate
45)]
46#![allow(clippy::tabs_in_doc_comments, internal_features)]
47//! This is a helper crate, with minimum dependencies, not even std included
48//!
49//! Things in here should and will be dirty!
50//! That's why there are so many `#[deny]` configs (clippy helps a lot here)
51#![doc = include_str!("../README.md")]
52
53extern crate alloc;
54pub use alloc::{
55	boxed::Box,
56	string::{String, ToString},
57	slice,
58	str,
59	vec::Vec,
60	format,
61};
62
63/// OS specific methods based on systemcalls (ASM)
64pub mod syscall;
65
66/// This represents the possible state of the socket response
67#[cfg(target_family = "unix")]
68#[repr(C)]
69#[derive(Debug)]
70pub struct SocketResponse
71{
72	/**
73	 * This represents the state of the connection.
74	 *
75	 * If `-1`, then the connection failed
76	 */
77	pub status: i32,
78	/**
79	 * if the connection was sucesseful,
80	 * then this will return the int id of the server socket
81	 */
82	pub server_socket: i32,
83}
84
85/// Wrapper type for the C `struct` that stores threads
86#[cfg(target_family = "unix")]
87#[derive(Debug)]
88#[repr(C)]
89#[allow(non_camel_case_types, clippy::missing_docs_in_private_items)]
90struct c_Thread
91{
92	pub id: i32,
93	pub thread: *mut void,
94}
95
96#[cfg(target_family = "unix")]
97/// This will handle with our C imports from `unix/socket.c`
98mod unix {
99	use crate::{AnyFunction, SocketResponse, void, c_Thread};
100
101	unsafe extern "C" {
102		pub(crate) fn create_socket(address: *mut void) -> SocketResponse;
103		pub(crate) fn read_socket(server_socket: i32, ch: *mut void) -> *mut void;
104		pub(crate) fn write_socket(server_socket: i32, ch: *mut void);
105		pub(crate) fn close_socket(server_socket: i32);
106		pub(crate) fn getenv(find: *const i8) -> *const i8;
107		pub(crate) fn create_thread(function: AnyFunction) -> c_Thread;
108		pub(crate) fn kill_thread(thread: &c_Thread);
109	}
110}
111
112/// Type for a function repr in C that takes `void* arg` and returns `void*`
113#[cfg(target_family = "unix")]
114pub type AnyFunction = extern "C" fn(*mut void) -> *mut void;
115
116/*#[unsafe(no_mangle)]
117pub extern "C" fn fn_wrapper(function: *mut fn(*mut void) -> *mut void) -> *mut void
118{
119	let rs_function = unsafe { *function };
120	rs_function(core::ptr::null_mut())
121}*/
122
123/// This is a thread interface with the C implementation
124#[derive(Debug)]
125#[cfg(target_family = "unix")]
126pub struct Thread {
127	/// The function beeing executed in the new thread
128	pub function: AnyFunction,
129	/// If the thread is active, this contains the ID and `pthread_t` struct
130	thread: Option<c_Thread>,
131}
132
133#[cfg(target_family = "unix")]
134impl Thread
135{
136	/// Creates a new thread with the field `thread_id` and a provided function
137	#[must_use]
138	pub fn default(function: AnyFunction) -> Self
139	{
140		Self {
141			function,
142			thread: None
143		}
144	}
145
146	/// Runs the `&self.thread`
147	pub fn run(&mut self)
148	{
149		let thread = unsafe { unix::create_thread(self.function) };
150		self.thread = Some(thread);
151	}
152
153	/**
154	 * Kills the specified running thread
155	 *
156	 * # Errors
157	 *
158	 * if the user tries to kill a thread that is not running, it will return Err(InvalidRequest)
159	 */
160	pub fn kill(&self) -> Result<(), WResponse>
161	{
162		let Some(ref thread) = self.thread else { return Err(WResponse::InvalidRequest) };
163		unsafe { unix::kill_thread(thread); }
164		Ok(())
165	}
166}
167
168/**
169 * The function will return the size of a string.
170 *
171 * This works by prompting the first byte of the string (represented by the pointer `p`),
172 * then the loop will get all following characters until the `\0` (string termination character)
173 */
174#[cfg(target_family = "unix")]
175unsafe fn c_strlen(p: *const u8, bypass_hardcoded_limit: Option<usize>) -> Option<usize>
176{
177	// this fucker.
178	// Sometimes the C function passes a pointer to nowhere (final of the env array),
179	// this happends when the env requested doesn't exist on `char **environ`
180	if p.is_null() { return None; }
181
182	// to avoid (but not prevent) boundary related errors,
183	// 1024 is a hardcoded limit in case `\0` doesn't exist
184	let limit = bypass_hardcoded_limit.unwrap_or(1024);
185	let mut len = 0;
186
187	while len < limit {
188		if unsafe { *p.add(len) == 0 } { return Some(len); }
189		len += 1;
190	}
191
192	None
193}
194
195/**
196 * this basicly creates a new string based on the initial location and the final size
197 * `ptr` is the first byte and `len` is how long it should read the string
198 */
199#[cfg(target_family = "unix")]
200unsafe fn getenv_str(ptr: *const u8) -> Option<&'static [u8]>
201{
202	unsafe {
203		let len = c_strlen(ptr, None);
204		Some(slice::from_raw_parts(ptr, len?))
205	}
206}
207
208/**
209 * this converts the raw byte value into a readable string
210 * <https://doc.rust-lang.org/stable/core/str/fn.from_utf8.html>
211 */
212#[cfg(target_family = "unix")]
213fn convert_bytes_to_string(bytes: &[u8]) -> Option<String>
214{
215	let Ok(str) = str::from_utf8(bytes) else { return None };
216	Some(ToString::to_string(str))
217}
218
219/**
220 * this will check the environ array and search for an specific keyword
221 *
222 * # Example
223 * ```rust
224 * if let Some(user) = getenv("USER") {
225 *     log::debug!("your user: {:?}", user);
226 * };
227 * ```
228 */
229#[cfg(target_family = "unix")]
230#[must_use]
231pub fn getenv(find: &'static str) -> Option<String>
232{
233	let raw_pointer = unsafe { unix::getenv(find.as_ptr().cast::<i8>()) };
234	let string = unsafe { getenv_str(raw_pointer.cast::<u8>()) };
235	convert_bytes_to_string(string?)
236}
237
238#[cfg(target_family = "unix")]
239#[derive(Debug)]
240/// The default Socket struct.
241pub struct Socket {
242	/// The same field of `SocketResponse.server_socket`.
243	/// This time in the rust layout.
244	/// Can be None in case the `socket::create_socket()` returned `-1` (or err in the c lib for sockets)
245	socket_id: Option<i32>,
246}
247
248#[cfg(target_family = "unix")]
249impl Socket {
250	/// Create a new socket connection to the defined address
251	#[must_use]
252	pub fn new(address: &'static [u8]) -> Self
253	{
254		let response: SocketResponse =
255			unsafe { unix::create_socket(void::to_handle(address)) };
256
257		if response.status == -1 {
258			return Socket { socket_id: None };
259		}
260
261		let socket_id = Some(response.server_socket);
262		Socket { socket_id, }
263	}
264
265	/// read the socket signal
266	#[must_use]
267	pub fn read_socket(&self, ch: &'static [u8]) -> Option<Box<&[f8]>>
268	{
269		let socket_id = self.socket_id?;
270		let response = unsafe { unix::read_socket(socket_id, void::to_handle(ch)) };
271		Some(Box::new(void::from_handle(response)))
272	}
273
274	/// write a socket signal
275	pub fn write_socket(&self, ch: &'static [u8])
276	{
277		let Some(socket_id) = self.socket_id else { return };
278		unsafe { unix::write_socket(socket_id, void::to_handle(ch)) };
279	}
280
281	/// close the connection with the socket
282	pub fn close_socket(&self)
283	{
284		let Some(socket_id) = self.socket_id else { return };
285		unsafe { unix::close_socket(socket_id) }
286	}
287}
288
289/// Always trust the f8 type. The ABI is not your friend!
290///
291/// This can be ether i8 or u8 depending on the current ABI specification used
292#[cfg(not(all(target_os = "linux", target_env = "musl", target_arch = "aarch64")))]
293#[allow(non_camel_case_types)]
294pub type f8 = i8;
295
296// fuck the ABI
297/// Always trust the f8 type. The ABI is not your friend!
298///
299/// This can be ether i8 or u8 depending on the current ABI specification used
300#[cfg(all(target_os = "linux", target_env = "musl", target_arch = "aarch64"))]
301#[allow(non_camel_case_types)]
302pub type f8 = u8;
303
304/// just a void type
305#[repr(C)]
306#[allow(non_camel_case_types)]
307#[derive(Debug)]
308pub struct void {
309	/// This is a pointer of nothing
310	/// An u8 array of size 0
311	/// similar as how `core::ffi::c_void` works
312	_private: [u8; 0],
313}
314
315impl void {
316	/// Get a T type value and stores it safely as a generic type
317	#[must_use]
318	#[inline]
319	pub fn to_handle<T>(val: T) -> *mut void
320		{ Box::into_raw(Box::new(val)).cast::<void>() }
321
322	/// Espects a T return type and a Boxed `void` pointer to get value inside the Box
323	#[must_use]
324	#[inline]
325	pub fn from_handle<T>(ptr: *const void) -> T
326		{ unsafe { *Box::from_raw(ptr as *mut T) } }
327}
328
329/// int32 bool type
330pub static TRUE: u32 = 1;
331/// int32 bool type
332pub static FALSE: u32 = 0;
333
334/** Possible responses
335 *
336 * 6## : Window Request Failed
337 *
338 * 4## : Rendererer Request Failed
339 *
340 * 5## : General Program limitation
341 */
342#[derive(Debug)]
343pub enum WResponse
344{
345	/// The binary does not support this function
346	BinarySpecificLimitation	= 500,
347	/// Tried to use a wayland protocol that wasn't implemented on the compositor
348	ProtocolNotSuported			= 501,
349	/// tried to access something and the request was denied by the OS
350	AccessDenied				= 502,
351	/// Recived a value that wasn't supposed to be empty or an error
352	UnexpectedError				= 503,
353	/// this will cause a buffer overflow
354	OutOfBounds					= 504,
355	/// user tried to do an impossible action
356	InvalidRequest				= 505,
357	/// Tried to do something with the window, but the compositor denied
358	ForbiddenByCompositor		= 601,
359	/// Something for macos
360	ChannelInUse				= 400,
361	/// A dynamic linked dependency was missing on execution
362	MissingDependencies			= 401,
363}
364
365// for some reason I can't move this to app.rs
366/// Abtraction layer for multiple OS support
367#[derive(Clone, PartialEq, Debug)]
368pub struct SurfaceWrapper(pub *mut void);
369
370impl SurfaceWrapper
371{
372	/// Create a new wrapper
373	#[must_use]
374	pub fn new<T>(wrap: T) -> Self { SurfaceWrapper(void::to_handle(wrap)) }
375	/// Is wrapper valid?
376	#[must_use]
377	pub fn is_null(&self) -> bool { self.0.is_null() }
378	/// cast wrapper to original value
379	#[must_use]
380	pub fn cast<T>(&self) -> T { void::from_handle(self.0) }
381}
382
383/// RGB color implementation
384/// reference: <https://github.com/seancroach/hex_color/blob/main/src/lib.rs>
385#[derive(PartialEq, Clone, Debug)]
386#[allow(missing_docs, non_snake_case)]
387pub struct Color { pub R: u8, pub G: u8, pub B: u8, pub A: u8, }
388
389// 0.0039215686274 <- I got here before realizing that this is just 1/255
390/// Just a simple constant to normalize the RGB (0-255) value to the normal shader value (0-1)
391/// In old days people used to this trick: `(x * 257) >> 16` (nice ✧ദ്ദി)
392const RGB_NORM: f64 = 1.0 / 255.0;
393
394impl Color {
395	/// Create new color value
396	#[must_use]
397	#[allow(non_snake_case)]
398	pub fn from(R: u8, G: u8, B: u8, A: u8) -> Self { Self { R, G, B, A } }
399
400	/// Converts this to a functional method to be used inside functions
401	#[must_use]
402	pub fn to_default(&self) -> ( f64, f64, f64, f64 )
403	{(
404		f64::from(self.R) * RGB_NORM,
405		f64::from(self.G) * RGB_NORM,
406		f64::from(self.B) * RGB_NORM,
407		f64::from(self.A) * RGB_NORM,
408	)}
409}