Skip to main content

strat9_kernel/arch/x86_64/
keyboard_layout.rs

1//! Keyboard Layout Configuration
2//!
3//! Owns the shared keyboard modifier state and scancode processing logic.
4//! Layout-specific modules (keyboard.rs, keyboard_us.rs) only provide
5//! scancode-to-ASCII lookup tables.
6
7use core::sync::atomic::{AtomicBool, Ordering};
8use spin::Mutex;
9
10/// French AZERTY scancode set 1 -> ASCII mapping (lowercase)
11pub static AZERTY_NORMAL: [u8; 128] = {
12    let mut t = [0u8; 128];
13    t[0x01] = 0x1B;
14    t[0x02] = b'&';
15    t[0x03] = b'e';
16    t[0x04] = b'"';
17    t[0x05] = b'\'';
18    t[0x06] = b'(';
19    t[0x07] = b'-';
20    t[0x08] = b'e';
21    t[0x09] = b'_';
22    t[0x0A] = b'c';
23    t[0x0B] = b'a';
24    t[0x0C] = b')';
25    t[0x0D] = b'+';
26    t[0x0E] = 0x08;
27    t[0x0F] = b'\t';
28    t[0x10] = b'a';
29    t[0x11] = b'z';
30    t[0x12] = b'e';
31    t[0x13] = b'r';
32    t[0x14] = b't';
33    t[0x15] = b'y';
34    t[0x16] = b'u';
35    t[0x17] = b'i';
36    t[0x18] = b'o';
37    t[0x19] = b'p';
38    t[0x1A] = b'^';
39    t[0x1B] = b'$';
40    t[0x1C] = b'\n';
41    t[0x1E] = b'q';
42    t[0x1F] = b's';
43    t[0x20] = b'd';
44    t[0x21] = b'f';
45    t[0x22] = b'g';
46    t[0x23] = b'h';
47    t[0x24] = b'j';
48    t[0x25] = b'k';
49    t[0x26] = b'l';
50    t[0x27] = b'm';
51    t[0x28] = b'u';
52    t[0x29] = b'*';
53    t[0x2A] = 0x00;
54    t[0x2B] = b'<';
55    t[0x2C] = b'w';
56    t[0x2D] = b'x';
57    t[0x2E] = b'c';
58    t[0x2F] = b'v';
59    t[0x30] = b'b';
60    t[0x31] = b'n';
61    t[0x32] = b',';
62    t[0x33] = b';';
63    t[0x34] = b'.';
64    t[0x35] = b'/';
65    t[0x36] = 0x00;
66    t[0x37] = b'*';
67    t[0x38] = 0x00;
68    t[0x39] = b' ';
69    t[0x3A] = 0x00;
70    t[0x56] = b'<';
71    t[0x47] = b'7';
72    t[0x48] = b'8';
73    t[0x49] = b'9';
74    t[0x4A] = b'-';
75    t[0x4B] = b'4';
76    t[0x4C] = b'5';
77    t[0x4D] = b'6';
78    t[0x4E] = b'+';
79    t[0x4F] = b'1';
80    t[0x50] = b'2';
81    t[0x51] = b'3';
82    t[0x52] = b'0';
83    t[0x53] = b'.';
84    t
85};
86
87/// French AZERTY shifted scancode -> ASCII mapping
88pub static AZERTY_SHIFT: [u8; 128] = {
89    let mut t = [0u8; 128];
90    t[0x01] = 0x1B;
91    t[0x02] = b'1';
92    t[0x03] = b'2';
93    t[0x04] = b'3';
94    t[0x05] = b'4';
95    t[0x06] = b'5';
96    t[0x07] = b'6';
97    t[0x08] = b'7';
98    t[0x09] = b'8';
99    t[0x0A] = b'9';
100    t[0x0B] = b'0';
101    t[0x0C] = b')';
102    t[0x0D] = b'+';
103    t[0x0E] = 0x08;
104    t[0x0F] = b'\t';
105    t[0x10] = b'A';
106    t[0x11] = b'Z';
107    t[0x12] = b'E';
108    t[0x13] = b'R';
109    t[0x14] = b'T';
110    t[0x15] = b'Y';
111    t[0x16] = b'U';
112    t[0x17] = b'I';
113    t[0x18] = b'O';
114    t[0x19] = b'P';
115    t[0x1A] = b'l';
116    t[0x1B] = b'*';
117    t[0x1C] = b'\n';
118    t[0x1E] = b'Q';
119    t[0x1F] = b'S';
120    t[0x20] = b'D';
121    t[0x21] = b'F';
122    t[0x22] = b'G';
123    t[0x23] = b'H';
124    t[0x24] = b'J';
125    t[0x25] = b'K';
126    t[0x26] = b'L';
127    t[0x27] = b'M';
128    t[0x28] = b'%';
129    t[0x29] = b'u';
130    t[0x2B] = b'>';
131    t[0x2C] = b'W';
132    t[0x2D] = b'X';
133    t[0x2E] = b'C';
134    t[0x2F] = b'V';
135    t[0x30] = b'B';
136    t[0x31] = b'N';
137    t[0x32] = b'?';
138    t[0x33] = b'.';
139    t[0x34] = b':';
140    t[0x35] = b'/';
141    t[0x36] = 0x00;
142    t[0x37] = b'*';
143    t[0x38] = 0x00;
144    t[0x39] = b' ';
145    t[0x3A] = 0x00;
146    t[0x56] = b'>';
147    t[0x47] = b'7';
148    t[0x48] = b'8';
149    t[0x49] = b'9';
150    t[0x4A] = b'-';
151    t[0x4B] = b'4';
152    t[0x4C] = b'5';
153    t[0x4D] = b'6';
154    t[0x4E] = b'+';
155    t[0x4F] = b'1';
156    t[0x50] = b'2';
157    t[0x51] = b'3';
158    t[0x52] = b'0';
159    t[0x53] = b'.';
160    t
161};
162
163/// US QWERTY scancode set 1 -> ASCII mapping (lowercase)
164pub static QWERTY_NORMAL: [u8; 128] = {
165    let mut t = [0u8; 128];
166    t[0x01] = 0x1B;
167    t[0x02] = b'1';
168    t[0x03] = b'2';
169    t[0x04] = b'3';
170    t[0x05] = b'4';
171    t[0x06] = b'5';
172    t[0x07] = b'6';
173    t[0x08] = b'7';
174    t[0x09] = b'8';
175    t[0x0A] = b'9';
176    t[0x0B] = b'0';
177    t[0x0C] = b'-';
178    t[0x0D] = b'=';
179    t[0x0E] = 0x08;
180    t[0x0F] = b'\t';
181    t[0x10] = b'q';
182    t[0x11] = b'w';
183    t[0x12] = b'e';
184    t[0x13] = b'r';
185    t[0x14] = b't';
186    t[0x15] = b'y';
187    t[0x16] = b'u';
188    t[0x17] = b'i';
189    t[0x18] = b'o';
190    t[0x19] = b'p';
191    t[0x1A] = b'[';
192    t[0x1B] = b']';
193    t[0x1C] = b'\n';
194    t[0x1E] = b'a';
195    t[0x1F] = b's';
196    t[0x20] = b'd';
197    t[0x21] = b'f';
198    t[0x22] = b'g';
199    t[0x23] = b'h';
200    t[0x24] = b'j';
201    t[0x25] = b'k';
202    t[0x26] = b'l';
203    t[0x27] = b';';
204    t[0x28] = b'\'';
205    t[0x29] = b'`';
206    t[0x2A] = 0x00;
207    t[0x2B] = b'\\';
208    t[0x2C] = b'z';
209    t[0x2D] = b'x';
210    t[0x2E] = b'c';
211    t[0x2F] = b'v';
212    t[0x30] = b'b';
213    t[0x31] = b'n';
214    t[0x32] = b'm';
215    t[0x33] = b',';
216    t[0x34] = b'.';
217    t[0x35] = b'/';
218    t[0x36] = 0x00;
219    t[0x37] = b'*';
220    t[0x38] = 0x00;
221    t[0x39] = b' ';
222    t[0x3A] = 0x00;
223    t[0x47] = b'7';
224    t[0x48] = b'8';
225    t[0x49] = b'9';
226    t[0x4A] = b'-';
227    t[0x4B] = b'4';
228    t[0x4C] = b'5';
229    t[0x4D] = b'6';
230    t[0x4E] = b'+';
231    t[0x4F] = b'1';
232    t[0x50] = b'2';
233    t[0x51] = b'3';
234    t[0x52] = b'0';
235    t[0x53] = b'.';
236    t
237};
238
239/// US QWERTY shifted scancode -> ASCII mapping
240pub static QWERTY_SHIFT: [u8; 128] = {
241    let mut t = [0u8; 128];
242    t[0x01] = 0x1B;
243    t[0x02] = b'!';
244    t[0x03] = b'@';
245    t[0x04] = b'#';
246    t[0x05] = b'$';
247    t[0x06] = b'%';
248    t[0x07] = b'^';
249    t[0x08] = b'&';
250    t[0x09] = b'*';
251    t[0x0A] = b'(';
252    t[0x0B] = b')';
253    t[0x0C] = b'_';
254    t[0x0D] = b'+';
255    t[0x0E] = 0x08;
256    t[0x0F] = b'\t';
257    t[0x10] = b'Q';
258    t[0x11] = b'W';
259    t[0x12] = b'E';
260    t[0x13] = b'R';
261    t[0x14] = b'T';
262    t[0x15] = b'Y';
263    t[0x16] = b'U';
264    t[0x17] = b'I';
265    t[0x18] = b'O';
266    t[0x19] = b'P';
267    t[0x1A] = b'{';
268    t[0x1B] = b'}';
269    t[0x1C] = b'\n';
270    t[0x1E] = b'A';
271    t[0x1F] = b'S';
272    t[0x20] = b'D';
273    t[0x21] = b'F';
274    t[0x22] = b'G';
275    t[0x23] = b'H';
276    t[0x24] = b'J';
277    t[0x25] = b'K';
278    t[0x26] = b'L';
279    t[0x27] = b':';
280    t[0x28] = b'"';
281    t[0x29] = b'~';
282    t[0x2B] = b'|';
283    t[0x2C] = b'Z';
284    t[0x2D] = b'X';
285    t[0x2E] = b'C';
286    t[0x2F] = b'V';
287    t[0x30] = b'B';
288    t[0x31] = b'N';
289    t[0x32] = b'M';
290    t[0x33] = b'<';
291    t[0x34] = b'>';
292    t[0x35] = b'?';
293    t[0x36] = 0x00;
294    t[0x37] = b'*';
295    t[0x38] = 0x00;
296    t[0x39] = b' ';
297    t[0x3A] = 0x00;
298    t[0x47] = b'7';
299    t[0x48] = b'8';
300    t[0x49] = b'9';
301    t[0x4A] = b'-';
302    t[0x4B] = b'4';
303    t[0x4C] = b'5';
304    t[0x4D] = b'6';
305    t[0x4E] = b'+';
306    t[0x4F] = b'1';
307    t[0x50] = b'2';
308    t[0x51] = b'3';
309    t[0x52] = b'0';
310    t[0x53] = b'.';
311    t
312};
313
314// Flag to determine which keyboard layout to use
315// true = French AZERTY, false = US QWERTY
316static USE_FRENCH_LAYOUT: AtomicBool = AtomicBool::new(true);
317
318/// Set the keyboard layout to French AZERTY
319pub fn set_french_layout() {
320    USE_FRENCH_LAYOUT.store(true, Ordering::Relaxed);
321}
322
323/// Set the keyboard layout to US QWERTY
324pub fn set_us_layout() {
325    USE_FRENCH_LAYOUT.store(false, Ordering::Relaxed);
326}
327
328/// Get the current keyboard layout setting
329pub fn is_french_layout() -> bool {
330    USE_FRENCH_LAYOUT.load(Ordering::Relaxed)
331}
332
333/// Keyboard modifier state : shared across all layouts.
334pub struct KeyboardState {
335    pub left_shift: bool,
336    pub right_shift: bool,
337    pub caps_lock: bool,
338    pub ctrl: bool,
339    pub alt: bool,
340    pub extended_scancode: bool,
341}
342
343impl KeyboardState {
344    pub const fn new() -> Self {
345        Self {
346            left_shift: false,
347            right_shift: false,
348            caps_lock: false,
349            ctrl: false,
350            alt: false,
351            extended_scancode: false,
352        }
353    }
354
355    pub fn shift_active(&self) -> bool {
356        self.left_shift || self.right_shift
357    }
358}
359
360/// Global keyboard modifier state : single instance shared by all layouts.
361pub static KEYBOARD: Mutex<KeyboardState> = Mutex::new(KeyboardState::new());
362
363/// Special key constants (non-ASCII, outside 0-127)
364pub const KEY_UP: u8 = 0x80;
365pub const KEY_DOWN: u8 = 0x81;
366pub const KEY_LEFT: u8 = 0x82;
367pub const KEY_RIGHT: u8 = 0x83;
368pub const KEY_HOME: u8 = 0x84;
369pub const KEY_END: u8 = 0x85;
370
371/// Process a single scancode byte with the given lookup tables.
372///
373/// Handles modifier tracking, extended scancode prefix (0xE0), caps lock
374/// inversion, and extended key dispatch (arrows, Home, End, Delete).
375/// Called from both PS/2 IRQ and task context (inject_hid_scancode).
376pub fn process_scancode(scancode: u8, normal: &[u8; 128], shift: &[u8; 128]) -> Option<u8> {
377    let mut kbd = KEYBOARD.lock();
378
379    // Extended scancode prefix (0xE0)
380    if scancode == 0xE0 {
381        kbd.extended_scancode = true;
382        return None;
383    }
384
385    let is_extended = kbd.extended_scancode;
386    kbd.extended_scancode = false;
387
388    // Key release (bit 7 set)
389    if scancode & 0x80 != 0 {
390        match scancode & 0x7F {
391            0x2A => kbd.left_shift = false,
392            0x36 => kbd.right_shift = false,
393            0x1D => kbd.ctrl = false,
394            0x38 => kbd.alt = false,
395            _ => {}
396        }
397        return None;
398    }
399
400    // Modifier key presses
401    match scancode {
402        0x2A => {
403            kbd.left_shift = true;
404            return None;
405        }
406        0x36 => {
407            kbd.right_shift = true;
408            return None;
409        }
410        0x1D => {
411            kbd.ctrl = true;
412            return None;
413        }
414        0x38 => {
415            kbd.alt = true;
416            return None;
417        }
418        0x3A => {
419            kbd.caps_lock = !kbd.caps_lock;
420            return None;
421        }
422        _ => {}
423    }
424
425    // Extended keys (arrow keys, Home, End, Delete sent as 0xE0 + scancode)
426    if is_extended {
427        return match scancode {
428            0x48 => Some(KEY_UP),
429            0x50 => Some(KEY_DOWN),
430            0x4B => Some(KEY_LEFT),
431            0x4D => Some(KEY_RIGHT),
432            0x47 => Some(KEY_HOME),
433            0x4F => Some(KEY_END),
434            0x53 => Some(b'\x7F'),
435            _ => None,
436        };
437    }
438
439    // Regular key: lookup in tables, apply caps lock
440    if scancode < 128 {
441        let shift_active = kbd.shift_active();
442        let ch = if shift_active {
443            shift[scancode as usize]
444        } else {
445            normal[scancode as usize]
446        };
447
448        if kbd.caps_lock && ch.is_ascii_alphabetic() {
449            let ch = if shift_active {
450                ch.to_ascii_lowercase()
451            } else {
452                ch.to_ascii_uppercase()
453            };
454            if ch != 0 {
455                return Some(ch);
456            }
457        }
458
459        if ch != 0 {
460            return Some(ch);
461        }
462    }
463
464    None
465}
466
467/// Read port 0x60 and process the scancode with the current layout tables.
468pub fn handle_scancode() -> Option<u8> {
469    let scancode = unsafe { super::io::inb(super::keyboard::KEYBOARD_DATA_PORT) };
470    handle_scancode_raw(scancode)
471}
472
473/// Process a pre-read scancode byte with the current layout tables.
474pub fn handle_scancode_raw(scancode: u8) -> Option<u8> {
475    if is_french_layout() {
476        process_scancode(scancode, &AZERTY_NORMAL, &AZERTY_SHIFT)
477    } else {
478        process_scancode(scancode, &QWERTY_NORMAL, &QWERTY_SHIFT)
479    }
480}