1use crate::{
3 arch::x86_64::vga::{self, RgbColor, TextAlign, TextOptions, UiTheme},
4 shell::ShellError,
5 shell_println,
6};
7use alloc::{string::String, vec, vec::Vec};
8
9pub fn cmd_gfx(args: &[String]) -> Result<(), ShellError> {
11 fn print_gfx_help() {
13 shell_println!("Usage: gfx <subcommand>");
14 shell_println!(" help Show this help");
15 shell_println!(" info Show framebuffer/text console info");
16 shell_println!(" mode on|off Enable/disable double-buffer mode");
17 shell_println!(" ui compact|normal|large");
18 shell_println!(" Set UI scaling preset");
19 shell_println!(" test Draw graphics validation screen");
20 }
21
22 if args.is_empty() {
23 print_gfx_help();
24 return Ok(());
25 }
26 match args[0].as_str() {
27 "help" => {
28 print_gfx_help();
29 Ok(())
30 }
31 "info" => {
32 let info = vga::framebuffer_info();
33 if !info.available {
34 shell_println!("Graphics console: unavailable");
35 return Ok(());
36 }
37
38 shell_println!("Graphics console:");
39 shell_println!(
40 " Framebuffer: {}x{} {}bpp pitch={}",
41 info.width,
42 info.height,
43 info.bpp,
44 info.pitch
45 );
46 shell_println!(
47 " RGB masks: R({}:{}) G({}:{}) B({}:{})",
48 info.red_size,
49 info.red_shift,
50 info.green_size,
51 info.green_shift,
52 info.blue_size,
53 info.blue_shift
54 );
55 shell_println!(
56 " Text grid: {}x{} (glyph={}x{})",
57 info.text_cols,
58 info.text_rows,
59 info.glyph_w,
60 info.glyph_h
61 );
62 shell_println!(
63 " Double buffer mode: {}",
64 if info.double_buffer_mode { "on" } else { "off" }
65 );
66 shell_println!(
67 " Double buffer active: {}",
68 if info.double_buffer_enabled {
69 "yes"
70 } else {
71 "no"
72 }
73 );
74 let scale = match info.ui_scale {
75 vga::UiScale::Compact => "compact",
76 vga::UiScale::Normal => "normal",
77 vga::UiScale::Large => "large",
78 };
79 shell_println!(" UI scale: {}", scale);
80 Ok(())
81 }
82 "mode" => {
83 if args.len() < 2 {
84 print_gfx_help();
85 return Ok(());
86 }
87 match args[1].as_str() {
88 "on" => {
89 vga::set_double_buffer_mode(true);
90 shell_println!("gfx: double-buffer mode enabled");
91 }
92 "off" => {
93 vga::set_double_buffer_mode(false);
94 shell_println!("gfx: double-buffer mode disabled");
95 }
96 _ => print_gfx_help(),
97 }
98 Ok(())
99 }
100 "ui" => {
101 if args.len() < 2 {
102 print_gfx_help();
103 return Ok(());
104 }
105 let scale = match args[1].as_str() {
106 "compact" => vga::UiScale::Compact,
107 "normal" => vga::UiScale::Normal,
108 "large" => vga::UiScale::Large,
109 _ => {
110 print_gfx_help();
111 return Ok(());
112 }
113 };
114 vga::set_ui_scale(scale);
115 shell_println!("gfx: ui scale updated");
116 Ok(())
117 }
118 "test" => cmd_gfx_test(),
119 _ => {
120 print_gfx_help();
121 Ok(())
122 }
123 }
124}
125
126pub fn cmd_gfx_test() -> Result<(), ShellError> {
128 if !vga::is_available() {
129 shell_println!("gfx-test: framebuffer console unavailable");
130 return Ok(());
131 }
132
133 let (w, h) = vga::screen_size();
134 let canvas = vga::Canvas::new(
135 RgbColor::new(0xE2, 0xE8, 0xF0),
136 RgbColor::new(0x12, 0x16, 0x1E),
137 );
138 canvas.begin_frame();
139 canvas.clear();
140
141 for y in (0..h).step_by(40) {
142 vga::draw_line(
143 0,
144 y as isize,
145 w.saturating_sub(1) as isize,
146 y as isize,
147 RgbColor::new(0x22, 0x2E, 0x3A),
148 );
149 }
150 for x in (0..w).step_by(40) {
151 vga::draw_line(
152 x as isize,
153 0,
154 x as isize,
155 h.saturating_sub(1) as isize,
156 RgbColor::new(0x22, 0x2E, 0x3A),
157 );
158 }
159
160 let bw = w.saturating_sub(120).min(560);
161 let bh = h.saturating_sub(220).min(240);
162 let bx = 60;
163 let by = 80;
164 vga::fill_rect(bx, by, bw, bh, RgbColor::new(0x1A, 0x22, 0x2C));
165 vga::draw_rect(bx, by, bw, bh, RgbColor::new(0x4F, 0xB3, 0xB3));
166 vga::fill_rect_alpha(
167 bx + 24,
168 by + 24,
169 bw.saturating_sub(48),
170 bh.saturating_sub(48),
171 RgbColor::new(0x7E, 0xC1, 0xFF),
172 96,
173 );
174 vga::set_clip_rect(
175 bx + 12,
176 by + 12,
177 bw.saturating_sub(24),
178 bh.saturating_sub(24),
179 );
180 vga::fill_rect(bx, by, bw, bh, RgbColor::new(0x1B, 0x4D, 0x8A));
181 vga::reset_clip_rect();
182
183 vga::draw_text(
184 bx + 18,
185 by + 16,
186 "GFX TEST: alpha / clip / text",
187 TextOptions {
188 fg: RgbColor::new(0xF5, 0xFA, 0xFF),
189 bg: RgbColor::new(0x1A, 0x22, 0x2C),
190 align: TextAlign::Left,
191 wrap: false,
192 max_width: Some(bw.saturating_sub(36)),
193 },
194 );
195
196 canvas.system_status_line(UiTheme::OCEAN_STATUS);
197 canvas.end_frame();
198 vga::set_text_cursor(0, vga::text_rows().saturating_sub(2));
199 shell_println!("gfx-test: rendered");
200 Ok(())
201}
202
203pub fn cmd_gfx_demo(_args: &[String]) -> Result<(), ShellError> {
205 use vga::{
206 DockEdge, TerminalWidget, UiDockLayout, UiLabel, UiPanel, UiProgressBar, UiRect, UiTable,
207 };
208
209 if !vga::is_available() {
210 shell_println!("gfx-demo: framebuffer console unavailable");
211 return Ok(());
212 }
213
214 let mut layout = UiDockLayout::from_screen();
215 let top = layout.dock(DockEdge::Top, vga::ui_scale_px(56));
216 let bottom = layout.dock(DockEdge::Bottom, vga::ui_scale_px(120));
217 let left = layout.dock(DockEdge::Left, vga::ui_scale_px(360));
218 let _center = layout.remaining();
219
220 let theme = UiTheme::SLATE;
221 let canvas = vga::Canvas::new(theme.text, theme.background);
222 canvas.begin_frame();
223 canvas.ui_clear(theme);
224
225 vga::ui_draw_panel_widget(&UiPanel {
226 rect: top,
227 title: "Strat9 Graphics Console",
228 body: "Dock layout + widgets + terminal demo",
229 theme,
230 });
231
232 canvas.ui_label(&UiLabel {
233 rect: UiRect::new(
234 top.x + vga::ui_scale_px(8),
235 top.y + vga::ui_scale_px(30),
236 top.w.saturating_sub(vga::ui_scale_px(16)),
237 vga::ui_scale_px(24),
238 ),
239 text: "layout: top + bottom + left + center",
240 fg: RgbColor::new(0xD0, 0xE4, 0xFF),
241 bg: theme.panel_bg,
242 align: TextAlign::Left,
243 });
244
245 canvas.ui_panel(
246 left.x,
247 left.y,
248 left.w,
249 left.h,
250 "System",
251 "Progress bars and data table",
252 theme,
253 );
254
255 canvas.ui_progress_bar(UiProgressBar {
256 rect: UiRect::new(
257 left.x + vga::ui_scale_px(12),
258 left.y + vga::ui_scale_px(46),
259 left.w.saturating_sub(vga::ui_scale_px(24)),
260 vga::ui_scale_px(16),
261 ),
262 value: 72,
263 fg: RgbColor::new(0x58, 0xD6, 0xA3),
264 bg: RgbColor::new(0x12, 0x16, 0x1E),
265 border: theme.panel_border,
266 });
267
268 let headers = vec![
269 String::from("Metric"),
270 String::from("Value"),
271 String::from("Status"),
272 ];
273 let rows: Vec<Vec<String>> = vec![
274 vec![String::from("CPU"), String::from("72%"), String::from("ok")],
275 vec![
276 String::from("Memory"),
277 String::from("43%"),
278 String::from("ok"),
279 ],
280 ];
281 canvas.ui_table(&UiTable {
282 rect: UiRect::new(
283 left.x + vga::ui_scale_px(12),
284 left.y + vga::ui_scale_px(96),
285 left.w.saturating_sub(vga::ui_scale_px(24)),
286 left.h.saturating_sub(vga::ui_scale_px(108)),
287 ),
288 headers,
289 rows,
290 theme,
291 });
292
293 let mut term = TerminalWidget::new(bottom, 64);
294 term.title = String::from("Kernel Terminal");
295 term.push_ansi_line("\u{1b}[36m[boot]\u{1b}[0m ui widgets initialized");
296 term.draw();
297
298 canvas.system_status_line(UiTheme::OCEAN_STATUS);
299 canvas.end_frame();
300 let row = vga::text_rows().saturating_sub(2);
301 vga::set_text_cursor(0, row);
302 Ok(())
303}