#![allow(dead_code)]
use anyhow::Result;
use x11rb::connection::Connection;
use x11rb::protocol::xproto::*;
#[derive(Debug, Clone, Copy)]
pub struct Rectangle {
pub x: i16,
pub y: i16,
pub width: u16,
pub height: u16,
}
impl Rectangle {
pub fn new(x: i16, y: i16, width: u16, height: u16) -> Self {
Self {
x,
y,
width,
height,
}
}
pub fn intersects(&self, other: &Rectangle) -> bool {
let self_right = self.x + self.width as i16;
let self_bottom = self.y + self.height as i16;
let other_right = other.x + other.width as i16;
let other_bottom = other.y + other.height as i16;
!(self.x >= other_right
|| other.x >= self_right
|| self.y >= other_bottom
|| other.y >= self_bottom)
}
pub fn contains_point(&self, x: i16, y: i16) -> bool {
x >= self.x
&& x < self.x + self.width as i16
&& y >= self.y
&& y < self.y + self.height as i16
}
}
#[derive(Debug, Clone, Copy)]
pub struct Color {
pub argb: u32,
}
impl Color {
pub const fn new(a: u8, r: u8, g: u8, b: u8) -> Self {
Self {
argb: ((a as u32) << 24) | ((r as u32) << 16) | ((g as u32) << 8) | (b as u32),
}
}
pub const fn rgb(r: u8, g: u8, b: u8) -> Self {
Self::new(255, r, g, b)
}
pub const GREEN: Color = Color::rgb(0, 255, 0);
pub const RED: Color = Color::rgb(255, 0, 0);
pub const BLUE: Color = Color::rgb(0, 0, 255);
pub const WHITE: Color = Color::rgb(255, 255, 255);
#[allow(dead_code)]
pub const BLACK: Color = Color::rgb(0, 0, 0);
}
pub struct Renderer<'a> {
conn: &'a x11rb::xcb_ffi::XCBConnection,
window: Window,
gc: Option<Gcontext>,
}
impl<'a> Renderer<'a> {
pub fn new(conn: &'a x11rb::xcb_ffi::XCBConnection, window: Window) -> Self {
Self {
conn,
window,
gc: None,
}
}
fn ensure_gc(&mut self) -> Result<Gcontext> {
if let Some(gc) = self.gc {
Ok(gc)
} else {
let gc = self.conn.generate_id()?;
self.conn
.create_gc(gc, self.window, &CreateGCAux::default())?;
self.gc = Some(gc);
Ok(gc)
}
}
pub fn fill_rectangle(&mut self, rect: Rectangle, color: Color) -> Result<()> {
let gc = self.ensure_gc()?;
self.conn
.change_gc(gc, &ChangeGCAux::new().foreground(color.argb))?;
self.conn.poly_fill_rectangle(
self.window,
gc,
&[x11rb::protocol::xproto::Rectangle {
x: rect.x,
y: rect.y,
width: rect.width,
height: rect.height,
}],
)?;
Ok(())
}
#[allow(dead_code)]
pub fn clear_area(&self, rect: Rectangle) -> Result<()> {
self.conn
.clear_area(false, self.window, rect.x, rect.y, rect.width, rect.height)?;
Ok(())
}
pub fn flush(&self) -> Result<()> {
self.conn.flush()?;
Ok(())
}
}
impl<'a> Drop for Renderer<'a> {
fn drop(&mut self) {
if let Some(gc) = self.gc {
let _ = self.conn.free_gc(gc);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_color_creation() {
let color = Color::new(255, 255, 0, 0);
assert_eq!(color.argb, 0xFF_FF_00_00);
}
#[test]
fn test_color_rgb() {
let red = Color::rgb(255, 0, 0);
assert_eq!(red.argb, 0xFF_FF_00_00);
let green = Color::rgb(0, 255, 0);
assert_eq!(green.argb, 0xFF_00_FF_00);
let blue = Color::rgb(0, 0, 255);
assert_eq!(blue.argb, 0xFF_00_00_FF);
}
#[test]
fn test_color_constants() {
assert_eq!(Color::RED.argb, 0xFF_FF_00_00);
assert_eq!(Color::GREEN.argb, 0xFF_00_FF_00);
assert_eq!(Color::BLUE.argb, 0xFF_00_00_FF);
assert_eq!(Color::WHITE.argb, 0xFF_FF_FF_FF);
assert_eq!(Color::BLACK.argb, 0xFF_00_00_00);
}
#[test]
fn test_color_alpha_channel() {
let transparent = Color::new(0, 255, 255, 255);
assert_eq!(transparent.argb, 0x00_FF_FF_FF);
let semi_transparent = Color::new(128, 255, 0, 0);
assert_eq!(semi_transparent.argb, 0x80_FF_00_00);
}
#[test]
fn test_rectangle_creation() {
let rect = Rectangle {
x: 10,
y: 20,
width: 100,
height: 200,
};
assert_eq!(rect.x, 10);
assert_eq!(rect.y, 20);
assert_eq!(rect.width, 100);
assert_eq!(rect.height, 200);
}
mod property_tests {
use super::*;
use proptest::prelude::*;
proptest! {
#[test]
fn test_color_components_roundtrip(a: u8, r: u8, g: u8, b: u8) {
let color = Color::new(a, r, g, b);
let extracted_a = ((color.argb >> 24) & 0xFF) as u8;
let extracted_r = ((color.argb >> 16) & 0xFF) as u8;
let extracted_g = ((color.argb >> 8) & 0xFF) as u8;
let extracted_b = (color.argb & 0xFF) as u8;
assert_eq!(extracted_a, a);
assert_eq!(extracted_r, r);
assert_eq!(extracted_g, g);
assert_eq!(extracted_b, b);
}
#[test]
fn test_rectangle_bounds(x: i16, y: i16, width: u16, height: u16) {
let rect = Rectangle { x, y, width, height };
assert_eq!(rect.x, x);
assert_eq!(rect.y, y);
assert_eq!(rect.width, width);
assert_eq!(rect.height, height);
}
}
}
}