1#![allow(dead_code)]
2
3use anyhow::Result;
4use x11rb::connection::Connection;
5use x11rb::protocol::xproto::*;
6
7#[derive(Debug, Clone, Copy)]
8pub struct Rectangle {
9 pub x: i16,
10 pub y: i16,
11 pub width: u16,
12 pub height: u16,
13}
14
15impl Rectangle {
16 pub fn new(x: i16, y: i16, width: u16, height: u16) -> Self {
17 Self {
18 x,
19 y,
20 width,
21 height,
22 }
23 }
24
25 pub fn intersects(&self, other: &Rectangle) -> bool {
26 let self_right = self.x + self.width as i16;
27 let self_bottom = self.y + self.height as i16;
28 let other_right = other.x + other.width as i16;
29 let other_bottom = other.y + other.height as i16;
30
31 !(self.x >= other_right
32 || other.x >= self_right
33 || self.y >= other_bottom
34 || other.y >= self_bottom)
35 }
36
37 pub fn contains_point(&self, x: i16, y: i16) -> bool {
38 x >= self.x
39 && x < self.x + self.width as i16
40 && y >= self.y
41 && y < self.y + self.height as i16
42 }
43}
44
45#[derive(Debug, Clone, Copy)]
46pub struct Color {
47 pub argb: u32,
48}
49
50impl Color {
51 pub const fn new(a: u8, r: u8, g: u8, b: u8) -> Self {
52 Self {
53 argb: ((a as u32) << 24) | ((r as u32) << 16) | ((g as u32) << 8) | (b as u32),
54 }
55 }
56
57 pub const fn rgb(r: u8, g: u8, b: u8) -> Self {
58 Self::new(255, r, g, b)
59 }
60
61 pub const GREEN: Color = Color::rgb(0, 255, 0);
62 pub const RED: Color = Color::rgb(255, 0, 0);
63 pub const BLUE: Color = Color::rgb(0, 0, 255);
64 pub const WHITE: Color = Color::rgb(255, 255, 255);
65 #[allow(dead_code)]
66 pub const BLACK: Color = Color::rgb(0, 0, 0);
67}
68
69pub struct Renderer<'a> {
70 conn: &'a x11rb::xcb_ffi::XCBConnection,
71 window: Window,
72 gc: Option<Gcontext>,
73}
74
75impl<'a> Renderer<'a> {
76 pub fn new(conn: &'a x11rb::xcb_ffi::XCBConnection, window: Window) -> Self {
77 Self {
78 conn,
79 window,
80 gc: None,
81 }
82 }
83
84 fn ensure_gc(&mut self) -> Result<Gcontext> {
85 if let Some(gc) = self.gc {
86 Ok(gc)
87 } else {
88 let gc = self.conn.generate_id()?;
89 self.conn
90 .create_gc(gc, self.window, &CreateGCAux::default())?;
91 self.gc = Some(gc);
92 Ok(gc)
93 }
94 }
95
96 pub fn fill_rectangle(&mut self, rect: Rectangle, color: Color) -> Result<()> {
97 let gc = self.ensure_gc()?;
98
99 self.conn
100 .change_gc(gc, &ChangeGCAux::new().foreground(color.argb))?;
101
102 self.conn.poly_fill_rectangle(
103 self.window,
104 gc,
105 &[x11rb::protocol::xproto::Rectangle {
106 x: rect.x,
107 y: rect.y,
108 width: rect.width,
109 height: rect.height,
110 }],
111 )?;
112
113 Ok(())
114 }
115
116 #[allow(dead_code)]
117 pub fn clear_area(&self, rect: Rectangle) -> Result<()> {
118 self.conn
119 .clear_area(false, self.window, rect.x, rect.y, rect.width, rect.height)?;
120 Ok(())
121 }
122
123 pub fn flush(&self) -> Result<()> {
124 self.conn.flush()?;
125 Ok(())
126 }
127}
128
129impl<'a> Drop for Renderer<'a> {
130 fn drop(&mut self) {
131 if let Some(gc) = self.gc {
132 let _ = self.conn.free_gc(gc);
133 }
134 }
135}
136
137#[cfg(test)]
138mod tests {
139 use super::*;
140
141 #[test]
142 fn test_color_creation() {
143 let color = Color::new(255, 255, 0, 0);
144 assert_eq!(color.argb, 0xFF_FF_00_00);
145 }
146
147 #[test]
148 fn test_color_rgb() {
149 let red = Color::rgb(255, 0, 0);
150 assert_eq!(red.argb, 0xFF_FF_00_00);
151
152 let green = Color::rgb(0, 255, 0);
153 assert_eq!(green.argb, 0xFF_00_FF_00);
154
155 let blue = Color::rgb(0, 0, 255);
156 assert_eq!(blue.argb, 0xFF_00_00_FF);
157 }
158
159 #[test]
160 fn test_color_constants() {
161 assert_eq!(Color::RED.argb, 0xFF_FF_00_00);
162 assert_eq!(Color::GREEN.argb, 0xFF_00_FF_00);
163 assert_eq!(Color::BLUE.argb, 0xFF_00_00_FF);
164 assert_eq!(Color::WHITE.argb, 0xFF_FF_FF_FF);
165 assert_eq!(Color::BLACK.argb, 0xFF_00_00_00);
166 }
167
168 #[test]
169 fn test_color_alpha_channel() {
170 let transparent = Color::new(0, 255, 255, 255);
171 assert_eq!(transparent.argb, 0x00_FF_FF_FF);
172
173 let semi_transparent = Color::new(128, 255, 0, 0);
174 assert_eq!(semi_transparent.argb, 0x80_FF_00_00);
175 }
176
177 #[test]
178 fn test_rectangle_creation() {
179 let rect = Rectangle {
180 x: 10,
181 y: 20,
182 width: 100,
183 height: 200,
184 };
185
186 assert_eq!(rect.x, 10);
187 assert_eq!(rect.y, 20);
188 assert_eq!(rect.width, 100);
189 assert_eq!(rect.height, 200);
190 }
191
192 mod property_tests {
193 use super::*;
194 use proptest::prelude::*;
195
196 proptest! {
197 #[test]
198 fn test_color_components_roundtrip(a: u8, r: u8, g: u8, b: u8) {
199 let color = Color::new(a, r, g, b);
200
201 let extracted_a = ((color.argb >> 24) & 0xFF) as u8;
203 let extracted_r = ((color.argb >> 16) & 0xFF) as u8;
204 let extracted_g = ((color.argb >> 8) & 0xFF) as u8;
205 let extracted_b = (color.argb & 0xFF) as u8;
206
207 assert_eq!(extracted_a, a);
208 assert_eq!(extracted_r, r);
209 assert_eq!(extracted_g, g);
210 assert_eq!(extracted_b, b);
211 }
212
213 #[test]
214 fn test_rectangle_bounds(x: i16, y: i16, width: u16, height: u16) {
215 let rect = Rectangle { x, y, width, height };
216
217 assert_eq!(rect.x, x);
219 assert_eq!(rect.y, y);
220 assert_eq!(rect.width, width);
221 assert_eq!(rect.height, height);
222 }
223 }
224 }
225}