Skip to main content

atmos/apps/keymap/
mod.rs

1//! キーボードチェッカーアプリ。
2//!
3//! 押下されたキーのスキャンコード・ASCII 変換結果・直近の入力履歴を画面表示し、
4//! キーマップ(JIS/US 等)設定の動作確認に使う。実際のレイアウト設定は
5//! [`crate::apps::settings`] が持つ(本アプリは表示・確認専用)。
6// src/keymap_app.rs - Keymap Settings and Keyboard Checker App for AtmOS
7#![allow(dead_code)]
8
9extern crate alloc;
10use alloc::string::String;
11
12use crate::kernel::config::{self, KeyboardLayout};
13use crate::kernel::draw::{Color, Screen};
14use crate::kernel::window_mgr::App;
15
16pub struct KeymapApp {
17    last_keycode: u8,
18    last_ascii: Option<char>,
19    is_pressed: bool,
20    history: alloc::vec::Vec<String>,
21}
22
23impl Default for KeymapApp {
24    fn default() -> Self {
25        Self::new()
26    }
27}
28
29impl KeymapApp {
30    pub fn new() -> Self {
31        Self {
32            last_keycode: 0,
33            last_ascii: None,
34            is_pressed: false,
35            history: alloc::vec::Vec::new(),
36        }
37    }
38
39    fn push_history(&mut self, ev_str: String) {
40        self.history.push(ev_str);
41        if self.history.len() > 10 {
42            self.history.remove(0);
43        }
44    }
45}
46
47fn keycode_to_name(keycode: u8, ascii: Option<char>) -> String {
48    match keycode {
49        0x01 => String::from("Esc"),
50        0x0E => String::from("Backspace"),
51        0x0F => String::from("Tab"),
52        0x1C => String::from("Enter"),
53        0x1D => String::from("LCtrl"),
54        0x2A => String::from("LShift"),
55        0x36 => String::from("RShift"),
56        0x38 => String::from("Alt/Option"),
57        0x5B => String::from("Command"),
58        0x39 => String::from("Space"),
59        0x3A => String::from("CapsLock"),
60        0x3B => String::from("F1"),
61        0x3C => String::from("F2"),
62        0x3D => String::from("F3"),
63        0x3E => String::from("F4"),
64        0x3F => String::from("F5"),
65        0x40 => String::from("F6"),
66        0x41 => String::from("F7"),
67        0x42 => String::from("F8"),
68        0x43 => String::from("F9"),
69        0x44 => String::from("F10"),
70        0x57 => String::from("F11"),
71        0x58 => String::from("F12"),
72        0x47 => String::from("Home"),
73        0x48 => String::from("Up"),
74        0x4B => String::from("Left"),
75        0x4D => String::from("Right"),
76        0x4F => String::from("End"),
77        0x50 => String::from("Down"),
78        0x53 => String::from("Delete"),
79        _ => {
80            if let Some(c) = ascii {
81                // タブや改行などの不可視文字のフォールバック
82                match c {
83                    '\t' => String::from("Tab"),
84                    '\n' => String::from("Enter"),
85                    '\x08' => String::from("Backspace"),
86                    ' ' => String::from("Space"),
87                    _ => alloc::format!("'{}'", c),
88                }
89            } else {
90                String::from("None")
91            }
92        }
93    }
94}
95
96impl App for KeymapApp {
97    fn name(&self) -> &str {
98        "Keymap & Checker"
99    }
100
101    fn draw(&mut self, screen: &Screen, win_x: u32, win_y: u32, win_w: u32, win_h: u32) {
102        let config = config::get_config();
103
104        // 1. 全体背景
105        screen.boxfill(
106            win_x,
107            win_y,
108            win_x + win_w,
109            win_y + win_h,
110            Color(config.theme.terminal_bg),
111        );
112
113        // 2. ヘッダー
114        screen.draw_string_vector(
115            win_x + 15,
116            win_y + 15,
117            "KEYBOARD CHECKER",
118            Color(config.theme.success_fg),
119            18,
120        );
121        screen.boxfill(
122            win_x + 15,
123            win_y + 38,
124            win_x + win_w - 15,
125            win_y + 39,
126            Color(config.theme.tab_area_border),
127        );
128
129        // 3. 現在の設定表示
130        let layout_str = match config.keyboard_layout {
131            KeyboardLayout::Jis => "JIS (106/109)",
132            KeyboardLayout::Us => "US (101/104)",
133        };
134        let caps_str = if config.swap_caps_ctrl {
135            "Ctrl"
136        } else {
137            "CapsLock"
138        };
139
140        screen.draw_string_vector(
141            win_x + 15,
142            win_y + 55,
143            "Current Layout:",
144            Color(config.theme.fn_bar_key_fg),
145            14,
146        );
147        screen.draw_string_vector(win_x + 150, win_y + 55, layout_str, Color(config.theme.terminal_fg), 14);
148
149        screen.draw_string_vector(
150            win_x + 15,
151            win_y + 80,
152            "CapsLock as:",
153            Color(config.theme.fn_bar_key_fg),
154            14,
155        );
156        screen.draw_string_vector(win_x + 150, win_y + 80, caps_str, Color(config.theme.terminal_fg), 14);
157
158        // 4. チェッカー領域
159        screen.draw_string_vector(
160            win_x + 15,
161            win_y + 120,
162            "Live Keyboard Event:",
163            Color(config.theme.warning_fg),
164            16,
165        );
166
167        // 大きな表示ボックス
168        let box_y = win_y + 145;
169        screen.boxfill(
170            win_x + 15,
171            box_y,
172            win_x + win_w - 15,
173            box_y + 100,
174            Color(config.theme.ui_overlay_bg),
175        );
176        screen.boxfill(
177            win_x + 15,
178            box_y,
179            win_x + win_w - 15,
180            box_y + 2,
181            Color(config.theme.sysmon_accent),
182        ); // 上部ハイライト
183
184        if self.last_keycode != 0 {
185            let state_str = if self.is_pressed {
186                "PRESSED "
187            } else {
188                "RELEASED"
189            };
190            let state_color = if self.is_pressed {
191                Color(config.theme.error_fg)
192            } else {
193                Color(config.theme.fn_bar_key_fg)
194            };
195
196            screen.draw_string_vector(win_x + 30, box_y + 25, "State:", Color(config.theme.fn_bar_dim_fg), 14);
197            screen.draw_string_vector(win_x + 90, box_y + 25, state_str, state_color, 14);
198
199            let code_str = alloc::format!("0x{:02X} ({})", self.last_keycode, self.last_keycode);
200            screen.draw_string_vector(win_x + 30, box_y + 50, "Code:", Color(config.theme.fn_bar_dim_fg), 14);
201            screen.draw_string_vector(win_x + 90, box_y + 50, &code_str, Color(config.theme.terminal_fg), 14);
202
203            let char_str = keycode_to_name(self.last_keycode, self.last_ascii);
204            screen.draw_string_vector(win_x + 200, box_y + 50, "Key:", Color(config.theme.fn_bar_dim_fg), 14);
205            screen.draw_string_vector(win_x + 240, box_y + 50, &char_str, Color(config.theme.tab_active_fg), 14);
206        } else {
207            screen.draw_string_vector(
208                win_x + 30,
209                box_y + 40,
210                "Press any key to test...",
211                Color(config.theme.fn_bar_dim_fg),
212                14,
213            );
214        }
215
216        // 5. 履歴表示
217        screen.draw_string_vector(
218            win_x + 15,
219            win_y + 270,
220            "Event History:",
221            Color(config.theme.success_fg),
222            14,
223        );
224        let mut hist_y = win_y + 295;
225        for hist in self.history.iter().rev() {
226            screen.draw_string_vector(win_x + 25, hist_y, hist, Color(config.theme.terminal_fg), 12);
227            hist_y += 18;
228        }
229    }
230
231    fn on_mouse(
232        &mut self,
233        _local_x: i32,
234        _local_y: i32,
235        _btn_left: bool,
236        _btn_right: bool,
237        _wheel: i32,
238    ) {
239        // ボタンなどはないので何もしない
240    }
241
242    fn on_key(&mut self, keycode: u8, pressed: bool, ascii: Option<char>) {
243        self.last_keycode = keycode;
244        self.is_pressed = pressed;
245        self.last_ascii = ascii;
246
247        let state_str = if pressed { "DOWN" } else { "UP  " };
248        let char_str = keycode_to_name(keycode, ascii);
249        let hist_str = alloc::format!(
250            "[{}] Code: 0x{:02X} | Key: {}",
251            state_str,
252            keycode,
253            char_str
254        );
255        self.push_history(hist_str);
256    }
257}