| 1 | //! Keyboard input handling for the greeter |
| 2 | //! |
| 3 | //! Converts X11 keycodes to characters with basic shift support. |
| 4 | |
| 5 | use x11rb::protocol::xproto::KeyButMask; |
| 6 | |
| 7 | /// Common X11 keycodes (evdev-based, typical for modern Linux) |
| 8 | pub mod keycodes { |
| 9 | pub const ESCAPE: u8 = 9; |
| 10 | pub const BACKSPACE: u8 = 22; |
| 11 | pub const TAB: u8 = 23; |
| 12 | pub const RETURN: u8 = 36; |
| 13 | pub const SHIFT_L: u8 = 50; |
| 14 | pub const SHIFT_R: u8 = 62; |
| 15 | pub const CAPS_LOCK: u8 = 66; |
| 16 | pub const LEFT: u8 = 113; |
| 17 | pub const UP: u8 = 111; |
| 18 | pub const RIGHT: u8 = 114; |
| 19 | pub const DOWN: u8 = 116; |
| 20 | pub const HOME: u8 = 110; |
| 21 | pub const END: u8 = 115; |
| 22 | pub const DELETE: u8 = 119; |
| 23 | } |
| 24 | |
| 25 | /// Convert a keycode to a character, considering shift and caps lock state |
| 26 | pub fn keycode_to_char(keycode: u8, state: KeyButMask) -> Option<char> { |
| 27 | let shift_pressed = state.contains(KeyButMask::SHIFT); |
| 28 | let caps_lock_on = state.contains(KeyButMask::LOCK); |
| 29 | |
| 30 | // Main alphanumeric keys (evdev keycodes) |
| 31 | let base = match keycode { |
| 32 | // Number row |
| 33 | 10 => '1', |
| 34 | 11 => '2', |
| 35 | 12 => '3', |
| 36 | 13 => '4', |
| 37 | 14 => '5', |
| 38 | 15 => '6', |
| 39 | 16 => '7', |
| 40 | 17 => '8', |
| 41 | 18 => '9', |
| 42 | 19 => '0', |
| 43 | 20 => '-', |
| 44 | 21 => '=', |
| 45 | |
| 46 | // Top row (QWERTY) |
| 47 | 24 => 'q', |
| 48 | 25 => 'w', |
| 49 | 26 => 'e', |
| 50 | 27 => 'r', |
| 51 | 28 => 't', |
| 52 | 29 => 'y', |
| 53 | 30 => 'u', |
| 54 | 31 => 'i', |
| 55 | 32 => 'o', |
| 56 | 33 => 'p', |
| 57 | 34 => '[', |
| 58 | 35 => ']', |
| 59 | |
| 60 | // Home row (ASDF) |
| 61 | 38 => 'a', |
| 62 | 39 => 's', |
| 63 | 40 => 'd', |
| 64 | 41 => 'f', |
| 65 | 42 => 'g', |
| 66 | 43 => 'h', |
| 67 | 44 => 'j', |
| 68 | 45 => 'k', |
| 69 | 46 => 'l', |
| 70 | 47 => ';', |
| 71 | 48 => '\'', |
| 72 | 51 => '\\', |
| 73 | |
| 74 | // Bottom row (ZXCV) |
| 75 | 52 => 'z', |
| 76 | 53 => 'x', |
| 77 | 54 => 'c', |
| 78 | 55 => 'v', |
| 79 | 56 => 'b', |
| 80 | 57 => 'n', |
| 81 | 58 => 'm', |
| 82 | 59 => ',', |
| 83 | 60 => '.', |
| 84 | 61 => '/', |
| 85 | |
| 86 | // Space |
| 87 | 65 => ' ', |
| 88 | |
| 89 | // Grave/tilde |
| 90 | 49 => '`', |
| 91 | |
| 92 | _ => return None, |
| 93 | }; |
| 94 | |
| 95 | // For letters: uppercase if shift XOR caps_lock (one but not both) |
| 96 | // For symbols: only shift matters |
| 97 | let c = if base.is_ascii_lowercase() { |
| 98 | // Letters: shift XOR caps_lock determines case |
| 99 | if shift_pressed != caps_lock_on { |
| 100 | base.to_ascii_uppercase() |
| 101 | } else { |
| 102 | base |
| 103 | } |
| 104 | } else if shift_pressed { |
| 105 | // Non-letters: only shift affects them |
| 106 | match base { |
| 107 | // Numbers to symbols |
| 108 | '1' => '!', |
| 109 | '2' => '@', |
| 110 | '3' => '#', |
| 111 | '4' => '$', |
| 112 | '5' => '%', |
| 113 | '6' => '^', |
| 114 | '7' => '&', |
| 115 | '8' => '*', |
| 116 | '9' => '(', |
| 117 | '0' => ')', |
| 118 | '-' => '_', |
| 119 | '=' => '+', |
| 120 | '[' => '{', |
| 121 | ']' => '}', |
| 122 | ';' => ':', |
| 123 | '\'' => '"', |
| 124 | '\\' => '|', |
| 125 | ',' => '<', |
| 126 | '.' => '>', |
| 127 | '/' => '?', |
| 128 | '`' => '~', |
| 129 | c => c, |
| 130 | } |
| 131 | } else { |
| 132 | base |
| 133 | }; |
| 134 | |
| 135 | Some(c) |
| 136 | } |
| 137 |