Vendor dependencies for 0.3.0 release

This commit is contained in:
2025-09-27 10:29:08 -05:00
parent 0c8d39d483
commit 82ab7f317b
26803 changed files with 16134934 additions and 0 deletions

218
vendor/orbclient/src/blur.rs vendored Normal file
View File

@@ -0,0 +1,218 @@
// SPDX-License-Identifier: MIT
/*
Inspired from http://blog.ivank.net/fastest-gaussian-blur.html the algorithm 4.
The struct MathColor is needed for the calculate with bigger numbers, the Color struct save the r,g,b values with a u8.
*/
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
use core::ops::{Add, AddAssign, Sub};
use crate::color::Color;
#[derive(Copy, Clone)]
pub struct MathColor {
pub r: isize,
pub g: isize,
pub b: isize,
}
impl MathColor {
pub fn new(color: Color) -> Self {
MathColor {
r: color.r() as isize,
g: color.g() as isize,
b: color.b() as isize,
}
}
pub fn get_multiplied_color(&mut self, iarr: f32) -> Color {
Color::rgb(
(self.r as f32 * iarr).round() as u8,
(self.g as f32 * iarr).round() as u8,
(self.b as f32 * iarr).round() as u8,
)
}
}
impl Add for MathColor {
type Output = MathColor;
#[inline(always)]
fn add(self, color: MathColor) -> MathColor {
MathColor {
r: self.r + color.r,
g: self.g + color.g,
b: self.b + color.b,
}
}
}
impl AddAssign for MathColor {
#[inline(always)]
fn add_assign(&mut self, color: MathColor) {
*self = MathColor {
r: self.r + color.r,
g: self.g + color.g,
b: self.b + color.b,
};
}
}
impl Sub for MathColor {
type Output = MathColor;
#[inline(always)]
fn sub(self, color: MathColor) -> MathColor {
MathColor {
r: self.r - color.r,
g: self.g - color.g,
b: self.b - color.b,
}
}
}
pub fn gauss_blur(data: &mut [Color], w: u32, h: u32, r: f32) {
let bxs = boxes_for_gauss(r, 3);
let mut tcl = data.to_owned();
box_blur(
&mut tcl,
data,
w as usize,
h as usize,
((bxs[0] - 1) / 2) as usize,
);
box_blur(
&mut tcl,
data,
w as usize,
h as usize,
((bxs[1] - 1) / 2) as usize,
);
box_blur(
&mut tcl,
data,
w as usize,
h as usize,
((bxs[2] - 1) / 2) as usize,
);
}
fn boxes_for_gauss(sigma: f32, n: usize) -> Vec<i32> {
let w_ideal: f32 = ((12.0 * sigma * sigma / n as f32) + 1.0).sqrt();
let mut wl: i32 = w_ideal.floor() as i32;
if wl % 2 == 0 {
wl -= 1;
};
let wu: i32 = wl + 2;
let m_ideal: f32 = (12.0 * sigma * sigma
- n as f32 * wl as f32 * wl as f32
- 4.0 * n as f32 * wl as f32
- 3.0 * n as f32)
/ (-4.0 * wl as f32 - 4.0);
let m: usize = m_ideal.round() as usize;
let mut sizes = Vec::<i32>::new();
for i in 0..n {
sizes.push(if i < m { wl } else { wu });
}
sizes
}
fn box_blur(tcl: &mut [Color], scl: &mut [Color], w: usize, h: usize, r: usize) {
box_blur_t(scl, tcl, w, h, r);
box_blur_h(tcl, scl, w, h, r);
}
#[inline(always)]
fn box_blur_h(tcl: &mut [Color], scl: &mut [Color], w: usize, h: usize, r: usize) {
let iarr: f32 = 1.0 / (r + r + 1) as f32;
for i in 0..h {
let mut ti: usize = i * w;
let mut li: usize = ti;
let mut ri: usize = ti + r;
let fv = MathColor::new(tcl[ti]);
let lv = MathColor::new(tcl[ti + w - 1]);
let mut val: MathColor = MathColor {
r: (r + 1) as isize * fv.r,
g: (r + 1) as isize * fv.g,
b: (r + 1) as isize * fv.b,
};
for j in 0..r {
val += MathColor::new(tcl[ti + j]);
}
for _ in 0..(r + 1) {
val += MathColor::new(tcl[ri]) - fv;
scl[ti] = val.get_multiplied_color(iarr);
ti += 1;
ri += 1;
}
for _ in (r + 1)..(w - r) {
val += MathColor::new(tcl[ri]) - MathColor::new(tcl[li]);
scl[ti] = val.get_multiplied_color(iarr);
ti += 1;
ri += 1;
li += 1;
}
for _ in (w - r)..w {
val += lv - MathColor::new(tcl[li]);
scl[ti] = val.get_multiplied_color(iarr);
ti += 1;
li += 1;
}
}
}
#[inline(always)]
fn box_blur_t(tcl: &mut [Color], scl: &mut [Color], w: usize, h: usize, r: usize) {
let iarr: f32 = 1.0 / (r + r + 1) as f32;
for i in 0..w {
let mut ti: usize = i;
let mut li: usize = ti;
let mut ri: usize = ti + r * w;
let fv = MathColor::new(tcl[ti]);
let lv = MathColor::new(tcl[ti + w * (h - 1)]);
let mut val: MathColor = MathColor {
r: (r + 1) as isize * fv.r,
g: (r + 1) as isize * fv.g,
b: (r + 1) as isize * fv.b,
};
for j in 0..r {
val += MathColor::new(tcl[ti + j * w]);
}
for _ in 0..(r + 1) {
val += MathColor::new(tcl[ri]) - fv;
scl[ti] = val.get_multiplied_color(iarr);
ti += w;
ri += w;
}
for _ in (r + 1)..(h - r) {
val += MathColor::new(tcl[ri]) - MathColor::new(tcl[li]);
scl[ti] = val.get_multiplied_color(iarr);
ti += w;
ri += w;
li += w;
}
for _ in (h - r)..h {
val += lv - MathColor::new(tcl[li]);
scl[ti] = val.get_multiplied_color(iarr);
ti += w;
li += w;
}
}
}

203
vendor/orbclient/src/color.rs vendored Normal file
View File

@@ -0,0 +1,203 @@
// SPDX-License-Identifier: MIT
use core::fmt;
#[cfg(feature = "serde")]
use serde::de::{self, Deserializer};
#[cfg(feature = "serde")]
use serde::Deserialize;
/// A color
#[derive(Copy, Clone)]
#[repr(transparent)]
pub struct Color {
pub data: u32,
}
impl Color {
/// Create a new color from RGB
pub const fn rgb(r: u8, g: u8, b: u8) -> Self {
Color {
data: 0xFF000000 | ((r as u32) << 16) | ((g as u32) << 8) | (b as u32),
}
}
/// Set the alpha
pub const fn rgba(r: u8, g: u8, b: u8, a: u8) -> Self {
Color {
data: ((a as u32) << 24) | ((r as u32) << 16) | ((g as u32) << 8) | (b as u32),
}
}
/// Get the r value
pub fn r(&self) -> u8 {
((self.data & 0x00FF0000) >> 16) as u8
}
/// Get the g value
pub fn g(&self) -> u8 {
((self.data & 0x0000FF00) >> 8) as u8
}
/// Get the b value
pub fn b(&self) -> u8 {
(self.data & 0x000000FF) as u8
}
/// Get the alpha value
pub fn a(&self) -> u8 {
((self.data & 0xFF000000) >> 24) as u8
}
/// Interpolate between two colors
pub fn interpolate(start_color: Color, end_color: Color, scale: f64) -> Color {
let r = Color::interp(start_color.r(), end_color.r(), scale);
let g = Color::interp(start_color.g(), end_color.g(), scale);
let b = Color::interp(start_color.b(), end_color.b(), scale);
let a = Color::interp(start_color.a(), end_color.a(), scale);
Color::rgba(r, g, b, a)
}
fn interp(start_color: u8, end_color: u8, scale: f64) -> u8 {
((end_color as f64 - start_color as f64) * scale + start_color as f64) as u8
}
}
/// Compare two colors (Do not take care of alpha)
impl PartialEq for Color {
fn eq(&self, other: &Color) -> bool {
self.r() == other.r() && self.g() == other.g() && self.b() == other.b()
}
}
impl fmt::Debug for Color {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(f, "{:#010X}", { self.data })
}
}
#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for Color {
fn deserialize<D>(deserializer: D) -> Result<Color, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_i32(ColorVisitor)
}
}
#[cfg(feature = "serde")]
struct ColorVisitor;
#[cfg(feature = "serde")]
impl<'de> de::Visitor<'de> for ColorVisitor {
type Value = Color;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("Color specification in HEX format '#ARGB'")
}
fn visit_str<E>(self, color_spec: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
if color_spec.len() != 9 {
return Err(E::custom(format!("Color spec must be of format '#AARRGGBB' ('{}')", color_spec)))
}
if &color_spec[0..1] != "#" {
return Err(E::custom(format!("Color spec must begin with '#' ('{}')", color_spec)));
}
let a = u8::from_str_radix(&color_spec[1..3], 16)
.map_err(|e| E::custom(e))?;
let r = u8::from_str_radix(&color_spec[3..5], 16)
.map_err(|e| E::custom(e.to_string()))?;
let g = u8::from_str_radix(&color_spec[5..7], 16)
.map_err(|e| E::custom(e.to_string()))?;
let b = u8::from_str_radix(&color_spec[7..9], 16)
.map_err(|e| E::custom(e.to_string()))?;
Ok(Color::rgba(r, g, b, a))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn partial_eq() {
assert_eq!(Color::rgb(1, 2, 3), Color::rgba(1, 2, 3, 200));
assert_ne!(Color::rgb(1, 2, 3), Color::rgba(11, 2, 3, 200));
assert_eq!(Color::rgba(1, 2, 3, 200), Color::rgba(1, 2, 3, 200));
}
#[test]
fn alignment() {
assert_eq!(4, core::mem::size_of::<Color>());
assert_eq!(8, core::mem::size_of::<[Color; 2]>());
assert_eq!(12, core::mem::size_of::<[Color; 3]>());
assert_eq!(16, core::mem::size_of::<[Color; 4]>());
assert_eq!(20, core::mem::size_of::<[Color; 5]>());
}
#[cfg(features = "serde")]
mod serde {
use serde_derive::Deserialize;
use toml;
use crate::Color;
#[derive(Deserialize)]
struct TestColor {
color: Color,
}
#[test]
fn deserialize_ok() {
let color_spec = r##"color = "#00010203""##;
let test_color: TestColor = toml::from_str(color_spec).expect("Color spec did not parse correctly");
assert_eq!(test_color.color.a(), 0, "Alpha channel incorrect");
assert_eq!(test_color.color.r(), 1, "Red channel incorrect");
assert_eq!(test_color.color.g(), 2, "Green channel incorrect");
assert_eq!(test_color.color.b(), 3, "Blue channel incorrect");
}
#[test]
fn deserialize_hex() {
let color_spec = r##"color = "#AABBCCDD""##;
let _: TestColor = toml::from_str(color_spec).expect("Color spec did not parse HEX correctly");
}
#[test]
fn deserialize_no_hash() {
let color_spec = r##"color = "$00010203""##;
let test_color: Result<TestColor, _> = toml::from_str(color_spec);
assert!(test_color.is_err(), "Color spec should not parse correctly without leading '#'");
assert!(test_color.err().unwrap().to_string().contains("must begin with '#'"));
}
#[test]
fn deserialize_not_hex() {
let color_spec = r##"color = "#GG010203""##;
let test_color: Result<TestColor, _> = toml::from_str(color_spec);
assert!(test_color.is_err(), "Color spec should not parse invalid HEX correctly");
assert!(test_color.err().unwrap().to_string().contains("invalid digit"));
}
#[test]
fn deserialize_str_too_long() {
let color_spec = r##"color = "#0001020304""##;
let test_color: Result<TestColor, _> = toml::from_str(color_spec);
assert!(test_color.is_err(), "Color spec should not parse invalid spec correctly");
assert!(test_color.err().unwrap().to_string().contains("must be of format '#AARRGGBB'"));
}
#[test]
fn deserialize_str_too_short() {
let color_spec = r##"color = "#000102""##;
let test_color: Result<TestColor, _> = toml::from_str(color_spec);
assert!(test_color.is_err(), "Color spec should not parse invalid spec correctly");
assert!(test_color.err().unwrap().to_string().contains("must be of format '#AARRGGBB'"));
}
}
}

647
vendor/orbclient/src/event.rs vendored Normal file
View File

@@ -0,0 +1,647 @@
// SPDX-License-Identifier: MIT
use core::ops::{Deref, DerefMut};
use core::{char, mem, slice};
pub const EVENT_NONE: i64 = 0;
pub const EVENT_KEY: i64 = 1;
pub const EVENT_MOUSE: i64 = 2;
pub const EVENT_BUTTON: i64 = 3;
pub const EVENT_SCROLL: i64 = 4;
pub const EVENT_QUIT: i64 = 5;
pub const EVENT_FOCUS: i64 = 6;
pub const EVENT_MOVE: i64 = 7;
pub const EVENT_RESIZE: i64 = 8;
pub const EVENT_SCREEN: i64 = 9;
pub const EVENT_CLIPBOARD: i64 = 10;
pub const EVENT_MOUSE_RELATIVE: i64 = 11;
pub const EVENT_DROP: i64 = 12;
pub const EVENT_TEXT_INPUT: i64 = 13;
pub const EVENT_CLIPBOARD_UPDATE: i64 = 14;
pub const EVENT_HOVER: i64 = 15;
/// An optional event
#[derive(Copy, Clone, Debug)]
pub enum EventOption {
/// A key event
Key(KeyEvent),
/// A text input event
TextInput(TextInputEvent),
/// A mouse event (absolute)
Mouse(MouseEvent),
/// A mouse event (relative)
MouseRelative(MouseRelativeEvent),
/// A mouse button event
Button(ButtonEvent),
/// A mouse scroll event
Scroll(ScrollEvent),
/// A quit request event
Quit(QuitEvent),
/// A focus event
Focus(FocusEvent),
/// A move event
Move(MoveEvent),
/// A resize event
Resize(ResizeEvent),
/// A screen report event
Screen(ScreenEvent),
/// A clipboard event
Clipboard(ClipboardEvent),
/// A clipboard update event
ClipboardUpdate(ClipboardUpdateEvent),
/// A drop file / text event (available on linux, windows and macOS)
Drop(DropEvent),
/// A hover event
Hover(HoverEvent),
/// An unknown event
Unknown(Event),
/// No event
None,
}
/// An event
#[derive(Copy, Clone, Debug)]
#[repr(packed)]
pub struct Event {
pub code: i64,
pub a: i64,
pub b: i64,
}
#[allow(clippy::new_without_default)]
impl Event {
/// Create a null event
pub fn new() -> Event {
Event {
code: 0,
a: 0,
b: 0,
}
}
/// Convert the event ot an optional event
// TODO: Consider doing this via a From trait.
pub fn to_option(self) -> EventOption {
match self.code {
EVENT_NONE => EventOption::None,
EVENT_KEY => EventOption::Key(KeyEvent::from_event(self)),
EVENT_TEXT_INPUT => EventOption::TextInput(TextInputEvent::from_event(self)),
EVENT_MOUSE => EventOption::Mouse(MouseEvent::from_event(self)),
EVENT_MOUSE_RELATIVE => {
EventOption::MouseRelative(MouseRelativeEvent::from_event(self))
}
EVENT_BUTTON => EventOption::Button(ButtonEvent::from_event(self)),
EVENT_SCROLL => EventOption::Scroll(ScrollEvent::from_event(self)),
EVENT_QUIT => EventOption::Quit(QuitEvent::from_event(self)),
EVENT_FOCUS => EventOption::Focus(FocusEvent::from_event(self)),
EVENT_MOVE => EventOption::Move(MoveEvent::from_event(self)),
EVENT_RESIZE => EventOption::Resize(ResizeEvent::from_event(self)),
EVENT_SCREEN => EventOption::Screen(ScreenEvent::from_event(self)),
EVENT_CLIPBOARD => EventOption::Clipboard(ClipboardEvent::from_event(self)),
EVENT_CLIPBOARD_UPDATE => {
EventOption::ClipboardUpdate(ClipboardUpdateEvent::from_event(self))
}
EVENT_DROP => EventOption::Drop(DropEvent::from_event(self)),
EVENT_HOVER => EventOption::Hover(HoverEvent::from_event(self)),
_ => EventOption::Unknown(self),
}
}
}
impl Deref for Event {
type Target = [u8];
fn deref(&self) -> &[u8] {
unsafe {
slice::from_raw_parts(self as *const Event as *const u8, mem::size_of::<Event>())
as &[u8]
}
}
}
impl DerefMut for Event {
fn deref_mut(&mut self) -> &mut [u8] {
unsafe {
slice::from_raw_parts_mut(self as *mut Event as *mut u8, mem::size_of::<Event>())
as &mut [u8]
}
}
}
pub const K_A: u8 = 0x1E;
pub const K_B: u8 = 0x30;
pub const K_C: u8 = 0x2E;
pub const K_D: u8 = 0x20;
pub const K_E: u8 = 0x12;
pub const K_F: u8 = 0x21;
pub const K_G: u8 = 0x22;
pub const K_H: u8 = 0x23;
pub const K_I: u8 = 0x17;
pub const K_J: u8 = 0x24;
pub const K_K: u8 = 0x25;
pub const K_L: u8 = 0x26;
pub const K_M: u8 = 0x32;
pub const K_N: u8 = 0x31;
pub const K_O: u8 = 0x18;
pub const K_P: u8 = 0x19;
pub const K_Q: u8 = 0x10;
pub const K_R: u8 = 0x13;
pub const K_S: u8 = 0x1F;
pub const K_T: u8 = 0x14;
pub const K_U: u8 = 0x16;
pub const K_V: u8 = 0x2F;
pub const K_W: u8 = 0x11;
pub const K_X: u8 = 0x2D;
pub const K_Y: u8 = 0x15;
pub const K_Z: u8 = 0x2C;
pub const K_0: u8 = 0x0B;
pub const K_1: u8 = 0x02;
pub const K_2: u8 = 0x03;
pub const K_3: u8 = 0x04;
pub const K_4: u8 = 0x05;
pub const K_5: u8 = 0x06;
pub const K_6: u8 = 0x07;
pub const K_7: u8 = 0x08;
pub const K_8: u8 = 0x09;
pub const K_9: u8 = 0x0A;
// Numpad keys (codes 0x70-0x79)
pub const K_NUM_0: u8 = 0x70;
pub const K_NUM_1: u8 = 0x71;
pub const K_NUM_2: u8 = 0x72;
pub const K_NUM_3: u8 = 0x73;
pub const K_NUM_4: u8 = 0x74;
pub const K_NUM_5: u8 = 0x75;
pub const K_NUM_6: u8 = 0x76;
pub const K_NUM_7: u8 = 0x77;
pub const K_NUM_8: u8 = 0x78;
pub const K_NUM_9: u8 = 0x79;
/// Tick/tilde key
pub const K_TICK: u8 = 0x29;
/// Minus/underline key
pub const K_MINUS: u8 = 0x0C;
/// Equals/plus key
pub const K_EQUALS: u8 = 0x0D;
/// Backslash/pipe key
pub const K_BACKSLASH: u8 = 0x2B;
/// Bracket open key
pub const K_BRACE_OPEN: u8 = 0x1A;
/// Bracket close key
pub const K_BRACE_CLOSE: u8 = 0x1B;
/// Semicolon key
pub const K_SEMICOLON: u8 = 0x27;
/// Quote key
pub const K_QUOTE: u8 = 0x28;
/// Comma key
pub const K_COMMA: u8 = 0x33;
/// Period key
pub const K_PERIOD: u8 = 0x34;
/// Slash key
pub const K_SLASH: u8 = 0x35;
/// Backspace key
pub const K_BKSP: u8 = 0x0E;
/// Space key
pub const K_SPACE: u8 = 0x39;
/// Tab key
pub const K_TAB: u8 = 0x0F;
/// Capslock
pub const K_CAPS: u8 = 0x3A;
/// Left shift
pub const K_LEFT_SHIFT: u8 = 0x2A;
/// Right shift
pub const K_RIGHT_SHIFT: u8 = 0x36;
/// Control key
pub const K_CTRL: u8 = 0x1D;
/// Alt key
pub const K_ALT: u8 = 0x38;
/// AltGr key
pub const K_ALT_GR: u8 = 0x64;
/// Enter key
pub const K_ENTER: u8 = 0x1C;
/// Escape key
pub const K_ESC: u8 = 0x01;
/// F1 key
pub const K_F1: u8 = 0x3B;
/// F2 key
pub const K_F2: u8 = 0x3C;
/// F3 key
pub const K_F3: u8 = 0x3D;
/// F4 key
pub const K_F4: u8 = 0x3E;
/// F5 key
pub const K_F5: u8 = 0x3F;
/// F6 key
pub const K_F6: u8 = 0x40;
/// F7 key
pub const K_F7: u8 = 0x41;
/// F8 key
pub const K_F8: u8 = 0x42;
/// F9 key
pub const K_F9: u8 = 0x43;
/// F10 key
pub const K_F10: u8 = 0x44;
/// Home key
pub const K_HOME: u8 = 0x47;
/// Up key
pub const K_UP: u8 = 0x48;
/// Page up key
pub const K_PGUP: u8 = 0x49;
/// Left key
pub const K_LEFT: u8 = 0x4B;
/// Right key
pub const K_RIGHT: u8 = 0x4D;
/// End key
pub const K_END: u8 = 0x4F;
/// Down key
pub const K_DOWN: u8 = 0x50;
/// Page down key
pub const K_PGDN: u8 = 0x51;
/// Delete key
pub const K_DEL: u8 = 0x53;
/// F11 key
pub const K_F11: u8 = 0x57;
/// F12 key
pub const K_F12: u8 = 0x58;
/// SUPER/META/WIN Key
pub const K_SUPER : u8 = 0x5B;
/// Media Key for Volume toggle (mute/unmute)
pub const K_VOLUME_TOGGLE : u8 = 0x80 + 0x20;
/// Media Key for Volume Down
pub const K_VOLUME_DOWN : u8 = 0x80 + 0x2E;
/// Media Key for Volume Up
pub const K_VOLUME_UP : u8 = 0x80 + 0x30;
/// A key event (such as a pressed key)
#[derive(Copy, Clone, Debug)]
pub struct KeyEvent {
/// The character of the key
pub character: char,
/// The scancode of the key
pub scancode: u8,
/// Was it pressed?
pub pressed: bool,
}
impl KeyEvent {
/// Convert to an `Event`
pub fn to_event(&self) -> Event {
Event {
code: EVENT_KEY,
a: self.character as i64,
b: self.scancode as i64 | (self.pressed as i64) << 8,
}
}
/// Convert from an `Event`
pub fn from_event(event: Event) -> KeyEvent {
KeyEvent {
character: char::from_u32(event.a as u32).unwrap_or('\0'),
scancode: event.b as u8,
pressed: event.b & 1 << 8 == 1 << 8,
}
}
}
#[derive(Copy, Clone, Debug)]
pub struct TextInputEvent {
pub character: char,
}
impl TextInputEvent {
/// Convert to an `Event`
pub fn to_event(&self) -> Event {
Event {
code: EVENT_TEXT_INPUT,
a: self.character as i64,
b: 0,
}
}
/// Convert from an `Event`
pub fn from_event(event: Event) -> TextInputEvent {
TextInputEvent {
character: char::from_u32(event.a as u32).unwrap_or('\0'),
}
}
}
/// A event related to the mouse (absolute position)
#[derive(Copy, Clone, Debug)]
pub struct MouseEvent {
/// The x coordinate of the mouse
pub x: i32,
/// The y coordinate of the mouse
pub y: i32,
}
impl MouseEvent {
/// Convert to an `Event`
pub fn to_event(&self) -> Event {
Event {
code: EVENT_MOUSE,
a: self.x as i64,
b: self.y as i64,
}
}
/// Convert an `Event` to a `MouseEvent`
pub fn from_event(event: Event) -> MouseEvent {
MouseEvent {
x: event.a as i32,
y: event.b as i32,
}
}
}
/// A event related to the mouse (relative position)
#[derive(Copy, Clone, Debug)]
pub struct MouseRelativeEvent {
/// The x coordinate of the mouse
pub dx: i32,
/// The y coordinate of the mouse
pub dy: i32,
}
impl MouseRelativeEvent {
/// Convert to an `Event`
pub fn to_event(&self) -> Event {
Event {
code: EVENT_MOUSE_RELATIVE,
a: self.dx as i64,
b: self.dy as i64,
}
}
/// Convert an `Event` to a `MouseRelativeEvent`
pub fn from_event(event: Event) -> MouseRelativeEvent {
MouseRelativeEvent {
dx: event.a as i32,
dy: event.b as i32,
}
}
}
/// A event for clicking the mouse
#[derive(Copy, Clone, Debug)]
pub struct ButtonEvent {
/// Was the left button pressed?
pub left: bool,
/// Was the middle button pressed?
pub middle: bool,
/// Was the right button pressed?
pub right: bool,
}
impl ButtonEvent {
/// Convert to an `Event`
pub fn to_event(&self) -> Event {
Event {
code: EVENT_BUTTON,
a: self.left as i64 | (self.middle as i64) << 1 | (self.right as i64) << 2,
b: 0,
}
}
/// Convert an `Event` to a `ButtonEvent`
pub fn from_event(event: Event) -> ButtonEvent {
ButtonEvent {
left: event.a & 1 == 1,
middle: event.a & 2 == 2,
right: event.a & 4 == 4,
}
}
}
/// A event for scrolling the mouse
#[derive(Copy, Clone, Debug)]
pub struct ScrollEvent {
/// The x distance of the scroll
pub x: i32,
/// The y distance of the scroll
pub y: i32,
}
impl ScrollEvent {
/// Convert to an `Event`
pub fn to_event(&self) -> Event {
Event {
code: EVENT_SCROLL,
a: self.x as i64,
b: self.y as i64,
}
}
/// Convert an `Event` to a `ScrollEvent`
pub fn from_event(event: Event) -> ScrollEvent {
ScrollEvent {
x: event.a as i32,
y: event.b as i32,
}
}
}
#[derive(Copy, Clone, Debug)]
pub struct QuitEvent;
impl QuitEvent {
pub fn to_event(&self) -> Event {
Event {
code: EVENT_QUIT,
a: 0,
b: 0,
}
}
pub fn from_event(_: Event) -> QuitEvent {
QuitEvent
}
}
/// A focus event
#[derive(Copy, Clone, Debug)]
pub struct FocusEvent {
/// True if window has been focused, false if not
pub focused: bool,
}
impl FocusEvent {
pub fn to_event(&self) -> Event {
Event {
code: EVENT_FOCUS,
a: self.focused as i64,
b: 0,
}
}
pub fn from_event(event: Event) -> FocusEvent {
FocusEvent {
focused: event.a > 0,
}
}
}
/// A move event
#[derive(Copy, Clone, Debug)]
pub struct MoveEvent {
pub x: i32,
pub y: i32,
}
impl MoveEvent {
pub fn to_event(&self) -> Event {
Event {
code: EVENT_MOVE,
a: self.x as i64,
b: self.y as i64,
}
}
pub fn from_event(event: Event) -> MoveEvent {
MoveEvent {
x: event.a as i32,
y: event.b as i32,
}
}
}
/// A resize event
#[derive(Copy, Clone, Debug)]
pub struct ResizeEvent {
pub width: u32,
pub height: u32,
}
impl ResizeEvent {
pub fn to_event(&self) -> Event {
Event {
code: EVENT_RESIZE,
a: self.width as i64,
b: self.height as i64,
}
}
pub fn from_event(event: Event) -> ResizeEvent {
ResizeEvent {
width: event.a as u32,
height: event.b as u32,
}
}
}
/// A screen report event
#[derive(Copy, Clone, Debug)]
pub struct ScreenEvent {
pub width: u32,
pub height: u32,
}
impl ScreenEvent {
pub fn to_event(&self) -> Event {
Event {
code: EVENT_SCREEN,
a: self.width as i64,
b: self.height as i64,
}
}
pub fn from_event(event: Event) -> ScreenEvent {
ScreenEvent {
width: event.a as u32,
height: event.b as u32,
}
}
}
pub const CLIPBOARD_COPY: u8 = 0;
pub const CLIPBOARD_CUT: u8 = 1;
pub const CLIPBOARD_PASTE: u8 = 2;
/// A clipboard event
#[derive(Copy, Clone, Debug)]
pub struct ClipboardUpdateEvent;
impl ClipboardUpdateEvent {
pub fn to_event(&self) -> Event {
Event {
code: EVENT_CLIPBOARD_UPDATE,
a: 0,
b: 0,
}
}
pub fn from_event(_: Event) -> ClipboardUpdateEvent {
ClipboardUpdateEvent
}
}
/// A clipboard event
#[derive(Copy, Clone, Debug)]
pub struct ClipboardEvent {
pub kind: u8,
pub size: usize,
}
impl ClipboardEvent {
pub fn to_event(&self) -> Event {
Event {
code: EVENT_CLIPBOARD,
a: self.kind as i64,
b: self.size as i64,
}
}
pub fn from_event(event: Event) -> ClipboardEvent {
ClipboardEvent {
kind: event.a as u8,
size: event.b as usize,
}
}
}
pub const DROP_FILE: u8 = 0;
pub const DROP_TEXT: u8 = 1;
/// A drop file event.
#[derive(Copy, Clone, Debug)]
pub struct DropEvent {
pub kind: u8,
}
impl DropEvent {
pub fn to_event(&self) -> Event {
Event {
code: EVENT_DROP,
a: self.kind as i64,
b: 0,
}
}
pub fn from_event(event: Event) -> DropEvent {
DropEvent {
kind: event.a as u8,
}
}
}
/// A hover event
#[derive(Copy, Clone, Debug)]
pub struct HoverEvent {
/// True if window has been entered, false if exited
pub entered: bool,
}
impl HoverEvent {
pub fn to_event(&self) -> Event {
Event {
code: EVENT_HOVER,
a: self.entered as i64,
b: 0,
}
}
pub fn from_event(event: Event) -> HoverEvent {
HoverEvent {
entered: event.a > 0,
}
}
}

120
vendor/orbclient/src/graphicspath.rs vendored Normal file
View File

@@ -0,0 +1,120 @@
// SPDX-License-Identifier: MIT
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
/// point type (is the point a new position or a connection point)
pub enum PointType {
Move,
Connect,
None,
}
/// graphic path with similar functions like html canvas
pub struct GraphicsPath {
x: i32,
y: i32,
pub points: Vec<(i32, i32, PointType)>,
}
#[allow(clippy::new_without_default)]
impl GraphicsPath {
pub fn new() -> GraphicsPath {
GraphicsPath {
x: 0,
y: 0,
points: Vec::new(),
}
}
/// move to position
pub fn move_to(&mut self, x: i32, y: i32) {
self.points.push((x, y, PointType::Move));
self.x = x;
self.y = y;
}
/// create a line between the last and new point
pub fn line_to(&mut self, x: i32, y: i32) {
self.points.push((x, y, PointType::Connect));
self.x = x;
self.y = y;
}
/// quadratic bezier curve
pub fn quadratic_curve_to(&mut self, argx1: i32, argy1: i32, argx2: i32, argy2: i32) {
let mut t: f32 = 0.0;
let mut u: f32;
let mut tt: f32;
let mut uu: f32;
let mut x: f32;
let mut y: f32;
while t < 1.0 {
u = 1.0 - t;
uu = u * u;
tt = t * t;
x = (self.x as f32) * uu;
y = (self.y as f32) * uu;
x += 2.0 * u * t * (argx1 as f32);
y += 2.0 * u * t * (argy1 as f32);
x += tt * (argx2 as f32);
y += tt * (argy2 as f32);
t += 0.01;
self.points.push((x as i32, y as i32, PointType::Connect));
}
self.x = argx2;
self.y = argy2;
}
/// cubic bezier curve
pub fn bezier_curve_to(
&mut self,
argx1: i32,
argy1: i32,
argx2: i32,
argy2: i32,
argx3: i32,
argy3: i32,
) {
let mut t: f32 = 0.0;
let mut u: f32;
let mut tt: f32;
let mut uu: f32;
let mut uuu: f32;
let mut ttt: f32;
let mut x: f32;
let mut y: f32;
while t < 1.0 {
u = 1.0 - t;
tt = t * t;
uu = u * u;
uuu = uu * u;
ttt = tt * t;
x = (self.x as f32) * uuu;
y = (self.y as f32) * uuu;
x += 3.0 * uu * t * (argx1 as f32);
y += 3.0 * uu * t * (argy1 as f32);
x += 3.0 * u * tt * (argx2 as f32);
y += 3.0 * u * tt * (argy2 as f32);
x += ttt * (argx3 as f32);
y += ttt * (argy3 as f32);
t += 0.01;
self.points.push((x as i32, y as i32, PointType::Connect));
}
self.x = argx3;
self.y = argy3;
}
}

49
vendor/orbclient/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,49 @@
// SPDX-License-Identifier: MIT
#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(not(feature = "std"))]
extern crate alloc;
#[cfg(all(feature = "std", not(target_os = "redox")))]
#[path = "sys/sdl2.rs"]
mod sys;
#[cfg(all(feature = "std", target_os = "redox"))]
#[path = "sys/orbital.rs"]
mod sys;
#[cfg(feature = "std")]
pub use sys::{get_display_size, EventIter, Window};
#[cfg(feature = "unifont")]
pub static FONT: &[u8] = include_bytes!("../res/unifont.font");
pub use color::Color;
pub use event::*;
pub use graphicspath::GraphicsPath;
pub use renderer::Renderer;
#[cfg(feature = "std")]
mod blur;
pub mod color;
pub mod event;
pub mod graphicspath;
pub mod renderer;
#[derive(Clone, Copy, Debug)]
pub enum WindowFlag {
Async,
Back,
Front,
Borderless,
Resizable,
Transparent,
Unclosable,
}
#[derive(Clone, Copy, Debug)]
pub enum Mode {
Blend, //Composite
Overwrite, //Replace
}

886
vendor/orbclient/src/renderer.rs vendored Normal file
View File

@@ -0,0 +1,886 @@
// SPDX-License-Identifier: MIT
use core::cell::Cell;
use core::cmp;
#[cfg(feature = "std")]
use crate::blur;
use crate::color::Color;
use crate::graphicspath::GraphicsPath;
use crate::graphicspath::PointType;
use crate::Mode;
pub trait Renderer {
/// Get width
fn width(&self) -> u32;
/// Get height
fn height(&self) -> u32;
/// Access the pixel buffer
fn data(&self) -> &[Color];
/// Access the pixel buffer mutably
fn data_mut(&mut self) -> &mut [Color];
/// Flip the buffer
fn sync(&mut self) -> bool;
/// Set/get drawing mode
fn mode(&self) -> &Cell<Mode>;
///Draw a pixel
//faster pixel implementation (multiplexing)
fn pixel(&mut self, x: i32, y: i32, color: Color) {
let replace = match self.mode().get() {
Mode::Blend => false,
Mode::Overwrite => true,
};
let w = self.width();
let h = self.height();
let data = self.data_mut();
if x >= 0 && y >= 0 && x < w as i32 && y < h as i32 {
let new = color.data;
let alpha = (new >> 24) & 0xFF;
let old = &mut data[y as usize * w as usize + x as usize].data;
if alpha >= 255 || replace {
*old = new;
} else if alpha > 0 {
let n_alpha = 255 - alpha;
let rb = ((n_alpha * (*old & 0x00FF00FF)) + (alpha * (new & 0x00FF00FF))) >> 8;
let ag = (n_alpha * ((*old & 0xFF00FF00) >> 8))
+ (alpha * (0x01000000 | ((new & 0x0000FF00) >> 8)));
*old = (rb & 0x00FF00FF) | (ag & 0xFF00FF00);
}
}
}
/// Draw a piece of an arc. Negative radius will fill in the inside
fn arc(&mut self, x0: i32, y0: i32, radius: i32, parts: u8, color: Color) {
let mut x = radius.abs();
let mut y = 0;
let mut err = 0;
// https://github.com/rust-lang/rust-clippy/issues/5354
#[allow(clippy::comparison_chain)]
while x >= y {
if radius < 0 {
if parts & 1 << 0 != 0 {
self.rect(x0 - x, y0 + y, x as u32, 1, color);
}
if parts & 1 << 1 != 0 {
self.rect(x0, y0 + y, x as u32 + 1, 1, color);
}
if parts & 1 << 2 != 0 {
self.rect(x0 - y, y0 + x, y as u32, 1, color);
}
if parts & 1 << 3 != 0 {
self.rect(x0, y0 + x, y as u32 + 1, 1, color);
}
if parts & 1 << 4 != 0 {
self.rect(x0 - x, y0 - y, x as u32, 1, color);
}
if parts & 1 << 5 != 0 {
self.rect(x0, y0 - y, x as u32 + 1, 1, color);
}
if parts & 1 << 6 != 0 {
self.rect(x0 - y, y0 - x, y as u32, 1, color);
}
if parts & 1 << 7 != 0 {
self.rect(x0, y0 - x, y as u32 + 1, 1, color);
}
} else if radius == 0 {
self.pixel(x0, y0, color);
} else {
if parts & 1 << 0 != 0 {
self.pixel(x0 - x, y0 + y, color);
}
if parts & 1 << 1 != 0 {
self.pixel(x0 + x, y0 + y, color);
}
if parts & 1 << 2 != 0 {
self.pixel(x0 - y, y0 + x, color);
}
if parts & 1 << 3 != 0 {
self.pixel(x0 + y, y0 + x, color);
}
if parts & 1 << 4 != 0 {
self.pixel(x0 - x, y0 - y, color);
}
if parts & 1 << 5 != 0 {
self.pixel(x0 + x, y0 - y, color);
}
if parts & 1 << 6 != 0 {
self.pixel(x0 - y, y0 - x, color);
}
if parts & 1 << 7 != 0 {
self.pixel(x0 + y, y0 - x, color);
}
}
y += 1;
err += 1 + 2 * y;
if 2 * (err - x) + 1 > 0 {
x -= 1;
err += 1 - 2 * x;
}
}
}
/// Draw a circle. Negative radius will fill in the inside
fn circle(&mut self, x0: i32, y0: i32, radius: i32, color: Color) {
let mut x = radius.abs();
let mut y = 0;
let mut err = -radius.abs();
match radius {
radius if radius > 0 => {
err = 0;
while x >= y {
self.pixel(x0 - x, y0 + y, color);
self.pixel(x0 + x, y0 + y, color);
self.pixel(x0 - y, y0 + x, color);
self.pixel(x0 + y, y0 + x, color);
self.pixel(x0 - x, y0 - y, color);
self.pixel(x0 + x, y0 - y, color);
self.pixel(x0 - y, y0 - x, color);
self.pixel(x0 + y, y0 - x, color);
y += 1;
err += 1 + 2 * y;
if 2 * (err - x) + 1 > 0 {
x -= 1;
err += 1 - 2 * x;
}
}
}
radius if radius < 0 => {
while x >= y {
let lasty = y;
err += y;
y += 1;
err += y;
self.line4points(x0, y0, x, lasty, color);
if err >= 0 {
if x != lasty {
self.line4points(x0, y0, lasty, x, color);
}
err -= x;
x -= 1;
err -= x;
}
}
}
_ => {
self.pixel(x0, y0, color);
}
}
}
fn line4points(&mut self, x0: i32, y0: i32, x: i32, y: i32, color: Color) {
//self.line(x0 - x, y0 + y, (x+x0), y0 + y, color);
self.rect(x0 - x, y0 + y, x as u32 * 2 + 1, 1, color);
if y != 0 {
//self.line(x0 - x, y0 - y, (x+x0), y0-y , color);
self.rect(x0 - x, y0 - y, x as u32 * 2 + 1, 1, color);
}
}
/// Draw a line
fn line(&mut self, argx1: i32, argy1: i32, argx2: i32, argy2: i32, color: Color) {
let mut x = argx1;
let mut y = argy1;
let dx = if argx1 > argx2 {
argx1 - argx2
} else {
argx2 - argx1
};
let dy = if argy1 > argy2 {
argy1 - argy2
} else {
argy2 - argy1
};
let sx = if argx1 < argx2 { 1 } else { -1 };
let sy = if argy1 < argy2 { 1 } else { -1 };
let mut err = if dx > dy { dx } else { -dy } / 2;
let mut err_tolerance;
loop {
self.pixel(x, y, color);
if x == argx2 && y == argy2 {
break;
};
err_tolerance = 2 * err;
if err_tolerance > -dx {
err -= dy;
x += sx;
}
if err_tolerance < dy {
err += dx;
y += sy;
}
}
}
fn lines(&mut self, points: &[[i32; 2]], color: Color) {
if points.is_empty() {
// when no points given, do nothing
} else if points.len() == 1 {
self.pixel(points[0][0], points[0][1], color);
} else {
for i in 0..points.len() - 1 {
self.line(
points[i][0],
points[i][1],
points[i + 1][0],
points[i + 1][1],
color,
);
}
}
}
/// Draw a path (GraphicsPath)
fn draw_path_stroke(&mut self, graphicspath: GraphicsPath, color: Color) {
let mut x: i32 = 0;
let mut y: i32 = 0;
for point in graphicspath.points {
if let PointType::Connect = point.2 {
self.line(x, y, point.0, point.1, color)
}
x = point.0;
y = point.1;
}
}
/// Draw a character, using the loaded font
#[cfg(feature = "unifont")]
fn char(&mut self, x: i32, y: i32, c: char, color: Color) {
let mut offset = (c as usize) * 16;
for row in 0..16 {
let row_data = if offset < crate::FONT.len() {
crate::FONT[offset]
} else {
0
};
for col in 0..8 {
let pixel = (row_data >> (7 - col)) & 1;
if pixel > 0 {
self.pixel(x + col, y + row, color);
}
}
offset += 1;
}
}
/// Set entire window to a color
fn set(&mut self, color: Color) {
let data = self.data_mut();
let data_ptr = data.as_mut_ptr();
for i in 0..data.len() as isize {
unsafe { *data_ptr.offset(i) = color }
}
}
/// Sets the whole window to black
fn clear(&mut self) {
self.set(Color::rgb(0, 0, 0));
}
fn rect(&mut self, x: i32, y: i32, w: u32, h: u32, color: Color) {
let replace = match self.mode().get() {
Mode::Blend => false,
Mode::Overwrite => true,
};
let self_w = self.width();
let self_h = self.height();
let start_y = cmp::max(0, cmp::min(self_h as i32 - 1, y));
let end_y = cmp::max(start_y, cmp::min(self_h as i32, y + h as i32));
let start_x = cmp::max(0, cmp::min(self_w as i32 - 1, x));
let len = cmp::max(start_x, cmp::min(self_w as i32, x + w as i32)) - start_x;
let alpha = (color.data >> 24) & 0xFF;
//if alpha > 0 {
if alpha >= 255 || replace {
let data = self.data_mut();
let data_ptr = data.as_mut_ptr();
for y in start_y..end_y {
let start = (y * self_w as i32 + start_x) as isize;
let end = start + len as isize;
for i in start..end {
unsafe {
*data_ptr.offset(i) = color;
}
}
}
} else {
for y in start_y..end_y {
for x in start_x..start_x + len {
self.pixel(x, y, color);
}
}
}
//}
}
#[cfg(feature = "std")]
fn box_blur(&mut self, x: i32, y: i32, w: u32, h: u32, r: i32) {
let self_w = self.width();
let self_h = self.height();
let start_y = cmp::max(0, cmp::min(self_h as i32 - 1, y));
let end_y = cmp::max(start_y, cmp::min(self_h as i32, y + h as i32));
let start_x = cmp::max(0, cmp::min(self_w as i32 - 1, x));
let end_x = cmp::max(start_x, cmp::min(self_w as i32, x + w as i32));
let data = self.data_mut();
let mut blur_data: Vec<Color> = Vec::new();
for y in start_y..end_y {
for x in start_x..end_x {
let old = data[y as usize * self_w as usize + x as usize];
blur_data.push(old);
}
}
let real_w = end_x - start_x;
let real_h = end_y - start_y;
blur::gauss_blur(&mut blur_data, real_w as u32, real_h as u32, r as f32);
let mut counter: u32 = 0;
for y in start_y..end_y {
for x in start_x..end_x {
let a = blur_data[counter as usize];
let old = &mut data[y as usize * self_w as usize + x as usize].data;
*old = a.data;
counter += 1;
}
}
}
#[allow(clippy::too_many_arguments)]
#[cfg(feature = "std")]
fn box_shadow(
&mut self,
x: i32,
y: i32,
w: u32,
h: u32,
offset_x: i32,
offset_y: i32,
r: i32,
color: Color,
) {
let real_w = w + (2 * r as u32);
let real_h = h + (2 * r as u32);
let mut blur_data: Vec<Color> = Vec::new();
for new_x in x..x + real_w as i32 {
for new_y in y..y + real_h as i32 {
if new_x < x + r
|| new_y < y + r
|| new_y >= y + h as i32 + r
|| new_x >= x + w as i32 + r
{
blur_data.push(Color::rgb(255, 0, 255));
} else {
blur_data.push(Color::rgb(0, 0, 0));
}
}
}
blur::gauss_blur(&mut blur_data, real_w, real_h, r as f32 / 3.0);
let mut counter: u32 = 0;
for new_x in (x - r)..(x + real_w as i32 - r) {
for new_y in (y - r)..(y + real_h as i32 - r) {
let c = blur_data[counter as usize];
let alpha: u8 = if color.a() < 255 - c.r() {
color.a()
} else {
255 - c.r()
};
let col = Color::rgba(color.r(), color.g(), color.b(), alpha);
let new_x_b = new_x + offset_x;
let new_y_b = new_y + offset_y;
if new_x_b < x
|| new_x_b > x + w as i32 - 1
|| new_y_b < y
|| new_y_b > y + h as i32 - 1
{
self.pixel(new_x_b, new_y_b, col);
}
counter += 1;
}
}
}
/// Display an image
fn image(&mut self, start_x: i32, start_y: i32, w: u32, h: u32, data: &[Color]) {
match self.mode().get() {
Mode::Blend => self.image_fast(start_x, start_y, w, h, data),
Mode::Overwrite => self.image_opaque(start_x, start_y, w, h, data),
}
}
// TODO: Improve speed
#[inline(always)]
fn image_legacy(&mut self, start_x: i32, start_y: i32, w: u32, h: u32, data: &[Color]) {
let mut i = 0;
for y in start_y..start_y + h as i32 {
for x in start_x..start_x + w as i32 {
if i < data.len() {
self.pixel(x, y, data[i])
}
i += 1;
}
}
}
///Display an image overwriting a portion of window starting at given line : very quick!!
fn image_over(&mut self, start: i32, image_data: &[Color]) {
let start = start as usize * self.width() as usize;
let window_data = self.data_mut();
let stop = cmp::min(start + image_data.len(), window_data.len());
let end = cmp::min(image_data.len(), window_data.len() - start);
window_data[start..stop].copy_from_slice(&image_data[..end]);
}
///Display an image using non transparent method
#[inline(always)]
fn image_opaque(&mut self, start_x: i32, start_y: i32, w: u32, h: u32, image_data: &[Color]) {
let w = w as usize;
let mut h = h as usize;
let width = self.width() as usize;
let height = self.height() as usize;
let start_x = start_x as usize;
let start_y = start_y as usize;
//check boundaries
if start_x >= width || start_y >= height {
return;
}
if h + start_y > height {
h = height - start_y;
}
let window_data = self.data_mut();
let offset = start_y * width + start_x;
//copy image slices to window line by line
for l in 0..h {
let start = offset + l * width;
let mut stop = start + w;
let begin = l * w;
let mut end = begin + w;
//check boundaries
if start_x + w > width {
stop = (start_y + l + 1) * width - 1;
end = begin + stop - start;
}
window_data[start..stop].copy_from_slice(&image_data[begin..end]);
}
}
// Speed improved, image can be outside of window boundary
#[inline(always)]
fn image_fast(&mut self, start_x: i32, start_y: i32, w: u32, h: u32, image_data: &[Color]) {
let w = w as usize;
let h = h as usize;
let width = self.width() as usize;
let start_x = start_x as usize;
let start_y = start_y as usize;
//simply return if image is outside of window
if start_x >= width || start_y >= self.height() as usize {
return;
}
let window_data = self.data_mut();
let offset = start_y * width + start_x;
//copy image slices to window line by line
for l in 0..h {
let start = offset + l * width;
let mut stop = start + w;
let begin = l * w;
let end = begin + w;
//check boundaries
if start_x + w > width {
stop = (start_y + l + 1) * width;
}
let mut k = 0;
for i in begin..end {
if i < image_data.len() {
let new = image_data[i].data;
let alpha = (new >> 24) & 0xFF;
if alpha > 0 && (start + k) < window_data.len() && (start + k) < stop {
let old = &mut window_data[start + k].data;
if alpha >= 255 {
*old = new;
} else {
let n_alpha = 255 - alpha;
let rb = ((n_alpha * (*old & 0x00FF00FF))
+ (alpha * (new & 0x00FF00FF)))
>> 8;
let ag = (n_alpha * ((*old & 0xFF00FF00) >> 8))
+ (alpha * (0x01000000 | ((new & 0x0000FF00) >> 8)));
*old = (rb & 0x00FF00FF) | (ag & 0xFF00FF00);
}
}
k += 1;
}
}
}
}
/// Draw a linear gradient in a rectangular region
#[allow(clippy::too_many_arguments)]
#[cfg(feature = "std")]
fn linear_gradient(
&mut self,
rect_x: i32,
rect_y: i32,
rect_width: u32,
rect_height: u32,
start_x: i32,
start_y: i32,
end_x: i32,
end_y: i32,
start_color: Color,
end_color: Color,
) {
if (start_x == end_x) && (start_y == end_y) {
// Degenerate gradient
self.rect(rect_x, rect_y, rect_width, rect_height, start_color);
} else if start_x == end_x {
// Vertical gradient
for y in rect_y..(rect_y + rect_height as i32) {
let proj = (y as f64 - start_y as f64) / (end_y as f64 - start_y as f64);
let scale = if proj < 0.0 {
0.0
} else if proj > 1.0 {
1.0
} else {
proj
};
let color = Color::interpolate(start_color, end_color, scale);
self.line(rect_x, y, rect_x + rect_width as i32 - 1, y, color);
}
} else if start_y == end_y {
// Horizontal gradient
for x in rect_x..(rect_x + rect_width as i32) {
let proj = (x as f64 - start_x as f64) / (end_x as f64 - start_x as f64);
let scale = if proj < 0.0 {
0.0
} else if proj > 1.0 {
1.0
} else {
proj
};
let color = Color::interpolate(start_color, end_color, scale);
self.line(x, rect_y, x, rect_y + rect_height as i32 - 1, color);
}
} else {
// Non axis-aligned gradient
// Gradient vector
let grad_x = end_x as f64 - start_x as f64;
let grad_y = end_y as f64 - start_y as f64;
let grad_len = grad_x * grad_x + grad_y * grad_y;
for y in rect_y..(rect_y + rect_height as i32) {
for x in rect_x..(rect_x + rect_width as i32) {
// Pixel vector
let pix_x = x as f64 - start_x as f64;
let pix_y = y as f64 - start_y as f64;
// Scalar projection
let proj = (pix_x * grad_x + pix_y * grad_y) / grad_len;
// Saturation
let scale = if proj < 0.0 {
0.0
} else if proj > 1.0 {
1.0
} else {
proj
};
// Interpolation
let color = Color::interpolate(start_color, end_color, scale);
self.pixel(x, y, color);
}
}
}
}
/// Draw a rect with rounded corners
#[allow(clippy::too_many_arguments)]
fn rounded_rect(
&mut self,
x: i32,
y: i32,
w: u32,
h: u32,
radius: u32,
filled: bool,
color: Color,
) {
let w = w as i32;
let h = h as i32;
let r = radius as i32;
if filled {
//Draw inside corners
self.arc(x + r, y + r, -r, 1 << 4 | 1 << 6, color);
self.arc(x + w - 1 - r, y + r, -r, 1 << 5 | 1 << 7, color);
self.arc(x + r, y + h - 1 - r, -r, 1 << 0 | 1 << 2, color);
self.arc(x + w - 1 - r, y + h - 1 - r, -r, 1 << 1 | 1 << 3, color);
// Draw inside rectangles
self.rect(x + r, y, (w - 1 - r * 2) as u32, r as u32 + 1, color);
self.rect(
x + r,
y + h - 1 - r,
(w - 1 - r * 2) as u32,
r as u32 + 1,
color,
);
self.rect(x, y + r + 1, w as u32, (h - 2 - r * 2) as u32, color);
} else {
//Draw outside corners
self.arc(x + r, y + r, r, 1 << 4 | 1 << 6, color);
self.arc(x + w - 1 - r, y + r, r, 1 << 5 | 1 << 7, color);
self.arc(x + r, y + h - 1 - r, r, 1 << 0 | 1 << 2, color);
self.arc(x + w - 1 - r, y + h - 1 - r, r, 1 << 1 | 1 << 3, color);
// Draw outside rectangles
self.rect(x + r + 1, y, (w - 2 - r * 2) as u32, 1, color);
self.rect(x + r + 1, y + h - 1, (w - 2 - r * 2) as u32, 1, color);
self.rect(x, y + r + 1, 1, (h - 2 - r * 2) as u32, color);
self.rect(x + w - 1, y + r + 1, 1, (h - 2 - r * 2) as u32, color);
}
}
/// Draws antialiased line
#[cfg(feature = "std")]
fn wu_line(&mut self, x0: i32, y0: i32, x1: i32, y1: i32, color: Color) {
//adapted from https://rosettacode.org/wiki/Xiaolin_Wu's_line_algorithm#C.23
let mut x0 = x0 as f64;
let mut y0 = y0 as f64;
let mut x1 = x1 as f64;
let mut y1 = y1 as f64;
let r = color.r();
let g = color.g();
let b = color.b();
let a = color.a() as f64;
fn ipart(x: f64) -> i32 {
x.trunc() as i32
}
fn fpart(x: f64) -> f64 {
if x < 0.0 {
return 1.0 - (x - x.floor());
}
x - x.floor()
}
fn rfpart(x: f64) -> f64 {
1.0 - fpart(x)
}
fn chkalpha(mut alpha: f64) -> u8 {
if alpha > 255.0 {
alpha = 255.0
};
if alpha < 0.0 {
alpha = 0.0
};
alpha as u8
}
let steep: bool = (y1 - y0).abs() > (x1 - x0).abs();
let mut temp;
if steep {
temp = x0;
x0 = y0;
y0 = temp;
temp = x1;
x1 = y1;
y1 = temp;
}
if x0 > x1 {
temp = x0;
x0 = x1;
x1 = temp;
temp = y0;
y0 = y1;
y1 = temp;
}
let dx = x1 - x0;
let dy = y1 - y0;
let gradient = dy / dx;
let mut xend: f64 = x0.round();
let mut yend: f64 = y0 + gradient * (xend - x0);
let mut xgap: f64 = rfpart(x0 + 0.5);
let xpixel1 = xend as i32;
let ypixel1 = ipart(yend);
if steep {
self.pixel(
ypixel1,
xpixel1,
Color::rgba(r, g, b, chkalpha(rfpart(yend) * xgap * a)),
);
self.pixel(
ypixel1 + 1,
xpixel1,
Color::rgba(r, g, b, chkalpha(fpart(yend) * xgap * a)),
);
} else {
self.pixel(
xpixel1,
ypixel1,
Color::rgba(r, g, b, chkalpha(rfpart(yend) * xgap * a)),
);
self.pixel(
xpixel1 + 1,
ypixel1,
Color::rgba(r, g, b, chkalpha(fpart(yend) * xgap * a)),
);
}
let mut intery: f64 = yend + gradient;
xend = x1.round();
yend = y1 + gradient * (xend - x1);
xgap = fpart(x1 + 0.5);
let xpixel2 = xend as i32;
let ypixel2 = ipart(yend) ;
if steep {
self.pixel(
ypixel2,
xpixel2,
Color::rgba(r, g, b, chkalpha(rfpart(yend) * xgap * a)),
);
self.pixel(
ypixel2 + 1,
xpixel2,
Color::rgba(r, g, b, chkalpha(fpart(yend) * xgap * a)),
);
} else {
self.pixel(
xpixel2,
ypixel2,
Color::rgba(r, g, b, chkalpha(rfpart(yend) * xgap * a)),
);
self.pixel(
xpixel2 + 1,
ypixel2,
Color::rgba(r, g, b, chkalpha(fpart(yend) * xgap * a)),
);
}
if steep {
for x in (xpixel1 + 1)..(xpixel2) {
self.pixel(
ipart(intery),
x,
Color::rgba(r, g, b, chkalpha(a * rfpart(intery))),
);
self.pixel(
ipart(intery) + 1,
x,
Color::rgba(r, g, b, chkalpha(a * fpart(intery))),
);
intery += gradient;
}
} else {
for x in (xpixel1 + 1)..(xpixel2) {
self.pixel(
x,
ipart(intery),
Color::rgba(r, g, b, chkalpha(a * rfpart(intery))),
);
self.pixel(
x,
ipart(intery) + 1,
Color::rgba(r, g, b, chkalpha(a * fpart(intery))),
);
intery += gradient;
}
}
}
///Draws antialiased circle
#[cfg(feature = "std")]
fn wu_circle(&mut self, x0: i32, y0: i32, radius: i32, color: Color) {
let r = color.r();
let g = color.g();
let b = color.b();
let a = color.a();
let mut y = 0;
let mut x = radius;
let mut d = 0_f64;
self.pixel(x0 + x, y0 + y, color);
self.pixel(x0 - x, y0 - y, color);
self.pixel(x0 + y, y0 - x, color);
self.pixel(x0 - y, y0 + x, color);
while x > y {
let di = dist(radius, y);
if di < d {
x -= 1;
}
let col = Color::rgba(r, g, b, (a as f64 * (1.0 - di)) as u8);
let col2 = Color::rgba(r, g, b, (a as f64 * di) as u8);
self.pixel(x0 + x, y0 + y, col);
self.pixel(x0 + x - 1, y0 + y, col2); //-
self.pixel(x0 - x, y0 + y, col);
self.pixel(x0 - x + 1, y0 + y, col2); //+
self.pixel(x0 + x, y0 - y, col);
self.pixel(x0 + x - 1, y0 - y, col2); //-
self.pixel(x0 - x, y0 - y, col);
self.pixel(x0 - x + 1, y0 - y, col2); //+
self.pixel(x0 + y, y0 + x, col);
self.pixel(x0 + y, y0 + x - 1, col2);
self.pixel(x0 - y, y0 + x, col);
self.pixel(x0 - y, y0 + x - 1, col2);
self.pixel(x0 + y, y0 - x, col);
self.pixel(x0 + y, y0 - x + 1, col2);
self.pixel(x0 - y, y0 - x, col);
self.pixel(x0 - y, y0 - x + 1, col2);
d = di;
y += 1;
}
fn dist(r: i32, y: i32) -> f64 {
let x: f64 = ((r * r - y * y) as f64).sqrt();
x.ceil() - x
}
}
///Gets pixel color at x,y position
fn getpixel(&self, x: i32, y: i32) -> Color {
let p = (self.width() as i32 * y + x) as usize;
if p >= self.data().len() {
return Color::rgba(0, 0, 0, 0);
}
self.data()[p]
}
}

440
vendor/orbclient/src/sys/orbital.rs vendored Normal file
View File

@@ -0,0 +1,440 @@
// SPDX-License-Identifier: MIT
use std::cell::Cell;
use std::fs::File;
use std::io::{Read, Write};
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
use std::{env, mem, slice, thread};
use libredox::{call as redox, flag};
use crate::color::Color;
use crate::event::{Event, EVENT_RESIZE};
use crate::renderer::Renderer;
use crate::Mode;
use crate::WindowFlag;
pub fn get_display_size() -> Result<(u32, u32), String> {
let display_path = env::var("DISPLAY").or(Err("DISPLAY not set"))?;
match File::open(&display_path) {
Ok(display) => {
let mut buf: [u8; 4096] = [0; 4096];
let count = redox::fpath(display.as_raw_fd() as usize, &mut buf)
.map_err(|err| format!("{}", err))?;
let path = unsafe { String::from_utf8_unchecked(Vec::from(&buf[..count])) };
let res = path.split(":").nth(1).unwrap_or("");
let width = res
.split("/")
.nth(1)
.unwrap_or("")
.parse::<u32>()
.unwrap_or(0);
let height = res
.split("/")
.nth(2)
.unwrap_or("")
.parse::<u32>()
.unwrap_or(0);
Ok((width, height))
}
Err(err) => Err(format!("{}", err)),
}
}
/// A window
pub struct Window {
/// The x coordinate of the window
x: i32,
/// The y coordinate of the window
y: i32,
/// The width of the window
w: u32,
/// The height of the window
h: u32,
/// The title of the window
t: String,
/// True if the window should not wait for events
window_async: bool,
/// True if the window can be resized
resizable: bool,
/// Drawing mode
mode: Cell<Mode>,
/// The input scheme
file_opt: Option<File>,
/// Window data
data_opt: Option<&'static mut [Color]>,
}
impl Renderer for Window {
/// Get width
fn width(&self) -> u32 {
self.w
}
/// Get height
fn height(&self) -> u32 {
self.h
}
/// Access pixel buffer
fn data(&self) -> &[Color] {
self.data_opt.as_ref().unwrap()
}
/// Access pixel buffer mutably
fn data_mut(&mut self) -> &mut [Color] {
self.data_opt.as_mut().unwrap()
}
/// Flip the buffer
fn sync(&mut self) -> bool {
self.file_mut().sync_data().is_ok()
}
/// Set/get mode
fn mode(&self) -> &Cell<Mode> {
&self.mode
}
}
impl Window {
/// Create a new window
pub fn new(x: i32, y: i32, w: u32, h: u32, title: &str) -> Option<Self> {
Window::new_flags(x, y, w, h, title, &[])
}
/// Create a new window with flags
pub fn new_flags(
x: i32,
y: i32,
w: u32,
h: u32,
title: &str,
flags: &[WindowFlag],
) -> Option<Self> {
let mut flag_str = String::new();
let mut window_async = false;
let mut resizable = false;
for &flag in flags.iter() {
match flag {
WindowFlag::Async => {
window_async = true;
flag_str.push('a');
}
WindowFlag::Back => flag_str.push('b'),
WindowFlag::Front => flag_str.push('f'),
WindowFlag::Borderless => flag_str.push('l'),
WindowFlag::Resizable => {
resizable = true;
flag_str.push('r');
}
WindowFlag::Transparent => flag_str.push('t'),
WindowFlag::Unclosable => flag_str.push('u'),
}
}
if let Ok(file) = File::open(&format!(
"orbital:{}/{}/{}/{}/{}/{}",
flag_str, x, y, w, h, title
)) {
let mut window = Window {
x,
y,
w,
h,
t: title.to_string(),
window_async,
resizable,
mode: Cell::new(Mode::Blend),
file_opt: Some(file),
data_opt: None,
};
unsafe { window.remap(); }
Some(window)
} else {
None
}
}
pub fn clipboard(&self) -> String {
let mut text = String::new();
let window_fd = self.file().as_raw_fd();
if let Ok(clipboard_fd) = redox::dup(window_fd as usize, b"clipboard") {
let mut clipboard_file = unsafe { File::from_raw_fd(clipboard_fd as RawFd) };
let _ = clipboard_file.read_to_string(&mut text);
}
text
}
pub fn set_clipboard(&mut self, text: &str) {
let window_fd = self.file().as_raw_fd();
if let Ok(clipboard_fd) = redox::dup(window_fd as usize, b"clipboard") {
let mut clipboard_file = unsafe { File::from_raw_fd(clipboard_fd as RawFd) };
let _ = clipboard_file.write(text.as_bytes());
}
}
/// Not yet available on Redox OS.
pub fn pop_drop_content(&self) -> Option<String> {
None
}
// TODO: Replace with smarter mechanism, maybe a move event?
pub fn sync_path(&mut self) {
let mut buf: [u8; 4096] = [0; 4096];
if let Ok(count) = redox::fpath(self.file().as_raw_fd() as usize, &mut buf) {
let path = unsafe { String::from_utf8_unchecked(Vec::from(&buf[..count])) };
// orbital:/x/y/w/h/t
let mut parts = path.split('/');
if let Some(flags) = parts.next() {
self.window_async = flags.contains('a');
self.resizable = flags.contains('r');
}
if let Some(x) = parts.next() {
self.x = x.parse::<i32>().unwrap_or(0);
}
if let Some(y) = parts.next() {
self.y = y.parse::<i32>().unwrap_or(0);
}
if let Some(w) = parts.next() {
self.w = w.parse::<u32>().unwrap_or(0);
}
if let Some(h) = parts.next() {
self.h = h.parse::<u32>().unwrap_or(0);
}
if let Some(t) = parts.next() {
self.t = t.to_string();
}
}
}
/// Get x
// TODO: Sync with window movements
pub fn x(&self) -> i32 {
self.x
}
/// Get y
// TODO: Sync with window movements
pub fn y(&self) -> i32 {
self.y
}
/// Get title
pub fn title(&self) -> String {
self.t.clone()
}
/// Get async
pub fn is_async(&self) -> bool {
self.window_async
}
/// Set async
pub fn set_async(&mut self, is_async: bool) {
self.window_async = is_async;
let _ = self.file_mut().write(if is_async { b"A,1" } else { b"A,0" });
}
/// Set cursor visibility
pub fn set_mouse_cursor(&mut self, visible: bool) {
let _ = self.file_mut().write(if visible { b"M,C,1" } else { b"M,C,0" });
}
/// Set mouse grabbing
pub fn set_mouse_grab(&mut self, grab: bool) {
let _ = self.file_mut().write(if grab { b"M,G,1" } else { b"M,G,0" });
}
/// Set mouse relative mode
pub fn set_mouse_relative(&mut self, relative: bool) {
let _ = self.file_mut().write(if relative { b"M,R,1" } else { b"M,R,0" });
}
/// Set position
pub fn set_pos(&mut self, x: i32, y: i32) {
let _ = self.file_mut().write(&format!("P,{},{}", x, y).as_bytes());
self.sync_path();
}
/// Set size
pub fn set_size(&mut self, width: u32, height: u32) {
//TODO: Improve safety and reliability
unsafe { self.unmap(); }
let _ = self
.file_mut()
.write(&format!("S,{},{}", width, height).as_bytes());
self.sync_path();
unsafe { self.remap(); }
}
/// Set title
pub fn set_title(&mut self, title: &str) {
let _ = self.file_mut().write(&format!("T,{}", title).as_bytes());
self.sync_path();
}
/// Blocking iterator over events
pub fn events(&mut self) -> EventIter {
let mut iter = EventIter {
extra: None,
events: [Event::new(); 16],
i: 0,
count: 0,
};
'blocking: loop {
if iter.count == iter.events.len() {
if iter.extra.is_none() {
iter.extra = Some(Vec::with_capacity(32));
}
iter.extra.as_mut().unwrap().extend_from_slice(&iter.events);
iter.count = 0;
}
let bytes = unsafe {
slice::from_raw_parts_mut(
iter.events[iter.count..].as_mut_ptr() as *mut u8,
iter.events[iter.count..].len() * mem::size_of::<Event>(),
)
};
match self.file_mut().read(bytes) {
Ok(0) => {
if !self.window_async && iter.extra.is_none() && iter.count == 0 {
thread::yield_now();
} else {
break 'blocking;
}
}
Ok(count) => {
let count = count / mem::size_of::<Event>();
let events = &iter.events[iter.count..][..count];
iter.count += count;
if self.resizable {
let mut resize = None;
for event in events {
let event = *event;
if event.code == EVENT_RESIZE {
resize = Some((event.a as u32, event.b as u32));
}
}
if let Some((w, h)) = resize {
self.set_size(w, h);
}
}
if !self.window_async {
// Synchronous windows are blocking, can't attempt another read
break 'blocking;
}
}
Err(_) => break 'blocking,
}
}
iter
}
fn file(&self) -> &File {
self.file_opt.as_ref().unwrap()
}
fn file_mut(&mut self) -> &mut File {
self.file_opt.as_mut().unwrap()
}
unsafe fn remap(&mut self) {
self.unmap();
let size = (self.w * self.h) as usize;
let address = redox::mmap(redox::MmapArgs {
fd: self.file().as_raw_fd() as usize,
offset: 0,
length: size * mem::size_of::<Color>(),
flags: flag::MAP_SHARED,
prot: flag::PROT_READ | flag::PROT_WRITE,
addr: core::ptr::null_mut(),
}).expect("orbclient: failed to map memory");
self.data_opt = Some(
slice::from_raw_parts_mut(address.cast::<Color>(), size)
);
}
unsafe fn unmap(&mut self) {
if let Some(data) = self.data_opt.take() {
redox::munmap(
data.as_mut_ptr().cast(),
data.len() * mem::size_of::<Color>(),
).expect("orbclient: failed to unmap memory");
}
}
}
impl Drop for Window {
fn drop(&mut self) {
unsafe { self.unmap(); }
}
}
impl AsRawFd for Window {
fn as_raw_fd(&self) -> RawFd {
self.file().as_raw_fd()
}
}
impl FromRawFd for Window {
unsafe fn from_raw_fd(fd: RawFd) -> Window {
let mut window = Window {
x: 0,
y: 0,
w: 0,
h: 0,
t: String::new(),
window_async: false,
resizable: false,
mode: Cell::new(Mode::Blend),
file_opt: Some(File::from_raw_fd(fd)),
data_opt: None,
};
window.sync_path();
window.remap();
window
}
}
impl IntoRawFd for Window {
fn into_raw_fd(mut self) -> RawFd {
self.file_opt.take().unwrap().into_raw_fd()
}
}
/// Event iterator
pub struct EventIter {
extra: Option<Vec<Event>>,
events: [Event; 16],
i: usize,
count: usize,
}
impl Iterator for EventIter {
type Item = Event;
fn next(&mut self) -> Option<Event> {
let mut i = self.i;
if let Some(ref mut extra) = self.extra {
if i < extra.len() {
self.i += 1;
return Some(extra[i]);
}
i -= extra.len();
}
if i < self.count {
self.i += 1;
return Some(self.events[i]);
}
None
}
}

624
vendor/orbclient/src/sys/sdl2.rs vendored Normal file
View File

@@ -0,0 +1,624 @@
// SPDX-License-Identifier: MIT
use std::cell::{Cell, RefCell};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::{mem, ptr, slice};
use crate::color::Color;
use crate::event::*;
use crate::renderer::Renderer;
use crate::Mode;
use crate::WindowFlag;
static SDL_USAGES: AtomicUsize = AtomicUsize::new(0);
/// SDL2 Context
static mut SDL_CTX: *mut sdl2::Sdl = ptr::null_mut();
/// Video Context
static mut VIDEO_CTX: *mut sdl2::VideoSubsystem = ptr::null_mut();
/// Event Pump
static mut EVENT_PUMP: *mut sdl2::EventPump = ptr::null_mut();
//Call this when the CTX needs to be used is created
#[inline]
unsafe fn init() {
if SDL_USAGES.fetch_add(1, Ordering::Relaxed) == 0 {
SDL_CTX = Box::into_raw(Box::new(sdl2::init().unwrap()));
VIDEO_CTX = Box::into_raw(Box::new((*SDL_CTX).video().unwrap()));
EVENT_PUMP = Box::into_raw(Box::new((*SDL_CTX).event_pump().unwrap()));
}
}
// Call this when drop the sdl2 CTX.
#[inline]
unsafe fn cleanup() {
if SDL_USAGES.fetch_sub(1, Ordering::Relaxed) == 0 {
return;
}
drop(Box::from_raw(SDL_CTX));
drop(Box::from_raw(VIDEO_CTX));
drop(Box::from_raw(EVENT_PUMP));
}
/// Return the (width, height) of the display in pixels
pub fn get_display_size() -> Result<(u32, u32), String> {
unsafe { init() };
unsafe { &*VIDEO_CTX }
.display_bounds(0)
.map(|rect| (rect.width(), rect.height()))
}
/// A window
#[allow(dead_code)]
pub struct Window {
/// The x coordinate of the window
x: i32,
/// The y coordinate of the window
y: i32,
/// The width of the window
w: u32,
/// The height of the window
h: u32,
/// The title of the window
t: String,
/// True if the window should not wait for events
window_async: bool,
/// Drawing mode
mode: Cell<Mode>,
/// The inner renderer
inner: sdl2::render::WindowCanvas,
/// Mouse in relative mode
mouse_relative: bool,
/// Content of the last drop (file | text) operation
drop_content: RefCell<Option<String>>,
}
impl Drop for Window {
fn drop(&mut self) {
unsafe {
cleanup();
}
}
}
impl Renderer for Window {
/// Get width
fn width(&self) -> u32 {
self.w
}
/// Get height
fn height(&self) -> u32 {
self.h
}
/// Access pixel buffer
fn data(&self) -> &[Color] {
let window = self.inner.window();
let surface = window.surface(unsafe { &*EVENT_PUMP }).unwrap();
let bytes = surface.without_lock().unwrap();
unsafe {
slice::from_raw_parts(
bytes.as_ptr() as *const Color,
bytes.len() / mem::size_of::<Color>(),
)
}
}
/// Access pixel buffer mutably
fn data_mut(&mut self) -> &mut [Color] {
let window = self.inner.window_mut();
let mut surface = window.surface(unsafe { &*EVENT_PUMP }).unwrap();
let bytes = surface.without_lock_mut().unwrap();
unsafe {
slice::from_raw_parts_mut(
bytes.as_mut_ptr() as *mut Color,
bytes.len() / mem::size_of::<Color>(),
)
}
}
/// Flip the window buffer
fn sync(&mut self) -> bool {
self.inner.present();
true
}
/// Set/get mode
fn mode(&self) -> &Cell<Mode> {
&self.mode
}
}
impl Window {
/// Create a new window
pub fn new(x: i32, y: i32, w: u32, h: u32, title: &str) -> Option<Self> {
Window::new_flags(x, y, w, h, title, &[])
}
/// Create a new window with flags
pub fn new_flags(
x: i32,
y: i32,
w: u32,
h: u32,
title: &str,
flags: &[WindowFlag],
) -> Option<Self> {
//Insure that init has been called
unsafe { init() };
let mut window_async = false;
//TODO: Use z-order
let mut _back = false;
let mut _front = false;
let mut borderless = false;
let mut resizable = false;
//TODO: Transparent
let mut _transparent = false;
//TODO: Hide exit button
let mut _unclosable = false;
for &flag in flags.iter() {
match flag {
WindowFlag::Async => window_async = true,
WindowFlag::Back => _back = true,
WindowFlag::Front => _front = true,
WindowFlag::Borderless => borderless = true,
WindowFlag::Resizable => resizable = true,
WindowFlag::Transparent => _transparent = true,
WindowFlag::Unclosable => _unclosable = true,
}
}
let mut builder = unsafe { &*VIDEO_CTX }.window(title, w, h);
{
builder.allow_highdpi();
}
if borderless {
builder.borderless();
}
if resizable {
builder.resizable();
}
if x >= 0 || y >= 0 {
builder.position(x, y);
}
match builder.build() {
Ok(window) => Some(Window {
x,
y,
w,
h,
t: title.to_string(),
window_async,
mode: Cell::new(Mode::Blend),
inner: window.into_canvas().software().build().unwrap(),
mouse_relative: false,
drop_content: RefCell::new(None),
}),
Err(_) => None,
}
}
pub fn event_sender(&self) -> sdl2::event::EventSender {
unsafe { &mut *SDL_CTX }.event().unwrap().event_sender()
}
pub fn clipboard(&self) -> String {
let result = unsafe { &*VIDEO_CTX }.clipboard().clipboard_text();
match result {
Ok(value) => return value,
Err(message) => println!("{}", message),
}
String::default()
}
pub fn set_clipboard(&mut self, text: &str) {
let result = unsafe { &*VIDEO_CTX }.clipboard().set_clipboard_text(text);
if let Err(message) = result {
println!("{}", message);
}
}
/// Pops the content of the last drop event from the window.
pub fn pop_drop_content(&self) -> Option<String> {
let result = self.drop_content.borrow().clone();
*self.drop_content.borrow_mut() = None;
result
}
pub fn sync_path(&mut self) {
let window = self.inner.window();
let pos = window.position();
let size = window.size();
let title = window.title();
self.x = pos.0;
self.y = pos.1;
self.w = size.0;
self.h = size.1;
self.t = title.to_string();
}
/// Get x
// TODO: Sync with window movements
pub fn x(&self) -> i32 {
self.x
}
/// Get y
// TODO: Sync with window movements
pub fn y(&self) -> i32 {
self.y
}
/// Get title
pub fn title(&self) -> String {
self.t.clone()
}
/// Get async
pub fn is_async(&self) -> bool {
self.window_async
}
/// Set async
pub fn set_async(&mut self, is_async: bool) {
self.window_async = is_async;
}
/// Set cursor visibility
pub fn set_mouse_cursor(&mut self, visible: bool) {
unsafe { &mut *SDL_CTX }.mouse().show_cursor(visible);
}
/// Set mouse grabbing
pub fn set_mouse_grab(&mut self, grab: bool) {
unsafe { &mut *SDL_CTX }.mouse().capture(grab);
}
/// Set mouse relative mode
pub fn set_mouse_relative(&mut self, relative: bool) {
unsafe { &mut *SDL_CTX }
.mouse()
.set_relative_mouse_mode(relative);
self.mouse_relative = relative;
}
/// Set position
pub fn set_pos(&mut self, x: i32, y: i32) {
self.inner.window_mut().set_position(
sdl2::video::WindowPos::Positioned(x),
sdl2::video::WindowPos::Positioned(y),
);
self.sync_path();
}
/// Set size
pub fn set_size(&mut self, width: u32, height: u32) {
let _ = self.inner.window_mut().set_size(width, height);
self.sync_path();
}
/// Set title
pub fn set_title(&mut self, title: &str) {
let _ = self.inner.window_mut().set_title(title);
self.sync_path();
}
fn convert_scancode(
&self,
scancode_option: Option<sdl2::keyboard::Scancode>,
shift: bool,
) -> Option<(char, u8)> {
if let Some(scancode) = scancode_option {
match scancode {
sdl2::keyboard::Scancode::A => Some((if shift { 'A' } else { 'a' }, K_A)),
sdl2::keyboard::Scancode::B => Some((if shift { 'B' } else { 'b' }, K_B)),
sdl2::keyboard::Scancode::C => Some((if shift { 'C' } else { 'c' }, K_C)),
sdl2::keyboard::Scancode::D => Some((if shift { 'D' } else { 'd' }, K_D)),
sdl2::keyboard::Scancode::E => Some((if shift { 'E' } else { 'e' }, K_E)),
sdl2::keyboard::Scancode::F => Some((if shift { 'F' } else { 'f' }, K_F)),
sdl2::keyboard::Scancode::G => Some((if shift { 'G' } else { 'g' }, K_G)),
sdl2::keyboard::Scancode::H => Some((if shift { 'H' } else { 'h' }, K_H)),
sdl2::keyboard::Scancode::I => Some((if shift { 'I' } else { 'i' }, K_I)),
sdl2::keyboard::Scancode::J => Some((if shift { 'J' } else { 'j' }, K_J)),
sdl2::keyboard::Scancode::K => Some((if shift { 'K' } else { 'k' }, K_K)),
sdl2::keyboard::Scancode::L => Some((if shift { 'L' } else { 'l' }, K_L)),
sdl2::keyboard::Scancode::M => Some((if shift { 'M' } else { 'm' }, K_M)),
sdl2::keyboard::Scancode::N => Some((if shift { 'N' } else { 'n' }, K_N)),
sdl2::keyboard::Scancode::O => Some((if shift { 'O' } else { 'o' }, K_O)),
sdl2::keyboard::Scancode::P => Some((if shift { 'P' } else { 'p' }, K_P)),
sdl2::keyboard::Scancode::Q => Some((if shift { 'Q' } else { 'q' }, K_Q)),
sdl2::keyboard::Scancode::R => Some((if shift { 'R' } else { 'r' }, K_R)),
sdl2::keyboard::Scancode::S => Some((if shift { 'S' } else { 's' }, K_S)),
sdl2::keyboard::Scancode::T => Some((if shift { 'T' } else { 't' }, K_T)),
sdl2::keyboard::Scancode::U => Some((if shift { 'U' } else { 'u' }, K_U)),
sdl2::keyboard::Scancode::V => Some((if shift { 'V' } else { 'v' }, K_V)),
sdl2::keyboard::Scancode::W => Some((if shift { 'W' } else { 'w' }, K_W)),
sdl2::keyboard::Scancode::X => Some((if shift { 'X' } else { 'x' }, K_X)),
sdl2::keyboard::Scancode::Y => Some((if shift { 'Y' } else { 'y' }, K_Y)),
sdl2::keyboard::Scancode::Z => Some((if shift { 'Z' } else { 'z' }, K_Z)),
sdl2::keyboard::Scancode::Num0 => Some((if shift { ')' } else { '0' }, K_0)),
sdl2::keyboard::Scancode::Num1 => Some((if shift { '!' } else { '1' }, K_1)),
sdl2::keyboard::Scancode::Num2 => Some((if shift { '@' } else { '2' }, K_2)),
sdl2::keyboard::Scancode::Num3 => Some((if shift { '#' } else { '3' }, K_3)),
sdl2::keyboard::Scancode::Num4 => Some((if shift { '$' } else { '4' }, K_4)),
sdl2::keyboard::Scancode::Num5 => Some((if shift { '%' } else { '5' }, K_5)),
sdl2::keyboard::Scancode::Num6 => Some((if shift { '^' } else { '6' }, K_6)),
sdl2::keyboard::Scancode::Num7 => Some((if shift { '&' } else { '7' }, K_7)),
sdl2::keyboard::Scancode::Num8 => Some((if shift { '*' } else { '8' }, K_8)),
sdl2::keyboard::Scancode::Num9 => Some((if shift { '(' } else { '9' }, K_9)),
sdl2::keyboard::Scancode::Grave => Some((if shift { '~' } else { '`' }, K_TICK)),
sdl2::keyboard::Scancode::Minus => Some((if shift { '_' } else { '-' }, K_MINUS)),
sdl2::keyboard::Scancode::Equals => Some((if shift { '+' } else { '=' }, K_EQUALS)),
sdl2::keyboard::Scancode::LeftBracket => {
Some((if shift { '{' } else { '[' }, K_BRACE_OPEN))
}
sdl2::keyboard::Scancode::RightBracket => {
Some((if shift { '}' } else { ']' }, K_BRACE_CLOSE))
}
sdl2::keyboard::Scancode::Backslash => {
Some((if shift { '|' } else { '\\' }, K_BACKSLASH))
}
sdl2::keyboard::Scancode::Semicolon => {
Some((if shift { ':' } else { ';' }, K_SEMICOLON))
}
sdl2::keyboard::Scancode::Apostrophe => {
Some((if shift { '"' } else { '\'' }, K_QUOTE))
}
sdl2::keyboard::Scancode::Comma => Some((if shift { '<' } else { ',' }, K_COMMA)),
sdl2::keyboard::Scancode::Period => Some((if shift { '>' } else { '.' }, K_PERIOD)),
sdl2::keyboard::Scancode::Slash => Some((if shift { '?' } else { '/' }, K_SLASH)),
sdl2::keyboard::Scancode::Space => Some((' ', K_SPACE)),
sdl2::keyboard::Scancode::Backspace => Some(('\0', K_BKSP)),
sdl2::keyboard::Scancode::Tab => Some(('\t', K_TAB)),
sdl2::keyboard::Scancode::LCtrl => Some(('\0', K_CTRL)),
sdl2::keyboard::Scancode::RCtrl => Some(('\0', K_CTRL)),
sdl2::keyboard::Scancode::LAlt => Some(('\0', K_ALT)),
sdl2::keyboard::Scancode::RAlt => Some(('\0', K_ALT)),
sdl2::keyboard::Scancode::Return => Some(('\n', K_ENTER)),
sdl2::keyboard::Scancode::Escape => Some(('\x1B', K_ESC)),
sdl2::keyboard::Scancode::F1 => Some(('\0', K_F1)),
sdl2::keyboard::Scancode::F2 => Some(('\0', K_F2)),
sdl2::keyboard::Scancode::F3 => Some(('\0', K_F3)),
sdl2::keyboard::Scancode::F4 => Some(('\0', K_F4)),
sdl2::keyboard::Scancode::F5 => Some(('\0', K_F5)),
sdl2::keyboard::Scancode::F6 => Some(('\0', K_F6)),
sdl2::keyboard::Scancode::F7 => Some(('\0', K_F7)),
sdl2::keyboard::Scancode::F8 => Some(('\0', K_F8)),
sdl2::keyboard::Scancode::F9 => Some(('\0', K_F9)),
sdl2::keyboard::Scancode::F10 => Some(('\0', K_F10)),
sdl2::keyboard::Scancode::Home => Some(('\0', K_HOME)),
sdl2::keyboard::Scancode::LGui => Some(('\0', K_HOME)),
sdl2::keyboard::Scancode::Up => Some(('\0', K_UP)),
sdl2::keyboard::Scancode::PageUp => Some(('\0', K_PGUP)),
sdl2::keyboard::Scancode::Left => Some(('\0', K_LEFT)),
sdl2::keyboard::Scancode::Right => Some(('\0', K_RIGHT)),
sdl2::keyboard::Scancode::End => Some(('\0', K_END)),
sdl2::keyboard::Scancode::Down => Some(('\0', K_DOWN)),
sdl2::keyboard::Scancode::PageDown => Some(('\0', K_PGDN)),
sdl2::keyboard::Scancode::Delete => Some(('\0', K_DEL)),
sdl2::keyboard::Scancode::F11 => Some(('\0', K_F11)),
sdl2::keyboard::Scancode::F12 => Some(('\0', K_F12)),
sdl2::keyboard::Scancode::LShift => Some(('\0', K_LEFT_SHIFT)),
sdl2::keyboard::Scancode::RShift => Some(('\0', K_RIGHT_SHIFT)),
_ => None,
}
} else {
None
}
}
fn get_mouse_position(&self) -> (i32, i32) {
unsafe {
let p_x: *mut i32 = libc::malloc(mem::size_of::<i32>()) as *mut i32;
let p_y: *mut i32 = libc::malloc(mem::size_of::<i32>()) as *mut i32;
sdl2_sys::SDL_GetMouseState(p_x, p_y);
(*p_x, *p_y)
}
}
fn convert_event(&self, event: sdl2::event::Event) -> Vec<Event> {
let mut events = Vec::new();
let button_event = || -> Event {
let mouse = unsafe { &mut *EVENT_PUMP }.mouse_state();
ButtonEvent {
left: mouse.left(),
middle: mouse.middle(),
right: mouse.right(),
}
.to_event()
};
let mods = unsafe { &mut *SDL_CTX }.keyboard().mod_state();
let shift = mods.contains(sdl2::keyboard::Mod::CAPSMOD)
|| mods.contains(sdl2::keyboard::Mod::LSHIFTMOD)
|| mods.contains(sdl2::keyboard::Mod::RSHIFTMOD);
match event {
sdl2::event::Event::RenderTargetsReset { .. } => {
events.push(Event::new());
}
sdl2::event::Event::Window { win_event, .. } => match win_event {
sdl2::event::WindowEvent::Moved(x, y) => {
events.push(MoveEvent { x, y }.to_event())
}
sdl2::event::WindowEvent::Resized(w, h) => events.push(
ResizeEvent {
width: w as u32,
height: h as u32,
}
.to_event(),
),
sdl2::event::WindowEvent::FocusGained => {
events.push(FocusEvent { focused: true }.to_event())
}
sdl2::event::WindowEvent::FocusLost => {
events.push(FocusEvent { focused: false }.to_event())
}
sdl2::event::WindowEvent::Enter => {
events.push(HoverEvent { entered: true }.to_event())
}
sdl2::event::WindowEvent::Leave => {
events.push(HoverEvent { entered: false }.to_event())
}
sdl2::event::WindowEvent::None => events.push(Event::new()),
_ => (),
},
sdl2::event::Event::ClipboardUpdate { .. } => {
events.push(ClipboardUpdateEvent.to_event())
}
sdl2::event::Event::MouseMotion {
x, y, xrel, yrel, ..
} => {
if self.mouse_relative {
events.push(MouseRelativeEvent { dx: xrel, dy: yrel }.to_event())
} else {
events.push(MouseEvent { x, y }.to_event())
}
}
sdl2::event::Event::MouseButtonDown { .. } => events.push(button_event()),
sdl2::event::Event::MouseButtonUp { .. } => events.push(button_event()),
sdl2::event::Event::MouseWheel { x, y, .. } => {
events.push(ScrollEvent { x, y }.to_event())
}
sdl2::event::Event::TextInput { text, .. } => {
for character in text.chars() {
events.push(
TextInputEvent {
character,
}
.to_event(),
);
}
}
sdl2::event::Event::KeyDown { scancode, .. } => {
if let Some(code) = self.convert_scancode(scancode, shift) {
events.push(
KeyEvent {
character: code.0,
scancode: code.1,
pressed: true,
}
.to_event(),
);
}
}
sdl2::event::Event::DropFile { filename, .. } => {
*self.drop_content.borrow_mut() = Some(filename);
let (x, y) = self.get_mouse_position();
events.push(MouseEvent { x, y }.to_event());
events.push(DropEvent { kind: DROP_FILE }.to_event())
}
sdl2::event::Event::DropText { filename, .. } => {
*self.drop_content.borrow_mut() = Some(filename);
let (x, y) = self.get_mouse_position();
events.push(MouseEvent { x, y }.to_event());
events.push(DropEvent { kind: DROP_TEXT }.to_event())
}
sdl2::event::Event::KeyUp { scancode, .. } => {
if let Some(code) = self.convert_scancode(scancode, shift) {
events.push(
KeyEvent {
character: code.0,
scancode: code.1,
pressed: false,
}
.to_event(),
);
}
}
sdl2::event::Event::Quit { .. } => events.push(QuitEvent.to_event()),
_ => (),
}
events
}
/// Blocking iterator over events
pub fn events(&mut self) -> EventIter {
let mut iter = EventIter {
events: [Event::new(); 16],
i: 0,
count: 0,
};
if !self.window_async {
let event = unsafe { &mut *EVENT_PUMP }.wait_event();
if let sdl2::event::Event::Window { .. } = event {
self.sync_path();
}
for converted_event in self.convert_event(event) {
if iter.count < iter.events.len() {
iter.events[iter.count] = converted_event;
iter.count += 1;
} else {
break;
}
}
}
while let Some(event) = unsafe { &mut *EVENT_PUMP }.poll_event() {
if let sdl2::event::Event::Window { .. } = event {
self.sync_path();
}
for converted_event in self.convert_event(event) {
if iter.count < iter.events.len() {
iter.events[iter.count] = converted_event;
iter.count += 1;
} else {
break;
}
}
if iter.count + 2 < iter.events.len() {
break;
}
}
iter
}
/// Returns the id
pub fn id(&self) -> u32 {
self.inner.window().id()
}
}
/// Event iterator
pub struct EventIter {
events: [Event; 16],
i: usize,
count: usize,
}
impl Iterator for EventIter {
type Item = Event;
fn next(&mut self) -> Option<Event> {
if self.i < self.count {
if let Some(event) = self.events.get(self.i) {
self.i += 1;
Some(*event)
} else {
None
}
} else {
None
}
}
}