1use super::types::PixelFormat;
8use crate::framebuffer::{CanvasBuffer, RgbColor};
9
10pub(crate) const CURSOR_W: usize = 12;
16pub(crate) const CURSOR_H: usize = 16;
18pub(crate) const TEXT_CURSOR_MAX_DIM: usize = 32;
20
21#[rustfmt::skip]
23pub(crate) const CURSOR_PIXELS: [u8; CURSOR_W * CURSOR_H] = [
24 1,0,0,0,0,0,0,0,0,0,0,0,
25 1,1,0,0,0,0,0,0,0,0,0,0,
26 1,2,1,0,0,0,0,0,0,0,0,0,
27 1,2,2,1,0,0,0,0,0,0,0,0,
28 1,2,2,2,1,0,0,0,0,0,0,0,
29 1,2,2,2,2,1,0,0,0,0,0,0,
30 1,2,2,2,2,2,1,0,0,0,0,0,
31 1,2,2,2,2,2,2,1,0,0,0,0,
32 1,2,2,2,2,2,2,2,1,0,0,0,
33 1,2,2,2,2,2,1,1,0,0,0,0,
34 1,2,2,2,1,0,0,0,0,0,0,0,
35 1,2,1,1,2,2,1,0,0,0,0,0,
36 1,1,0,0,1,2,2,1,0,0,0,0,
37 0,0,0,0,0,1,2,1,0,0,0,0,
38 0,0,0,0,0,0,1,1,0,0,0,0,
39 0,0,0,0,0,0,0,0,0,0,0,0,
40];
41
42pub(crate) struct CursorManager {
52 pub mc_x: i32,
54 pub mc_y: i32,
55 pub mc_visible: bool,
56 pub mc_dirty: bool,
57 pub mc_save: [u32; CURSOR_W * CURSOR_H],
58
59 pub tc_col: usize,
61 pub tc_row: usize,
62 pub tc_w: usize,
63 pub tc_h: usize,
64 pub tc_visible: bool,
65 pub tc_dirty: bool,
66 pub tc_color: u32,
67 pub tc_save: [u32; TEXT_CURSOR_MAX_DIM * TEXT_CURSOR_MAX_DIM],
68}
69
70impl CursorManager {
71 pub const fn new() -> Self {
73 Self {
74 mc_x: 0,
75 mc_y: 0,
76 mc_visible: false,
77 mc_dirty: false,
78 mc_save: [0u32; CURSOR_W * CURSOR_H],
79 tc_col: 0,
80 tc_row: 0,
81 tc_w: 0,
82 tc_h: 0,
83 tc_visible: false,
84 tc_dirty: false,
85 tc_color: 0,
86 tc_save: [0u32; TEXT_CURSOR_MAX_DIM * TEXT_CURSOR_MAX_DIM],
87 }
88 }
89
90 pub fn mc_save_hw(&mut self, canvas: &CanvasBuffer) {
97 self.mc_dirty = true;
98 let x = self.mc_x;
99 let y = self.mc_y;
100 for cy in 0..CURSOR_H {
101 for cx in 0..CURSOR_W {
102 let px = x + cx as i32;
103 let py = y + cy as i32;
104 if px < 0 || py < 0 || px as usize >= canvas.width || py as usize >= canvas.height {
105 self.mc_save[cy * CURSOR_W + cx] = 0;
106 continue;
107 }
108 self.mc_save[cy * CURSOR_W + cx] = canvas.read_hw_pixel(px as usize, py as usize);
109 }
110 }
111 }
112
113 pub fn mc_draw_hw(&mut self, canvas: &mut CanvasBuffer, fmt: &PixelFormat) {
115 let x = self.mc_x;
116 let y = self.mc_y;
117 let black = fmt.pack_rgb(RgbColor::BLACK.r, RgbColor::BLACK.g, RgbColor::BLACK.b);
118 let white = fmt.pack_rgb(RgbColor::WHITE.r, RgbColor::WHITE.g, RgbColor::WHITE.b);
119 for cy in 0..CURSOR_H {
120 for cx in 0..CURSOR_W {
121 let p = CURSOR_PIXELS[cy * CURSOR_W + cx];
122 if p == 0 {
123 continue;
124 }
125 let px = x + cx as i32;
126 let py = y + cy as i32;
127 if px < 0 || py < 0 || px as usize >= canvas.width || py as usize >= canvas.height {
128 continue;
129 }
130 let color = if p == 1 { black } else { white };
131 canvas.write_hw_pixel(px as usize, py as usize, color);
132 }
133 }
134 }
135
136 pub fn mc_erase_hw(&mut self, canvas: &mut CanvasBuffer) {
139 self.mc_dirty = true;
140 let x = self.mc_x;
141 let y = self.mc_y;
142 for cy in 0..CURSOR_H {
143 for cx in 0..CURSOR_W {
144 if CURSOR_PIXELS[cy * CURSOR_W + cx] == 0 {
145 continue;
146 }
147 let px = x + cx as i32;
148 let py = y + cy as i32;
149 if px < 0 || py < 0 || px as usize >= canvas.width || py as usize >= canvas.height {
150 continue;
151 }
152 canvas.write_hw_pixel(px as usize, py as usize, self.mc_save[cy * CURSOR_W + cx]);
153 }
154 }
155 }
156
157 pub fn update_mouse_cursor(
159 &mut self,
160 x: i32,
161 y: i32,
162 canvas: &mut CanvasBuffer,
163 fmt: &PixelFormat,
164 ) -> bool {
165 let moved = self.mc_visible && (self.mc_x != x || self.mc_y != y);
166 let needs_redraw = !self.mc_visible || moved;
167
168 if moved {
169 self.mc_erase_hw(canvas);
170 }
171 if needs_redraw {
172 self.mc_x = x;
173 self.mc_y = y;
174 self.mc_save_hw(canvas);
175 self.mc_draw_hw(canvas, fmt);
176 self.mc_visible = true;
177 }
178 needs_redraw
179 }
180
181 pub fn hide_mouse_cursor(&mut self, canvas: &mut CanvasBuffer) {
183 if self.mc_visible {
184 self.mc_erase_hw(canvas);
185 self.mc_visible = false;
186 }
187 }
188
189 pub fn text_cursor_rect(
195 &self,
196 col: usize,
197 row: usize,
198 glyph_w: usize,
199 glyph_h: usize,
200 ) -> (usize, usize, usize, usize) {
201 let tw = glyph_w.min(TEXT_CURSOR_MAX_DIM);
202 let th = glyph_h.min(TEXT_CURSOR_MAX_DIM);
203 (col * glyph_w, row * glyph_h, tw, th)
204 }
205
206 pub fn text_cursor_save_hw(
208 &mut self,
209 col: usize,
210 row: usize,
211 glyph_w: usize,
212 glyph_h: usize,
213 canvas: &CanvasBuffer,
214 ) {
215 let (tx, ty, tw, th) = self.text_cursor_rect(col, row, glyph_w, glyph_h);
216 for dy in 0..th {
217 for dx in 0..tw {
218 let px = tx + dx;
219 let py = ty + dy;
220 if px < canvas.width && py < canvas.height {
221 self.tc_save[dy * tw + dx] = canvas.read_hw_pixel(px, py);
222 }
223 }
224 }
225 }
226
227 pub fn text_cursor_draw_hw(
230 &mut self,
231 col: usize,
232 row: usize,
233 glyph_w: usize,
234 glyph_h: usize,
235 color: u32,
236 canvas: &mut CanvasBuffer,
237 ) {
238 self.tc_dirty = false;
239 let (tx, ty, tw, th) = self.text_cursor_rect(col, row, glyph_w, glyph_h);
240 for dy in 0..th {
241 for dx in 0..tw {
242 let px = tx + dx;
243 let py = ty + dy;
244 if px < canvas.width && py < canvas.height {
245 canvas.write_hw_pixel(px, py, color);
246 }
247 }
248 }
249 }
250
251 pub fn text_cursor_erase_hw(
254 &mut self,
255 col: usize,
256 row: usize,
257 glyph_w: usize,
258 glyph_h: usize,
259 canvas: &mut CanvasBuffer,
260 ) {
261 let (tx, ty, tw, th) = self.text_cursor_rect(col, row, glyph_w, glyph_h);
262 for dy in 0..th {
263 for dx in 0..tw {
264 let px = tx + dx;
265 let py = ty + dy;
266 if px < canvas.width && py < canvas.height {
267 canvas.write_hw_pixel(px, py, self.tc_save[dy * tw + dx]);
268 }
269 }
270 }
271 }
272
273 pub fn draw_text_cursor_overlay(
275 &mut self,
276 col: usize,
277 row: usize,
278 glyph_w: usize,
279 glyph_h: usize,
280 color: u32,
281 canvas: &mut CanvasBuffer,
282 ) {
283 self.text_cursor_draw_hw(col, row, glyph_w, glyph_h, color, canvas);
284 }
285
286 pub fn hide_text_cursor(
288 &mut self,
289 col: usize,
290 row: usize,
291 glyph_w: usize,
292 glyph_h: usize,
293 canvas: &mut CanvasBuffer,
294 ) {
295 if self.tc_visible {
296 self.text_cursor_erase_hw(col, row, glyph_w, glyph_h, canvas);
297 self.tc_visible = false;
298 }
299 }
300}