Горещи клавиши
Модулът hotkeys/ имплементира глобално прихващане на клавишни комбинации за управление на диктовката. Използва GetAsyncKeyState polling вместо RegisterHotKey или low-level keyboard hooks за по-надеждна работа с elevated приложения.
Архитектура
HotkeyEvent enum
pub enum HotkeyEvent {
DictationStart, // Hold mode: натискане
DictationStop, // Hold mode: пускане
DictationToggle, // Toggle mode: натискане
TranslateStart, // Hold mode: натискане (translate hotkey)
TranslateStop, // Hold mode: пускане
TranslateToggle, // Toggle mode: натискане
Quit, // Ctrl+Shift+Q (hardcoded)
}
Режими на диктовка
| Режим | Поведение |
|---|---|
| Hold (по подразбиране) | Задръж клавишите за запис, пусни за спиране. Изпраща DictationStart при натискане и DictationStop при пускане. |
| Toggle | Натисни веднъж за старт, натисни отново за стоп. Изпраща DictationToggle. |
Polling механизъм
Защо polling вместо hooks
- RegisterHotKey: Не работи когато целевото приложение е elevated (admin)
- Low-level keyboard hooks (WH_KEYBOARD_LL): Сложна имплементация, изисква message pump
- GetAsyncKeyState polling: Работи независимо от привилегиите на приложенията, прост за имплементация
Edge detection
Polling-ът отчита промяна на състоянието (edge), не просто натиснато (level):
if pressed && !was_pressed {
// Rising edge: клавишът току-що е натиснат
tx.send(DictationStart);
} else if !pressed && was_pressed && mode == Hold {
// Falling edge: клавишът е пуснат
tx.send(DictationStop);
}
was_pressed = pressed;
HotkeyConfig
pub struct HotkeyConfig {
pub required_modifiers: Vec<Modifier>, // Ctrl, Shift, Alt, Win
pub main_key: MainKey, // Keyboard(vk) или Mouse(btn)
pub mode: DictationMode, // Hold или Toggle
}
Конфигурацията се споделя между main thread и hotkey thread чрез Arc<Mutex<HotkeyConfig>>. Всеки 20ms hotkey thread-ът заключва mutex-а за кратко четене.
Формат на hotkey string
Hotkey комбинациите се записват като +-разделени стрингове:
ctrl+shift+r // Ctrl+Shift+R
alt+mouse4 // Alt+Mouse4 (back button)
mouse5 // Mouse5 (forward button, без modifier)
win+space // Win+Space
ctrl+shift+alt+f12 // Ctrl+Shift+Alt+F12
Парсване
parse_hotkey("ctrl+shift+r") връща:
modifiers:[Ctrl, Shift]main_key:Keyboard(0x52)(VK_R)
Поддържани модификатори: ctrl/control, shift, alt, win/super/meta.
Поддържани главни клавиши:
- Букви a-z
- Цифри 0-9
- F1-F12
- Специални: space, enter, escape, tab, backspace, insert, delete, home, end, pageup, pagedown, arrows
- OEM: grave, minus, equals, brackets, backslash, semicolon, quote, comma, period, slash
- Lock клавиши: capslock, numlock, scrolllock
- Mouse бутони: mouse1-mouse5
Форматиране
Модификаторите се извеждат в стабилен ред: ctrl, shift, alt, win. Roundtrip parse -> format -> parse е гарантиран.
Virtual Key Mapping
Модулът vk_map.rs предоставя двупосочно преобразуване между Win32 virtual key codes и display стрингове:
vk_to_string(0x52)->"r"str_to_vk("space")->Some(0x20)vk_to_modifier(0xA0)->Some(Modifier::Shift)(VK_LSHIFT)
HotkeyRecorder
Механизъм за записване на нова hotkey комбинация от потребителя в Settings панела:
500ms debounce: След натискане на "Record" бутона, системата игнорира входа за 500ms за да не залови самото натискане на бутона.
scan_pressed_key: Сканира всички VK codes (0x08 до 0xFE) и mouse бутони, пропускайки modifier-ите. Първият намерен натиснат клавиш се комбинира с текущите модификатори.
macOS (TODO)
macOS имплементацията е stub. Планирана е чрез rdev crate за CGEvent tap-based глобално прихващане на входа. Изисква Accessibility/Input Monitoring permission.