1#![allow(dead_code)]
9#![allow(static_mut_refs)]
10
11extern crate alloc;
12use crate::kernel::fs;
13pub use crate::kernel::keyboard::KeyboardLayout;
14use crate::os_lib::json::{self, JsonValue};
15use alloc::collections::BTreeMap;
16use alloc::string::String;
17
18#[derive(Clone, Copy, Debug)]
19pub struct ThemeConfig {
20 pub name: &'static str,
21 pub desktop_bg: u32,
22 pub terminal_bg: u32,
23 pub terminal_fg: u32,
24 pub active_window_border: u32,
25 pub non_active_window_border: u32,
26 pub window_title_bar: u32,
27 pub window_title_fg: u32,
28 pub app_bg: u32,
30 pub app_fg: u32,
31 pub button_bg: u32,
32 pub button_fg: u32,
33 pub button_active_bg: u32,
34 pub button_active_fg: u32,
35 pub focus_highlight: u32,
36 pub mouse_cursor: u32,
38 pub mouse_stroke: u32,
39 pub login_screen_bg: u32,
41 pub login_dialog_bg: u32,
42 pub login_dialog_shadow: u32,
43 pub login_dialog_border: u32,
44 pub login_text_fg: u32,
45 pub login_field_bg: u32,
46 pub login_field_border: u32,
47 pub sysmon_bg: u32,
49 pub sysmon_header_bg: u32,
50 pub sysmon_accent: u32,
51 pub sysmon_text: u32,
52 pub sysmon_value: u32,
53 pub sysmon_border: u32,
54 pub tab_area_bg: u32,
56 pub tab_area_border: u32,
57 pub tab_active_bg: u32,
58 pub tab_active_fg: u32,
59 pub tab_inactive_fg: u32,
60 pub fn_bar_bg: u32,
62 pub fn_bar_cell_bg: u32,
63 pub fn_bar_key_bg: u32,
64 pub fn_bar_key_fg: u32,
65 pub fn_bar_text_fg: u32,
66 pub fn_bar_dim_fg: u32,
67 pub fn_bar_border: u32,
68 pub ime_bg: u32,
70 pub ime_border: u32,
71 pub ime_text: u32,
72 pub win_btn_close: u32,
74 pub win_btn_fullscreen: u32,
75 pub win_btn_maximize: u32,
76 pub win_btn_restore: u32,
77 pub win_btn_minimize: u32,
78 pub error_fg: u32, pub success_fg: u32, pub warning_fg: u32, pub ui_overlay_bg: u32, pub ui_overlay_accent: u32, pub ui_input_bg: u32, pub browser_bg: u32, pub browser_fg: u32, pub browser_secondary: u32, pub login_caret: u32, pub login_hint_fg: u32, pub scrollbar_thumb: u32, pub form_input_bg: u32, pub form_input_fg: u32, pub form_input_border: u32, pub form_input_border_focus: u32, pub form_input_accent: u32, pub form_checkbox_checked: u32, pub form_checkbox_unchecked: u32, pub btn_default_bg: u32, pub btn_default_border: u32, pub btn_hover_bg: u32, pub btn_hover_border: u32, pub editor_line_highlight: u32, pub editor_dim_fg: u32, pub video_bar_bg: u32, pub video_bar_sep: u32, pub video_progress_fg: u32, pub video_thumb_fg: u32, pub privacy_badge_bg: u32, pub privacy_badge_fg: u32, }
117
118pub static PRESET_THEMES: &[ThemeConfig] = &[
119 ThemeConfig {
120 name: "Earth Cream",
121 desktop_bg: 0xFFE5DFD3,
122 terminal_bg: 0xFFFDFBF7,
123 terminal_fg: 0xFF2F2A25,
124 active_window_border: 0xFF8F9779,
125 non_active_window_border: 0xFFC5BDB2,
126 window_title_bar: 0xFFBCAE9B,
127 window_title_fg: 0xFF2F2A25,
128 app_bg: 0xFFFAF8F5,
129 app_fg: 0xFF2F2A25,
130 button_bg: 0xFFDDD6CB,
131 button_fg: 0xFF4E453C,
132 button_active_bg: 0xFFC48B71,
133 button_active_fg: 0xFFFFFFFF,
134 focus_highlight: 0xFFC48B71,
135 mouse_cursor: 0xFFFAF8F5,
136 mouse_stroke: 0xFF2F2A25,
137 login_screen_bg: 0xFF28231E,
138 login_dialog_bg: 0xFFEAE5DC,
139 login_dialog_shadow: 0xFF161311,
140 login_dialog_border: 0xFF8F9779,
141 login_text_fg: 0xFF2F2A25,
142 login_field_bg: 0xFFFAF8F5,
143 login_field_border: 0xFF8F9779,
144 sysmon_bg: 0xFF2F2A25,
145 sysmon_header_bg: 0xFF3D3730,
146 sysmon_accent: 0xFFC48B71,
147 sysmon_text: 0xFFB8B0A5,
148 sysmon_value: 0xFFF6F2EA,
149 sysmon_border: 0xFF4A423B,
150 tab_area_bg: 0xFF3A342E,
151 tab_area_border: 0xFF4A423B,
152 tab_active_bg: 0xFFC48B71,
153 tab_active_fg: 0xFFFDFBF7,
154 tab_inactive_fg: 0xFFB8B0A5,
155 fn_bar_bg: 0xFF25201C,
156 fn_bar_cell_bg: 0xFF2F2A25,
157 fn_bar_key_bg: 0xFF4A423B,
158 fn_bar_key_fg: 0xFFC48B71,
159 fn_bar_text_fg: 0xFFFAF8F5,
160 fn_bar_dim_fg: 0xFF73695F,
161 fn_bar_border: 0xFF4A423B,
162 ime_bg: 0xFFFAF8F5,
163 ime_border: 0xFFC48B71,
164 ime_text: 0xFF2F2A25,
165 win_btn_close: 0xFFB85450,
166 win_btn_fullscreen: 0xFF8A6CA0,
167 win_btn_maximize: 0xFF5A8C5A,
168 win_btn_restore: 0xFFB87840,
169 win_btn_minimize: 0xFFA09040,
170 error_fg: 0xFFCC4444,
171 success_fg: 0xFF4A8A50,
172 warning_fg: 0xFFCC8040,
173 ui_overlay_bg: 0xFF2A2420,
174 ui_overlay_accent: 0xFFC48B71,
175 ui_input_bg: 0xFF3A342E,
176 browser_bg: 0xFF2A2420,
177 browser_fg: 0xFFFDFBF7,
178 browser_secondary: 0xFF4A423B,
179 login_caret: 0xFFC48B71,
180 login_hint_fg: 0xFF8A7E72,
181 scrollbar_thumb: 0xFF8F9779,
182 form_input_bg: 0xFFFAF8F5,
183 form_input_fg: 0xFF2F2A25,
184 form_input_border: 0xFF9E9286,
185 form_input_border_focus: 0xFFC48B71,
186 form_input_accent: 0xFF8F9779,
187 form_checkbox_checked: 0xFFC48B71,
188 form_checkbox_unchecked: 0xFFEAE5DC,
189 btn_default_bg: 0xFFDDD6CB,
190 btn_default_border: 0xFF9E9286,
191 btn_hover_bg: 0xFF8F9779,
192 btn_hover_border: 0xFFC48B71,
193 editor_line_highlight: 0xFFC48B71,
194 editor_dim_fg: 0xFF9E9286,
195 video_bar_bg: 0xFF3A342E,
196 video_bar_sep: 0xFF4A423B,
197 video_progress_fg: 0xFF4A8A50,
198 video_thumb_fg: 0xFFFAF8F5,
199 privacy_badge_bg: 0xFF2F4A2F,
200 privacy_badge_fg: 0xFFECFDF5,
201 },
202 ThemeConfig {
203 name: "Dracula",
204 desktop_bg: 0xFF181A1F,
205 terminal_bg: 0xFF282A36,
206 terminal_fg: 0xFFF8F8F2,
207 active_window_border: 0xFFFF79C6,
208 non_active_window_border: 0xFF6272A4,
209 window_title_bar: 0xFF44475A,
210 window_title_fg: 0xFFF8F8F2,
211 app_bg: 0xFFF3F4F6,
212 app_fg: 0xFF1F2937,
213 button_bg: 0xFFE5E7EB,
214 button_fg: 0xFF1F2937,
215 button_active_bg: 0xFF3B82F6,
216 button_active_fg: 0xFFFFFFFF,
217 focus_highlight: 0xFFF59E0B,
218 mouse_cursor: 0xFFFFFFFF,
219 mouse_stroke: 0xFF000000,
220 login_screen_bg: 0xFF0B0B0D,
221 login_dialog_bg: 0xFF1E1F29,
222 login_dialog_shadow: 0xFF040405,
223 login_dialog_border: 0xFF6272A4,
224 login_text_fg: 0xFFF8F8F2,
225 login_field_bg: 0xFF282A36,
226 login_field_border: 0xFFFF79C6,
227 sysmon_bg: 0xFF1E1F29,
228 sysmon_header_bg: 0xFF282A36,
229 sysmon_accent: 0xFFBD93F9,
230 sysmon_text: 0xFF6272A4,
231 sysmon_value: 0xFFF8F8F2,
232 sysmon_border: 0xFF44475A,
233 tab_area_bg: 0xFF1E1F29,
234 tab_area_border: 0xFF44475A,
235 tab_active_bg: 0xFF44475A,
236 tab_active_fg: 0xFF50FA7B,
237 tab_inactive_fg: 0xFF8BE9FD,
238 fn_bar_bg: 0xFF1E1F29,
239 fn_bar_cell_bg: 0xFF282A36,
240 fn_bar_key_bg: 0xFF44475A,
241 fn_bar_key_fg: 0xFF8BE9FD,
242 fn_bar_text_fg: 0xFFF8F8F2,
243 fn_bar_dim_fg: 0xFF6272A4,
244 fn_bar_border: 0xFF6272A4,
245 ime_bg: 0xFF1E1F29,
246 ime_border: 0xFFBD93F9,
247 ime_text: 0xFFF8F8F2,
248 win_btn_close: 0xFFFF5555,
249 win_btn_fullscreen: 0xFFBD93F9,
250 win_btn_maximize: 0xFF50FA7B,
251 win_btn_restore: 0xFFFFB86C,
252 win_btn_minimize: 0xFFF1FA8C,
253 error_fg: 0xFFFF5555,
254 success_fg: 0xFF50FA7B,
255 warning_fg: 0xFFFFB86C,
256 ui_overlay_bg: 0xFF1E1F29,
257 ui_overlay_accent: 0xFFFF79C6,
258 ui_input_bg: 0xFF1E1F29,
259 browser_bg: 0xFF282A36,
260 browser_fg: 0xFFF8F8F2,
261 browser_secondary: 0xFF44475A,
262 login_caret: 0xFF756CE0,
263 login_hint_fg: 0xFF6272A4,
264 scrollbar_thumb: 0xFF6272A4,
265 form_input_bg: 0xFF1E1F29,
266 form_input_fg: 0xFFF8F8F2,
267 form_input_border: 0xFF6272A4,
268 form_input_border_focus: 0xFFFF79C6,
269 form_input_accent: 0xFF8BE9FD,
270 form_checkbox_checked: 0xFFFF79C6,
271 form_checkbox_unchecked: 0xFF1E1F29,
272 btn_default_bg: 0xFF44475A,
273 btn_default_border: 0xFF6272A4,
274 btn_hover_bg: 0xFF6272A4,
275 btn_hover_border: 0xFF8BE9FD,
276 editor_line_highlight: 0xFF756CE0,
277 editor_dim_fg: 0xFF6272A4,
278 video_bar_bg: 0xFF282A36,
279 video_bar_sep: 0xFF44475A,
280 video_progress_fg: 0xFF50FA7B,
281 video_thumb_fg: 0xFFF8F8F2,
282 privacy_badge_bg: 0xFF064E3B,
283 privacy_badge_fg: 0xFFECFDF5,
284 },
285 ThemeConfig {
286 name: "Slate Blue",
287 desktop_bg: 0xFF0F172A,
288 terminal_bg: 0xFF1E293B,
289 terminal_fg: 0xFFF1F5F9,
290 active_window_border: 0xFF38BDF8,
291 non_active_window_border: 0xFF475569,
292 window_title_bar: 0xFF334155,
293 window_title_fg: 0xFFF1F5F9,
294 app_bg: 0xFFF8FAFC,
295 app_fg: 0xFF0F172A,
296 button_bg: 0xFFE2E8F0,
297 button_fg: 0xFF334155,
298 button_active_bg: 0xFF2563EB,
299 button_active_fg: 0xFFFFFFFF,
300 focus_highlight: 0xFFF59E0B,
301 mouse_cursor: 0xFFFFFFFF,
302 mouse_stroke: 0xFF000000,
303 login_screen_bg: 0xFF0B132B,
304 login_dialog_bg: 0xFF1C2541,
305 login_dialog_shadow: 0xFF050814,
306 login_dialog_border: 0xFF475569,
307 login_text_fg: 0xFFF1F5F9,
308 login_field_bg: 0xFF1E293B,
309 login_field_border: 0xFF38BDF8,
310 sysmon_bg: 0xFF1E293B,
311 sysmon_header_bg: 0xFF334155,
312 sysmon_accent: 0xFF38BDF8,
313 sysmon_text: 0xFF94A3B8,
314 sysmon_value: 0xFFF1F5F9,
315 sysmon_border: 0xFF475569,
316 tab_area_bg: 0xFF1E293B,
317 tab_area_border: 0xFF475569,
318 tab_active_bg: 0xFF334155,
319 tab_active_fg: 0xFF38BDF8,
320 tab_inactive_fg: 0xFF94A3B8,
321 fn_bar_bg: 0xFF0F172A,
322 fn_bar_cell_bg: 0xFF1E293B,
323 fn_bar_key_bg: 0xFF334155,
324 fn_bar_key_fg: 0xFF38BDF8,
325 fn_bar_text_fg: 0xFFF1F5F9,
326 fn_bar_dim_fg: 0xFF64748B,
327 fn_bar_border: 0xFF475569,
328 ime_bg: 0xFF1E293B,
329 ime_border: 0xFF38BDF8,
330 ime_text: 0xFFF1F5F9,
331 win_btn_close: 0xFFEF4444,
332 win_btn_fullscreen: 0xFF8B5CF6,
333 win_btn_maximize: 0xFF22C55E,
334 win_btn_restore: 0xFFF97316,
335 win_btn_minimize: 0xFFEAB308,
336 error_fg: 0xFFEF4444,
337 success_fg: 0xFF22C55E,
338 warning_fg: 0xFFF97316,
339 ui_overlay_bg: 0xFF1E293B,
340 ui_overlay_accent: 0xFF38BDF8,
341 ui_input_bg: 0xFF1E293B,
342 browser_bg: 0xFF1E293B,
343 browser_fg: 0xFFF1F5F9,
344 browser_secondary: 0xFF334155,
345 login_caret: 0xFF38BDF8,
346 login_hint_fg: 0xFF64748B,
347 scrollbar_thumb: 0xFF475569,
348 form_input_bg: 0xFF1E293B,
349 form_input_fg: 0xFFF1F5F9,
350 form_input_border: 0xFF475569,
351 form_input_border_focus: 0xFF38BDF8,
352 form_input_accent: 0xFF38BDF8,
353 form_checkbox_checked: 0xFF38BDF8,
354 form_checkbox_unchecked: 0xFF1E293B,
355 btn_default_bg: 0xFF334155,
356 btn_default_border: 0xFF475569,
357 btn_hover_bg: 0xFF475569,
358 btn_hover_border: 0xFF38BDF8,
359 editor_line_highlight: 0xFF38BDF8,
360 editor_dim_fg: 0xFF64748B,
361 video_bar_bg: 0xFF1E293B,
362 video_bar_sep: 0xFF334155,
363 video_progress_fg: 0xFF22C55E,
364 video_thumb_fg: 0xFFF1F5F9,
365 privacy_badge_bg: 0xFF064E3B,
366 privacy_badge_fg: 0xFFECFDF5,
367 },
368 ThemeConfig {
369 name: "Emerald Green",
370 desktop_bg: 0xFF022C22,
371 terminal_bg: 0xFF064E3B,
372 terminal_fg: 0xFFECFDF5,
373 active_window_border: 0xFF34D399,
374 non_active_window_border: 0xFF059669,
375 window_title_bar: 0xFF047857,
376 window_title_fg: 0xFFECFDF5,
377 app_bg: 0xFFF0FDF4,
378 app_fg: 0xFF064E3B,
379 button_bg: 0xFFD1FAE5,
380 button_fg: 0xFF065F46,
381 button_active_bg: 0xFF10B981,
382 button_active_fg: 0xFFFFFFFF,
383 focus_highlight: 0xFFF59E0B,
384 mouse_cursor: 0xFFFFFFFF,
385 mouse_stroke: 0xFF000000,
386 login_screen_bg: 0xFF021B15,
387 login_dialog_bg: 0xFF064E3B,
388 login_dialog_shadow: 0xFF010C09,
389 login_dialog_border: 0xFF059669,
390 login_text_fg: 0xFFECFDF5,
391 login_field_bg: 0xFF022C22,
392 login_field_border: 0xFF34D399,
393 sysmon_bg: 0xFF064E3B,
394 sysmon_header_bg: 0xFF047857,
395 sysmon_accent: 0xFF34D399,
396 sysmon_text: 0xFF6EE7B7,
397 sysmon_value: 0xFFECFDF5,
398 sysmon_border: 0xFF059669,
399 tab_area_bg: 0xFF064E3B,
400 tab_area_border: 0xFF059669,
401 tab_active_bg: 0xFF047857,
402 tab_active_fg: 0xFF34D399,
403 tab_inactive_fg: 0xFF6EE7B7,
404 fn_bar_bg: 0xFF022C22,
405 fn_bar_cell_bg: 0xFF064E3B,
406 fn_bar_key_bg: 0xFF047857,
407 fn_bar_key_fg: 0xFF34D399,
408 fn_bar_text_fg: 0xFFECFDF5,
409 fn_bar_dim_fg: 0xFF059669,
410 fn_bar_border: 0xFF059669,
411 ime_bg: 0xFF064E3B,
412 ime_border: 0xFF34D399,
413 ime_text: 0xFFECFDF5,
414 win_btn_close: 0xFFEF4444,
415 win_btn_fullscreen: 0xFFA78BFA,
416 win_btn_maximize: 0xFF34D399,
417 win_btn_restore: 0xFFFB923C,
418 win_btn_minimize: 0xFFFBBF24,
419 error_fg: 0xFFEF4444,
420 success_fg: 0xFF34D399,
421 warning_fg: 0xFFFB923C,
422 ui_overlay_bg: 0xFF064E3B,
423 ui_overlay_accent: 0xFF34D399,
424 ui_input_bg: 0xFF064E3B,
425 browser_bg: 0xFF064E3B,
426 browser_fg: 0xFFECFDF5,
427 browser_secondary: 0xFF047857,
428 login_caret: 0xFF34D399,
429 login_hint_fg: 0xFF6EE7B7,
430 scrollbar_thumb: 0xFF059669,
431 form_input_bg: 0xFF064E3B,
432 form_input_fg: 0xFFECFDF5,
433 form_input_border: 0xFF059669,
434 form_input_border_focus: 0xFF34D399,
435 form_input_accent: 0xFF34D399,
436 form_checkbox_checked: 0xFF34D399,
437 form_checkbox_unchecked: 0xFF064E3B,
438 btn_default_bg: 0xFF047857,
439 btn_default_border: 0xFF059669,
440 btn_hover_bg: 0xFF059669,
441 btn_hover_border: 0xFF34D399,
442 editor_line_highlight: 0xFF34D399,
443 editor_dim_fg: 0xFF059669,
444 video_bar_bg: 0xFF064E3B,
445 video_bar_sep: 0xFF047857,
446 video_progress_fg: 0xFF34D399,
447 video_thumb_fg: 0xFFECFDF5,
448 privacy_badge_bg: 0xFF022C22,
449 privacy_badge_fg: 0xFFECFDF5,
450 },
451 ThemeConfig {
452 name: "Cherry Light",
453 desktop_bg: 0xFFFFF1F2,
454 terminal_bg: 0xFFFFE4E6,
455 terminal_fg: 0xFF9F1239,
456 active_window_border: 0xFFF43F5E,
457 non_active_window_border: 0xFFFDA4AF,
458 window_title_bar: 0xFFFECDD3,
459 window_title_fg: 0xFF9F1239,
460 app_bg: 0xFFFFF1F2,
461 app_fg: 0xFF881337,
462 button_bg: 0xFFFFE4E6,
463 button_fg: 0xFF9F1239,
464 button_active_bg: 0xFFE11D48,
465 button_active_fg: 0xFFFFFFFF,
466 focus_highlight: 0xFFF59E0B,
467 mouse_cursor: 0xFFFFFFFF,
468 mouse_stroke: 0xFF000000,
469 login_screen_bg: 0xFF4C0519,
470 login_dialog_bg: 0xFFFFE4E6,
471 login_dialog_shadow: 0xFF27020B,
472 login_dialog_border: 0xFFFDA4AF,
473 login_text_fg: 0xFF9F1239,
474 login_field_bg: 0xFFFFF1F2,
475 login_field_border: 0xFFF43F5E,
476 sysmon_bg: 0xFFFFE4E6,
477 sysmon_header_bg: 0xFFFECDD3,
478 sysmon_accent: 0xFFF43F5E,
479 sysmon_text: 0xFFFDA4AF,
480 sysmon_value: 0xFF9F1239,
481 sysmon_border: 0xFFFDA4AF,
482 tab_area_bg: 0xFFFFE4E6,
483 tab_area_border: 0xFFFDA4AF,
484 tab_active_bg: 0xFFFECDD3,
485 tab_active_fg: 0xFFF43F5E,
486 tab_inactive_fg: 0xFFFDA4AF,
487 fn_bar_bg: 0xFFFFF1F2,
488 fn_bar_cell_bg: 0xFFFFE4E6,
489 fn_bar_key_bg: 0xFFFECDD3,
490 fn_bar_key_fg: 0xFFF43F5E,
491 fn_bar_text_fg: 0xFF9F1239,
492 fn_bar_dim_fg: 0xFFFDA4AF,
493 fn_bar_border: 0xFFFDA4AF,
494 ime_bg: 0xFFFFE4E6,
495 ime_border: 0xFFF43F5E,
496 ime_text: 0xFF9F1239,
497 win_btn_close: 0xFFE11D48,
498 win_btn_fullscreen: 0xFFA855F7,
499 win_btn_maximize: 0xFF22C55E,
500 win_btn_restore: 0xFFF97316,
501 win_btn_minimize: 0xFFEAB308,
502 error_fg: 0xFFE11D48,
503 success_fg: 0xFF22C55E,
504 warning_fg: 0xFFF97316,
505 ui_overlay_bg: 0xFFFFE4E6,
506 ui_overlay_accent: 0xFFF43F5E,
507 ui_input_bg: 0xFFFFE4E6,
508 browser_bg: 0xFFFFE4E6,
509 browser_fg: 0xFF9F1239,
510 browser_secondary: 0xFFFECDD3,
511 login_caret: 0xFFF43F5E,
512 login_hint_fg: 0xFFFDA4AF,
513 scrollbar_thumb: 0xFFFDA4AF,
514 form_input_bg: 0xFFFFF1F2,
515 form_input_fg: 0xFF9F1239,
516 form_input_border: 0xFFFDA4AF,
517 form_input_border_focus: 0xFFF43F5E,
518 form_input_accent: 0xFFF43F5E,
519 form_checkbox_checked: 0xFFF43F5E,
520 form_checkbox_unchecked: 0xFFFFE4E6,
521 btn_default_bg: 0xFFFFE4E6,
522 btn_default_border: 0xFFFDA4AF,
523 btn_hover_bg: 0xFFFDA4AF,
524 btn_hover_border: 0xFFF43F5E,
525 editor_line_highlight: 0xFFF43F5E,
526 editor_dim_fg: 0xFFFDA4AF,
527 video_bar_bg: 0xFFFFE4E6,
528 video_bar_sep: 0xFFFDA4AF,
529 video_progress_fg: 0xFFF43F5E,
530 video_thumb_fg: 0xFF9F1239,
531 privacy_badge_bg: 0xFF4C0519,
532 privacy_badge_fg: 0xFFFFF1F2,
533 },
534];
535
536#[derive(Clone, Debug)]
537pub struct OsConfig {
538 pub keyboard_layout: KeyboardLayout,
539 pub theme_name: String,
540 pub theme: ThemeConfig,
541 pub font_names: [String; 3],
543 pub enable_sound: bool,
544 pub swap_caps_ctrl: bool,
545 pub auto_dhcp: bool,
546 pub wifi_ssid: String,
547 pub wifi_password: String,
548 pub custom_theme: ThemeConfig, pub enable_custom_colors: bool, }
551
552impl Default for OsConfig {
553 fn default() -> Self {
554 Self {
555 keyboard_layout: KeyboardLayout::Jis,
556 theme_name: String::from("Earth Cream"),
557 theme: PRESET_THEMES[0],
558 font_names: [
559 String::from(crate::kernel::vector_font::DEFAULT_FONT_CHAIN[0]),
560 String::from(crate::kernel::vector_font::DEFAULT_FONT_CHAIN[1]),
561 String::from(crate::kernel::vector_font::DEFAULT_FONT_CHAIN[2]),
562 ],
563 enable_sound: true,
564 swap_caps_ctrl: false,
565 auto_dhcp: true,
566 wifi_ssid: String::new(),
567 wifi_password: String::new(),
568 custom_theme: PRESET_THEMES[0],
569 enable_custom_colors: false,
570 }
571 }
572}
573
574pub static mut GLOBAL_CONFIG: Option<OsConfig> = None;
576
577#[derive(Clone, Debug)]
586pub struct ConfigLoad {
587 pub config: OsConfig,
588 pub status: Result<(), &'static str>,
589}
590
591fn record_failure(status: &mut Result<(), &'static str>, err: &'static str) {
593 if status.is_ok() {
594 *status = Err(err);
595 }
596}
597
598pub fn get_config() -> &'static OsConfig {
606 unsafe {
608 GLOBAL_CONFIG.get_or_insert_with(|| {
609 let loaded = load_config();
610 if let Err(e) = loaded.status {
611 crate::error!("[SYS] get_config: configuration load degraded: {}", e);
612 }
613 loaded.config
614 })
615 }
616}
617
618pub fn init_or_load() -> ConfigLoad {
619 let loaded = load_config();
620 unsafe {
621 GLOBAL_CONFIG = Some(loaded.config.clone());
622 }
623 loaded
624}
625
626pub fn reload_and_apply() -> Result<(), &'static str> {
627 crate::info!("[SYS] Reloading OS Configuration...");
628 let loaded = load_config();
629 let conf = loaded.config;
630 unsafe {
631 GLOBAL_CONFIG = Some(conf.clone());
632 }
633
634 let chain: alloc::vec::Vec<&str> = conf.font_names.iter().map(|s| s.as_str()).collect();
636 crate::kernel::vector_font::init_global_font_chain(&chain);
637
638 let mut wm = crate::kernel::window_mgr::get_instance();
640 wm.dirty = true;
641
642 loaded.status
645}
646
647pub fn parse_hex_color(val: &JsonValue) -> Option<u32> {
648 match val {
649 JsonValue::Number(n) => Some(*n as u32),
650 JsonValue::String(s) => {
651 let s_trimmed = s.trim_start_matches("0x").trim_start_matches('#');
652 u32::from_str_radix(s_trimmed, 16).ok()
653 }
654 _ => None,
655 }
656}
657
658fn apply_color(
664 obj: &BTreeMap<String, JsonValue>,
665 key: &'static str,
666 dst: &mut u32,
667) -> Result<(), &'static str> {
668 if let Some(v) = obj.get(key) {
669 match parse_hex_color(v) {
670 Some(c) => *dst = c,
671 None => {
672 crate::error!("[SYS] colorset: invalid color value for key '{}'", key);
673 return Err("colorset: invalid color value");
674 }
675 }
676 }
677 Ok(())
678}
679
680pub fn load_colorset_from_json(
681 json_str: &str,
682 theme: &mut ThemeConfig,
683) -> Result<(), &'static str> {
684 let parsed = json::parse(json_str).map_err(|_| "colorset: malformed JSON")?;
685 let JsonValue::Object(obj) = parsed else {
686 return Err("colorset: root is not an object");
687 };
688 {
689 let obj = &obj;
690 apply_color(obj, "desktop_bg", &mut theme.desktop_bg)?;
691 apply_color(obj, "terminal_bg", &mut theme.terminal_bg)?;
692 apply_color(obj, "terminal_fg", &mut theme.terminal_fg)?;
693 apply_color(obj, "active_window_border", &mut theme.active_window_border)?;
694 apply_color(obj, "non_active_window_border", &mut theme.non_active_window_border)?;
695 apply_color(obj, "window_title_bar", &mut theme.window_title_bar)?;
696 apply_color(obj, "window_title_fg", &mut theme.window_title_fg)?;
697 apply_color(obj, "app_bg", &mut theme.app_bg)?;
698 apply_color(obj, "app_fg", &mut theme.app_fg)?;
699 apply_color(obj, "button_bg", &mut theme.button_bg)?;
700 apply_color(obj, "button_fg", &mut theme.button_fg)?;
701 apply_color(obj, "button_active_bg", &mut theme.button_active_bg)?;
702 apply_color(obj, "button_active_fg", &mut theme.button_active_fg)?;
703 apply_color(obj, "focus_highlight", &mut theme.focus_highlight)?;
704 apply_color(obj, "mouse_cursor", &mut theme.mouse_cursor)?;
705 apply_color(obj, "mouse_stroke", &mut theme.mouse_stroke)?;
706 apply_color(obj, "login_screen_bg", &mut theme.login_screen_bg)?;
707 apply_color(obj, "login_dialog_bg", &mut theme.login_dialog_bg)?;
708 apply_color(obj, "login_dialog_shadow", &mut theme.login_dialog_shadow)?;
709 apply_color(obj, "login_dialog_border", &mut theme.login_dialog_border)?;
710 apply_color(obj, "login_text_fg", &mut theme.login_text_fg)?;
711 apply_color(obj, "login_field_bg", &mut theme.login_field_bg)?;
712 apply_color(obj, "login_field_border", &mut theme.login_field_border)?;
713 apply_color(obj, "sysmon_bg", &mut theme.sysmon_bg)?;
714 apply_color(obj, "sysmon_header_bg", &mut theme.sysmon_header_bg)?;
715 apply_color(obj, "sysmon_accent", &mut theme.sysmon_accent)?;
716 apply_color(obj, "sysmon_text", &mut theme.sysmon_text)?;
717 apply_color(obj, "sysmon_value", &mut theme.sysmon_value)?;
718 apply_color(obj, "sysmon_border", &mut theme.sysmon_border)?;
719 apply_color(obj, "tab_area_bg", &mut theme.tab_area_bg)?;
720 apply_color(obj, "tab_area_border", &mut theme.tab_area_border)?;
721 apply_color(obj, "tab_active_bg", &mut theme.tab_active_bg)?;
722 apply_color(obj, "tab_active_fg", &mut theme.tab_active_fg)?;
723 apply_color(obj, "tab_inactive_fg", &mut theme.tab_inactive_fg)?;
724 apply_color(obj, "fn_bar_bg", &mut theme.fn_bar_bg)?;
725 apply_color(obj, "fn_bar_cell_bg", &mut theme.fn_bar_cell_bg)?;
726 apply_color(obj, "fn_bar_key_bg", &mut theme.fn_bar_key_bg)?;
727 apply_color(obj, "fn_bar_key_fg", &mut theme.fn_bar_key_fg)?;
728 apply_color(obj, "fn_bar_text_fg", &mut theme.fn_bar_text_fg)?;
729 apply_color(obj, "fn_bar_dim_fg", &mut theme.fn_bar_dim_fg)?;
730 apply_color(obj, "fn_bar_border", &mut theme.fn_bar_border)?;
731 apply_color(obj, "ime_bg", &mut theme.ime_bg)?;
732 apply_color(obj, "ime_border", &mut theme.ime_border)?;
733 apply_color(obj, "ime_text", &mut theme.ime_text)?;
734 apply_color(obj, "win_btn_close", &mut theme.win_btn_close)?;
735 apply_color(obj, "win_btn_fullscreen", &mut theme.win_btn_fullscreen)?;
736 apply_color(obj, "win_btn_maximize", &mut theme.win_btn_maximize)?;
737 apply_color(obj, "win_btn_restore", &mut theme.win_btn_restore)?;
738 apply_color(obj, "win_btn_minimize", &mut theme.win_btn_minimize)?;
739 apply_color(obj, "error_fg", &mut theme.error_fg)?;
740 apply_color(obj, "success_fg", &mut theme.success_fg)?;
741 apply_color(obj, "warning_fg", &mut theme.warning_fg)?;
742 apply_color(obj, "ui_overlay_bg", &mut theme.ui_overlay_bg)?;
743 apply_color(obj, "ui_overlay_accent", &mut theme.ui_overlay_accent)?;
744 apply_color(obj, "ui_input_bg", &mut theme.ui_input_bg)?;
745 apply_color(obj, "browser_bg", &mut theme.browser_bg)?;
746 apply_color(obj, "browser_fg", &mut theme.browser_fg)?;
747 apply_color(obj, "browser_secondary", &mut theme.browser_secondary)?;
748 apply_color(obj, "login_caret", &mut theme.login_caret)?;
749 apply_color(obj, "login_hint_fg", &mut theme.login_hint_fg)?;
750 apply_color(obj, "scrollbar_thumb", &mut theme.scrollbar_thumb)?;
751 apply_color(obj, "form_input_bg", &mut theme.form_input_bg)?;
752 apply_color(obj, "form_input_fg", &mut theme.form_input_fg)?;
753 apply_color(obj, "form_input_border", &mut theme.form_input_border)?;
754 apply_color(obj, "form_input_border_focus", &mut theme.form_input_border_focus)?;
755 apply_color(obj, "form_input_accent", &mut theme.form_input_accent)?;
756 apply_color(obj, "form_checkbox_checked", &mut theme.form_checkbox_checked)?;
757 apply_color(obj, "form_checkbox_unchecked", &mut theme.form_checkbox_unchecked)?;
758 apply_color(obj, "btn_default_bg", &mut theme.btn_default_bg)?;
759 apply_color(obj, "btn_default_border", &mut theme.btn_default_border)?;
760 apply_color(obj, "btn_hover_bg", &mut theme.btn_hover_bg)?;
761 apply_color(obj, "btn_hover_border", &mut theme.btn_hover_border)?;
762 apply_color(obj, "editor_line_highlight", &mut theme.editor_line_highlight)?;
763 apply_color(obj, "editor_dim_fg", &mut theme.editor_dim_fg)?;
764 apply_color(obj, "video_bar_bg", &mut theme.video_bar_bg)?;
765 apply_color(obj, "video_bar_sep", &mut theme.video_bar_sep)?;
766 apply_color(obj, "video_progress_fg", &mut theme.video_progress_fg)?;
767 apply_color(obj, "video_thumb_fg", &mut theme.video_thumb_fg)?;
768 apply_color(obj, "privacy_badge_bg", &mut theme.privacy_badge_bg)?;
769 apply_color(obj, "privacy_badge_fg", &mut theme.privacy_badge_fg)?;
770 }
771 Ok(())
772}
773
774fn take_bool(
777 obj: &BTreeMap<String, JsonValue>,
778 key: &'static str,
779 dst: &mut bool,
780) -> Result<(), &'static str> {
781 match obj.get(key) {
782 None => Ok(()),
783 Some(JsonValue::Bool(b)) => {
784 *dst = *b;
785 Ok(())
786 }
787 Some(_) => {
788 crate::error!("[SYS] config: '{}' must be a boolean", key);
789 Err("config: value has wrong type")
790 }
791 }
792}
793
794fn take_string(
796 obj: &BTreeMap<String, JsonValue>,
797 key: &'static str,
798 dst: &mut String,
799) -> Result<(), &'static str> {
800 match obj.get(key) {
801 None => Ok(()),
802 Some(JsonValue::String(s)) => {
803 *dst = s.clone();
804 Ok(())
805 }
806 Some(_) => {
807 crate::error!("[SYS] config: '{}' must be a string", key);
808 Err("config: value has wrong type")
809 }
810 }
811}
812
813fn take_u32(
815 obj: &BTreeMap<String, JsonValue>,
816 key: &'static str,
817 dst: &mut u32,
818) -> Result<(), &'static str> {
819 match obj.get(key) {
820 None => Ok(()),
821 Some(JsonValue::Number(n)) => {
822 if !n.is_finite() || *n < 0.0 || *n > u32::MAX as f64 {
823 crate::error!("[SYS] config: '{}' is out of range", key);
824 return Err("config: numeric value out of range");
825 }
826 *dst = *n as u32;
827 Ok(())
828 }
829 Some(_) => {
830 crate::error!("[SYS] config: '{}' must be a number", key);
831 Err("config: value has wrong type")
832 }
833 }
834}
835
836fn apply_system_section(
839 system: &BTreeMap<String, JsonValue>,
840 current_config: &mut OsConfig,
841 status: &mut Result<(), &'static str>,
842) {
843 match system.get("keyboard_layout") {
845 None => {}
846 Some(JsonValue::String(layout)) => match KeyboardLayout::parse(layout) {
847 Some(l) => current_config.keyboard_layout = l,
848 None => {
849 crate::error!("[SYS] config: unknown keyboard_layout '{}'", layout);
850 record_failure(status, "config: unknown keyboard_layout");
851 }
852 },
853 Some(_) => {
854 crate::error!("[SYS] config: 'keyboard_layout' must be a string");
855 record_failure(status, "config: value has wrong type");
856 }
857 }
858
859 match system.get("custom_colors") {
861 None => {}
862 Some(JsonValue::Object(custom_colors)) => {
863 let t = &mut current_config.custom_theme;
864 let r = (|| -> Result<(), &'static str> {
865 apply_color(custom_colors, "desktop_bg", &mut t.desktop_bg)?;
866 apply_color(custom_colors, "terminal_bg", &mut t.terminal_bg)?;
867 apply_color(custom_colors, "terminal_fg", &mut t.terminal_fg)?;
868 apply_color(custom_colors, "active_window_border", &mut t.active_window_border)?;
869 apply_color(
870 custom_colors,
871 "non_active_window_border",
872 &mut t.non_active_window_border,
873 )?;
874 apply_color(custom_colors, "window_title_bar", &mut t.window_title_bar)?;
875 apply_color(custom_colors, "window_title_fg", &mut t.window_title_fg)?;
876 apply_color(custom_colors, "app_bg", &mut t.app_bg)?;
877 apply_color(custom_colors, "app_fg", &mut t.app_fg)?;
878 apply_color(custom_colors, "button_bg", &mut t.button_bg)?;
879 apply_color(custom_colors, "button_fg", &mut t.button_fg)?;
880 apply_color(custom_colors, "button_active_bg", &mut t.button_active_bg)?;
881 apply_color(custom_colors, "button_active_fg", &mut t.button_active_fg)?;
882 apply_color(custom_colors, "focus_highlight", &mut t.focus_highlight)?;
883 Ok(())
884 })();
885 if let Err(e) = r {
886 record_failure(status, e);
887 }
888 }
889 Some(_) => {
890 crate::error!("[SYS] config: 'custom_colors' must be an object");
891 record_failure(status, "config: value has wrong type");
892 }
893 }
894
895 match system.get("theme_name") {
899 None => {}
900 Some(JsonValue::String(theme_name)) => {
901 current_config.theme_name = theme_name.clone();
902 if theme_name == "Custom" {
903 current_config.theme = current_config.custom_theme;
904 } else {
905 for t in PRESET_THEMES {
906 if t.name == theme_name {
907 current_config.theme = *t;
908 break;
909 }
910 }
911 }
912 }
913 Some(_) => {
914 crate::error!("[SYS] config: 'theme_name' must be a string");
915 record_failure(status, "config: value has wrong type");
916 }
917 }
918
919 match system.get("font_names") {
921 Some(JsonValue::Array(arr)) => {
922 if arr.len() > 3 {
923 crate::error!("[SYS] config: 'font_names' has more than 3 entries");
924 record_failure(status, "config: too many font names");
925 }
926 for (i, v) in arr.iter().take(3).enumerate() {
927 match v {
928 JsonValue::String(name) if !name.is_empty() => {
929 current_config.font_names[i] = name.clone()
930 }
931 _ => {
932 crate::error!("[SYS] config: 'font_names[{}]' is not a valid name", i);
933 record_failure(status, "config: invalid font name");
934 }
935 }
936 }
937 }
938 Some(_) => {
939 crate::error!("[SYS] config: 'font_names' must be an array");
940 record_failure(status, "config: value has wrong type");
941 }
942 None => {
943 if let Err(e) = take_string(system, "font_name", &mut current_config.font_names[0]) {
944 record_failure(status, e);
945 }
946 }
947 }
948
949 if let Err(e) = take_bool(system, "enable_sound", &mut current_config.enable_sound) {
951 record_failure(status, e);
952 }
953 if let Err(e) = take_bool(
954 system,
955 "enable_custom_colors",
956 &mut current_config.enable_custom_colors,
957 ) {
958 record_failure(status, e);
959 }
960 if current_config.enable_custom_colors {
961 current_config.theme = current_config.custom_theme;
962 }
963 if let Err(e) = take_bool(system, "swap_caps_ctrl", &mut current_config.swap_caps_ctrl) {
964 record_failure(status, e);
965 }
966 if let Err(e) = take_bool(system, "auto_dhcp", &mut current_config.auto_dhcp) {
967 record_failure(status, e);
968 }
969 if let Err(e) = take_string(system, "wifi_ssid", &mut current_config.wifi_ssid) {
970 record_failure(status, e);
971 }
972 if let Err(e) = take_string(system, "wifi_password", &mut current_config.wifi_password) {
973 record_failure(status, e);
974 }
975
976 match system.get("keyrepeat") {
978 None => {}
979 Some(JsonValue::Object(repeat)) => {
980 let (mut fd, mut fi, mut sc, mut sd, mut si) = unsafe {
983 (
984 crate::REPEAT_PARAMS.first_delay_ms,
985 crate::REPEAT_PARAMS.first_interval_ms,
986 crate::REPEAT_PARAMS.second_repeat_count,
987 crate::REPEAT_PARAMS.second_delay_ms,
988 crate::REPEAT_PARAMS.second_interval_ms,
989 )
990 };
991 if let Err(e) = take_u32(repeat, "first_delay_ms", &mut fd) {
992 record_failure(status, e);
993 }
994 if let Err(e) = take_u32(repeat, "first_interval_ms", &mut fi) {
995 record_failure(status, e);
996 }
997 if let Err(e) = take_u32(repeat, "second_repeat_count", &mut sc) {
998 record_failure(status, e);
999 }
1000 if let Err(e) = take_u32(repeat, "second_delay_ms", &mut sd) {
1001 record_failure(status, e);
1002 }
1003 if let Err(e) = take_u32(repeat, "second_interval_ms", &mut si) {
1004 record_failure(status, e);
1005 }
1006 unsafe {
1007 crate::REPEAT_PARAMS.first_delay_ms = fd;
1008 crate::REPEAT_PARAMS.first_interval_ms = fi;
1009 crate::REPEAT_PARAMS.second_repeat_count = sc;
1010 crate::REPEAT_PARAMS.second_delay_ms = sd;
1011 crate::REPEAT_PARAMS.second_interval_ms = si;
1012 }
1013 }
1014 Some(_) => {
1015 crate::error!("[SYS] config: 'keyrepeat' must be an object");
1016 record_failure(status, "config: value has wrong type");
1017 }
1018 }
1019}
1020
1021pub fn load_config() -> ConfigLoad {
1026 let mut current_config = OsConfig::default();
1027 let mut status: Result<(), &'static str> = Ok(());
1028 let fs = fs::get_fs();
1029
1030 let mut config_ok = false;
1031 let mut unreadable = false;
1035 let read_result = fs.read_file_checked("/os/config.json");
1036 let config_exists = if let Some((_, content)) = match read_result {
1037 Ok(v) => v,
1038 Err(e) => {
1039 crate::error!("[SYS] config: /os/config.json could not be read: {}", e);
1040 record_failure(&mut status, e);
1041 unreadable = true;
1042 None
1043 }
1044 } {
1045 match core::str::from_utf8(&content) {
1046 Err(_) => {
1047 crate::error!("[SYS] config: /os/config.json is not valid UTF-8");
1048 }
1049 Ok(content_str) => match json::parse(content_str) {
1050 Ok(JsonValue::Object(obj)) => {
1051 config_ok = true;
1052 match obj.get("system") {
1053 Some(JsonValue::Object(system)) => {
1054 apply_system_section(system, &mut current_config, &mut status);
1055 }
1056 Some(_) => {
1057 crate::error!("[SYS] config: 'system' must be an object");
1058 record_failure(&mut status, "config: value has wrong type");
1059 }
1060 None => {
1061 crate::error!("[SYS] config: 'system' section is missing");
1062 record_failure(&mut status, "config: 'system' section is missing");
1063 }
1064 }
1065 }
1066 _ => {
1067 crate::error!("[SYS] config: /os/config.json is not a JSON object");
1068 }
1069 },
1070 }
1071 true
1072 } else {
1073 false
1074 };
1075
1076 if unreadable {
1077 crate::error!(
1080 "[SYS] config: keeping /os/config.json untouched because it could not be read"
1081 );
1082 } else if config_exists && !config_ok {
1083 crate::error!("[SYS] /os/config.json is corrupted! Renaming to config.json.bak and recovering default...");
1084 record_failure(&mut status, "config: /os/config.json is corrupted");
1085 if let Some((_, bad_content)) = fs.read_file("/os/config.json") {
1086 if let Err(e) = fs.delete_file("/os/config.json") {
1087 crate::error!("[SYS] config: failed to remove corrupted config: {}", e);
1088 record_failure(&mut status, e);
1089 }
1090 if let Err(e) = fs.save_file("/os/config.json.bak", &bad_content, "system,backup") {
1091 crate::error!("[SYS] config: failed to write config.json.bak: {}", e);
1092 record_failure(&mut status, e);
1093 }
1094 }
1095 if let Err(e) = save_config_internal(¤t_config) {
1097 record_failure(&mut status, e);
1098 }
1099 } else if !config_exists {
1100 crate::info!("[SYS] Config file not found, creating default /os/config.json");
1101 if let Err(e) = save_config_internal(¤t_config) {
1102 record_failure(&mut status, e);
1103 }
1104 }
1105
1106 let json_default = include_str!("../../colorset/default.json");
1108 let json_earth = include_str!("../../colorset/earth.json");
1109 let json_dracula = include_str!("../../colorset/dracula.json");
1110 let json_nord = include_str!("../../colorset/nord.json");
1111 let json_sakura = include_str!("../../colorset/sakura.json");
1112
1113 for (path, body) in [
1114 ("/os/default.json", json_default),
1115 ("/os/earth.json", json_earth),
1116 ("/os/dracula.json", json_dracula),
1117 ("/os/nord.json", json_nord),
1118 ("/os/sakura.json", json_sakura),
1119 ("/os/colorset.json", json_default),
1121 ] {
1122 match fs.read_file_checked(path) {
1125 Ok(None) => {
1126 if let Err(e) = fs.save_file(path, body.as_bytes(), "system,colorset") {
1127 crate::error!("[SYS] config: failed to create {}: {}", path, e);
1128 record_failure(&mut status, e);
1129 }
1130 }
1131 Ok(Some(_)) => {}
1132 Err(e) => {
1133 crate::error!("[SYS] config: {} could not be read: {}", path, e);
1134 record_failure(&mut status, e);
1135 }
1136 }
1137 }
1138
1139 let mut colorset_loaded = false;
1140 let colorset_read = match fs.read_file_checked("/os/colorset.json") {
1141 Ok(v) => v,
1142 Err(e) => {
1143 crate::error!("[SYS] config: /os/colorset.json could not be read: {}", e);
1146 record_failure(&mut status, e);
1147 return ConfigLoad {
1148 config: current_config,
1149 status,
1150 };
1151 }
1152 };
1153 if let Some((_, colorset_content)) = colorset_read {
1154 match core::str::from_utf8(&colorset_content) {
1155 Err(_) => {
1156 crate::error!("[SYS] config: /os/colorset.json is not valid UTF-8");
1157 record_failure(&mut status, "colorset: not valid UTF-8");
1158 }
1159 Ok(colorset_str) => match load_colorset_from_json(colorset_str, &mut current_config.theme) {
1160 Ok(()) => {
1161 if let Err(e) =
1162 load_colorset_from_json(colorset_str, &mut current_config.custom_theme)
1163 {
1164 record_failure(&mut status, e);
1165 }
1166 colorset_loaded = true;
1167 }
1168 Err(e) => record_failure(&mut status, e),
1169 },
1170 }
1171 }
1172
1173 if !colorset_loaded {
1174 crate::error!("[SYS] /os/colorset.json is corrupted! Renaming to colorset.json.bak and recovering default...");
1175 record_failure(&mut status, "colorset: /os/colorset.json is corrupted");
1176 if let Some((_, bad_content)) = fs.read_file("/os/colorset.json") {
1177 if let Err(e) = fs.delete_file("/os/colorset.json") {
1178 crate::error!("[SYS] config: failed to remove corrupted colorset: {}", e);
1179 record_failure(&mut status, e);
1180 }
1181 if let Err(e) = fs.save_file("/os/colorset.json.bak", &bad_content, "system,backup") {
1182 crate::error!("[SYS] config: failed to write colorset.json.bak: {}", e);
1183 record_failure(&mut status, e);
1184 }
1185 }
1186 if let Err(e) = fs.save_file("/os/colorset.json", json_default.as_bytes(), "system,colorset")
1188 {
1189 crate::error!("[SYS] config: failed to restore default colorset: {}", e);
1190 record_failure(&mut status, e);
1191 }
1192 if let Err(e) = load_colorset_from_json(json_default, &mut current_config.theme) {
1195 record_failure(&mut status, e);
1196 }
1197 if let Err(e) = load_colorset_from_json(json_default, &mut current_config.custom_theme) {
1198 record_failure(&mut status, e);
1199 }
1200 }
1201
1202 ConfigLoad {
1203 config: current_config,
1204 status,
1205 }
1206}
1207
1208pub fn validate_config(config: &OsConfig) -> Result<(), &'static str> {
1213 if config.theme_name.is_empty() {
1214 return Err("config: theme_name is empty");
1215 }
1216 for name in config.font_names.iter() {
1217 if name.is_empty() {
1218 return Err("config: font name is empty");
1219 }
1220 }
1221 Ok(())
1222}
1223
1224pub fn save_config(
1227 layout: KeyboardLayout,
1228 theme_name: &str,
1229 font_names: &[String],
1230 enable_sound: bool,
1231 swap_caps_ctrl: bool,
1232 auto_dhcp: bool,
1233 enable_custom_colors: bool,
1234) -> Result<(), &'static str> {
1235 let current_custom = get_config().custom_theme;
1236 save_config_with_wifi(
1237 layout,
1238 theme_name,
1239 font_names,
1240 enable_sound,
1241 swap_caps_ctrl,
1242 auto_dhcp,
1243 "",
1244 "",
1245 ¤t_custom,
1246 enable_custom_colors,
1247 )
1248}
1249
1250#[allow(clippy::too_many_arguments)]
1256pub fn save_config_with_wifi(
1257 layout: KeyboardLayout,
1258 theme_name: &str,
1259 font_names: &[String],
1260 enable_sound: bool,
1261 swap_caps_ctrl: bool,
1262 auto_dhcp: bool,
1263 wifi_ssid: &str,
1264 wifi_password: &str,
1265 custom_theme: &ThemeConfig,
1266 enable_custom_colors: bool,
1267) -> Result<(), &'static str> {
1268 if font_names.is_empty() {
1269 return Err("config: no font specified");
1270 }
1271 if font_names.len() > 3 {
1272 crate::error!(
1273 "[SYS] config: {} font names given (max 3)",
1274 font_names.len()
1275 );
1276 return Err("config: too many font names");
1277 }
1278
1279 let mut config = OsConfig {
1280 keyboard_layout: layout,
1281 theme_name: String::from(theme_name),
1282 custom_theme: *custom_theme,
1283 enable_custom_colors,
1284 ..Default::default()
1285 };
1286 for (i, n) in font_names.iter().enumerate() {
1287 config.font_names[i] = n.clone();
1288 }
1289 config.enable_sound = enable_sound;
1290 config.swap_caps_ctrl = swap_caps_ctrl;
1291 config.auto_dhcp = auto_dhcp;
1292 config.wifi_ssid = String::from(wifi_ssid);
1293 config.wifi_password = String::from(wifi_password);
1294
1295 if enable_custom_colors {
1296 config.theme = *custom_theme;
1297 } else {
1298 for t in PRESET_THEMES {
1299 if t.name == theme_name {
1300 config.theme = *t;
1301 break;
1302 }
1303 }
1304 }
1305
1306 validate_config(&config)?;
1307 save_config_internal(&config)
1308}
1309
1310pub fn save_config_internal(config: &OsConfig) -> Result<(), &'static str> {
1313 let mut repeat_obj = BTreeMap::new();
1314 unsafe {
1315 repeat_obj.insert(
1316 String::from("first_delay_ms"),
1317 JsonValue::Number(crate::REPEAT_PARAMS.first_delay_ms as f64),
1318 );
1319 repeat_obj.insert(
1320 String::from("first_interval_ms"),
1321 JsonValue::Number(crate::REPEAT_PARAMS.first_interval_ms as f64),
1322 );
1323 repeat_obj.insert(
1324 String::from("second_repeat_count"),
1325 JsonValue::Number(crate::REPEAT_PARAMS.second_repeat_count as f64),
1326 );
1327 repeat_obj.insert(
1328 String::from("second_delay_ms"),
1329 JsonValue::Number(crate::REPEAT_PARAMS.second_delay_ms as f64),
1330 );
1331 repeat_obj.insert(
1332 String::from("second_interval_ms"),
1333 JsonValue::Number(crate::REPEAT_PARAMS.second_interval_ms as f64),
1334 );
1335 }
1336
1337 let mut custom_obj = BTreeMap::new();
1338 custom_obj.insert(
1339 String::from("desktop_bg"),
1340 JsonValue::Number(config.custom_theme.desktop_bg as f64),
1341 );
1342 custom_obj.insert(
1343 String::from("terminal_bg"),
1344 JsonValue::Number(config.custom_theme.terminal_bg as f64),
1345 );
1346 custom_obj.insert(
1347 String::from("terminal_fg"),
1348 JsonValue::Number(config.custom_theme.terminal_fg as f64),
1349 );
1350 custom_obj.insert(
1351 String::from("active_window_border"),
1352 JsonValue::Number(config.custom_theme.active_window_border as f64),
1353 );
1354 custom_obj.insert(
1355 String::from("non_active_window_border"),
1356 JsonValue::Number(config.custom_theme.non_active_window_border as f64),
1357 );
1358 custom_obj.insert(
1359 String::from("window_title_bar"),
1360 JsonValue::Number(config.custom_theme.window_title_bar as f64),
1361 );
1362 custom_obj.insert(
1363 String::from("app_bg"),
1364 JsonValue::Number(config.custom_theme.app_bg as f64),
1365 );
1366 custom_obj.insert(
1367 String::from("app_fg"),
1368 JsonValue::Number(config.custom_theme.app_fg as f64),
1369 );
1370 custom_obj.insert(
1371 String::from("button_bg"),
1372 JsonValue::Number(config.custom_theme.button_bg as f64),
1373 );
1374 custom_obj.insert(
1375 String::from("button_fg"),
1376 JsonValue::Number(config.custom_theme.button_fg as f64),
1377 );
1378 custom_obj.insert(
1379 String::from("button_active_bg"),
1380 JsonValue::Number(config.custom_theme.button_active_bg as f64),
1381 );
1382 custom_obj.insert(
1383 String::from("button_active_fg"),
1384 JsonValue::Number(config.custom_theme.button_active_fg as f64),
1385 );
1386 custom_obj.insert(
1387 String::from("focus_highlight"),
1388 JsonValue::Number(config.custom_theme.focus_highlight as f64),
1389 );
1390
1391 let mut system_obj = BTreeMap::new();
1392 let layout_str = match config.keyboard_layout {
1393 KeyboardLayout::Us => "us",
1394 KeyboardLayout::Jis => "jis",
1395 };
1396 system_obj.insert(
1397 String::from("keyboard_layout"),
1398 JsonValue::String(String::from(layout_str)),
1399 );
1400 system_obj.insert(
1401 String::from("theme_name"),
1402 JsonValue::String(config.theme_name.clone()),
1403 );
1404 system_obj.insert(
1405 String::from("font_names"),
1406 JsonValue::Array(
1407 config
1408 .font_names
1409 .iter()
1410 .map(|n| JsonValue::String(n.clone()))
1411 .collect(),
1412 ),
1413 );
1414 system_obj.insert(
1415 String::from("enable_sound"),
1416 JsonValue::Bool(config.enable_sound),
1417 );
1418 system_obj.insert(
1419 String::from("swap_caps_ctrl"),
1420 JsonValue::Bool(config.swap_caps_ctrl),
1421 );
1422 system_obj.insert(String::from("auto_dhcp"), JsonValue::Bool(config.auto_dhcp));
1423 system_obj.insert(
1424 String::from("enable_custom_colors"),
1425 JsonValue::Bool(config.enable_custom_colors),
1426 );
1427 system_obj.insert(
1428 String::from("wifi_ssid"),
1429 JsonValue::String(config.wifi_ssid.clone()),
1430 );
1431 system_obj.insert(
1432 String::from("wifi_password"),
1433 JsonValue::String(config.wifi_password.clone()),
1434 );
1435 system_obj.insert(String::from("keyrepeat"), JsonValue::Object(repeat_obj));
1436 system_obj.insert(String::from("custom_colors"), JsonValue::Object(custom_obj));
1437
1438 let mut root_obj = BTreeMap::new();
1439 root_obj.insert(String::from("system"), JsonValue::Object(system_obj));
1440
1441 let json_str = json::to_string(&JsonValue::Object(root_obj));
1442 let fs = fs::get_fs();
1443 if let Err(e) = fs.save_file("/os/config.json", json_str.as_bytes(), "system,config") {
1444 crate::error!("[SYS] config: failed to write /os/config.json: {}", e);
1445 return Err(e);
1446 }
1447 Ok(())
1448}