Skip to main content

rust_switcher/input/
ring_buffer.rs

1use std::{
2    collections::VecDeque,
3    sync::{Mutex, OnceLock},
4};
5
6#[cfg(windows)]
7use windows::Win32::UI::{
8    Input::KeyboardAndMouse::{
9        GetAsyncKeyState, GetKeyState, GetKeyboardLayout, GetKeyboardState, HKL, ToUnicodeEx,
10        VIRTUAL_KEY, VK_BACK, VK_CAPITAL, VK_DELETE, VK_DOWN, VK_END, VK_ESCAPE, VK_HOME,
11        VK_INSERT, VK_LEFT, VK_LSHIFT, VK_NEXT, VK_PRIOR, VK_RETURN, VK_RIGHT, VK_RSHIFT, VK_SHIFT,
12        VK_TAB, VK_UP,
13    },
14    WindowsAndMessaging::{
15        GetForegroundWindow, GetWindowThreadProcessId, KBDLLHOOKSTRUCT, LLKHF_INJECTED,
16    },
17};
18
19static JOURNAL: OnceLock<Mutex<InputJournal>> = OnceLock::new();
20
21fn journal() -> &'static Mutex<InputJournal> {
22    JOURNAL.get_or_init(|| Mutex::new(InputJournal::new(100)))
23}
24
25fn with_journal_mut<R>(f: impl FnOnce(&mut InputJournal) -> R) -> R {
26    let mut guard = match journal().lock() {
27        Ok(g) => g,
28        Err(poison) => {
29            #[cfg(debug_assertions)]
30            tracing::warn!("input journal mutex was poisoned; continuing with inner value");
31            poison.into_inner()
32        }
33    };
34    f(&mut guard)
35}
36
37#[cfg(any(test, windows))]
38fn with_journal<R>(f: impl FnOnce(&InputJournal) -> R) -> R {
39    let guard = match journal().lock() {
40        Ok(g) => g,
41        Err(poison) => {
42            #[cfg(debug_assertions)]
43            tracing::warn!("input journal mutex was poisoned; continuing with inner value");
44            poison.into_inner()
45        }
46    };
47    f(&guard)
48}
49
50#[cfg(windows)]
51const LANG_ENGLISH_PRIMARY: u16 = 0x09;
52#[cfg(windows)]
53const LANG_RUSSIAN_PRIMARY: u16 = 0x19;
54
55#[derive(Copy, Clone, Debug, Eq, PartialEq)]
56pub enum LayoutTag {
57    Ru,
58    En,
59    Other(u16),
60    Unknown,
61}
62
63#[derive(Copy, Clone, Debug, Eq, PartialEq)]
64pub enum RunOrigin {
65    Physical,
66    Programmatic,
67}
68
69#[derive(Copy, Clone, Debug, Eq, PartialEq)]
70pub enum RunKind {
71    Text,
72    Whitespace,
73}
74
75#[derive(Clone, Debug, Eq, PartialEq)]
76pub struct InputRun {
77    pub text: String,
78    pub layout: LayoutTag,
79    pub origin: RunOrigin,
80    pub kind: RunKind,
81}
82
83#[derive(Debug, Default)]
84struct InputJournal {
85    runs: VecDeque<InputRun>,
86    cap_chars: usize,
87    total_chars: usize,
88    last_token_autoconverted: bool,
89    #[cfg(windows)]
90    last_fg_hwnd: isize,
91}
92
93impl InputJournal {
94    const fn new(cap_chars: usize) -> Self {
95        Self {
96            runs: VecDeque::new(),
97            cap_chars,
98            total_chars: 0,
99            last_token_autoconverted: false,
100            #[cfg(windows)]
101            last_fg_hwnd: 0,
102        }
103    }
104
105    #[cfg(any(test, windows))]
106    fn clear(&mut self) {
107        self.runs.clear();
108        self.total_chars = 0;
109        self.last_token_autoconverted = false;
110    }
111
112    fn append_segment(&mut self, text: &str, layout: LayoutTag, origin: RunOrigin, kind: RunKind) {
113        if text.is_empty() {
114            return;
115        }
116
117        if let Some(last) = self.runs.back_mut()
118            && last.layout == layout
119            && last.origin == origin
120            && last.kind == kind
121        {
122            last.text.push_str(text);
123            self.total_chars += text.chars().count();
124            self.enforce_cap_chars();
125            return;
126        }
127
128        self.total_chars += text.chars().count();
129        self.runs.push_back(InputRun {
130            text: text.to_string(),
131            layout,
132            origin,
133            kind,
134        });
135        self.enforce_cap_chars();
136    }
137
138    #[cfg(any(test, windows))]
139    fn push_text_internal(&mut self, text: &str, layout: LayoutTag, origin: RunOrigin) {
140        if text.is_empty() {
141            return;
142        }
143
144        // Segment the input without intermediate allocations by slicing `text` at kind boundaries.
145        // This keeps the journal cheap for frequent short inputs (single key presses) and also
146        // efficient for programmatic multi-character inserts.
147        let mut start = 0usize;
148        let mut current_kind: Option<RunKind> = None;
149
150        for (i, ch) in text.char_indices() {
151            let kind = if ch.is_whitespace() {
152                RunKind::Whitespace
153            } else {
154                RunKind::Text
155            };
156
157            match current_kind {
158                None => {
159                    start = i;
160                    current_kind = Some(kind);
161                }
162                Some(k) if k == kind => {}
163                Some(k) => {
164                    self.append_segment(&text[start..i], layout, origin, k);
165                    start = i;
166                    current_kind = Some(kind);
167                }
168            }
169        }
170
171        if let Some(kind) = current_kind {
172            self.append_segment(&text[start..], layout, origin, kind);
173        }
174    }
175
176    fn push_run(&mut self, run: InputRun) {
177        self.append_segment(&run.text, run.layout, run.origin, run.kind);
178    }
179
180    fn push_runs(&mut self, runs: impl IntoIterator<Item = InputRun>) {
181        for run in runs {
182            self.push_run(run);
183        }
184    }
185
186    fn enforce_cap_chars(&mut self) {
187        while self.total_chars > self.cap_chars {
188            let mut remove_front_run = false;
189
190            if let Some(front) = self.runs.front_mut() {
191                if let Some((idx, _)) = front.text.char_indices().nth(1) {
192                    front.text.drain(..idx);
193                } else {
194                    front.text.clear();
195                    remove_front_run = true;
196                }
197                self.total_chars = self.total_chars.saturating_sub(1);
198
199                if front.text.is_empty() {
200                    remove_front_run = true;
201                }
202            } else {
203                self.total_chars = 0;
204                break;
205            }
206
207            if remove_front_run {
208                let _ = self.runs.pop_front();
209            }
210        }
211    }
212
213    #[cfg(any(test, windows))]
214    fn backspace(&mut self) {
215        let mut pop_last = false;
216
217        if let Some(last) = self.runs.back_mut()
218            && let Some((idx, _)) = last.text.char_indices().last()
219        {
220            last.text.drain(idx..);
221            self.total_chars = self.total_chars.saturating_sub(1);
222            if last.text.is_empty() {
223                pop_last = true;
224            }
225        }
226
227        if pop_last {
228            let _ = self.runs.pop_back();
229        }
230    }
231
232    #[cfg(windows)]
233    fn invalidate_if_foreground_changed(&mut self) {
234        let fg = unsafe { GetForegroundWindow() };
235        let raw = fg.0 as isize;
236        if raw == 0 {
237            self.clear();
238            self.last_fg_hwnd = 0;
239            return;
240        }
241
242        if self.last_fg_hwnd == 0 {
243            self.last_fg_hwnd = raw;
244            return;
245        }
246
247        if self.last_fg_hwnd != raw {
248            self.clear();
249            self.last_fg_hwnd = raw;
250        }
251    }
252
253    #[cfg(any(test, windows))]
254    fn last_char(&self) -> Option<char> {
255        self.runs.back()?.text.chars().last()
256    }
257
258    #[cfg(any(test, windows))]
259    fn prev_char_before_last(&self) -> Option<char> {
260        let mut runs_it = self.runs.iter().rev();
261        let last_run = runs_it.next()?;
262
263        let mut chars = last_run.text.chars().rev();
264        let _ = chars.next()?;
265        if let Some(prev) = chars.next() {
266            return Some(prev);
267        }
268
269        for run in runs_it {
270            if let Some(ch) = run.text.chars().last() {
271                return Some(ch);
272            }
273        }
274
275        None
276    }
277
278    fn take_last_layout_run_with_suffix(&mut self) -> Option<(InputRun, Vec<InputRun>)> {
279        let mut suffix_runs = self.pop_suffix_whitespace();
280
281        if self.runs.back().is_none_or(|run| run.kind != RunKind::Text) {
282            self.restore_suffix(&mut suffix_runs);
283            return None;
284        }
285
286        let run = self.runs.pop_back()?;
287        self.total_chars = self.total_chars.saturating_sub(run.text.chars().count());
288        suffix_runs.reverse();
289        Some((run, suffix_runs))
290    }
291
292    fn take_last_layout_sequence_with_suffix(&mut self) -> Option<(Vec<InputRun>, Vec<InputRun>)> {
293        let mut suffix_runs = self.pop_suffix_whitespace();
294
295        if self.runs.back().is_none_or(|run| run.kind != RunKind::Text) {
296            self.restore_suffix(&mut suffix_runs);
297            return None;
298        }
299
300        let last = self.runs.back()?;
301        let target_layout = last.layout;
302        let target_origin = last.origin;
303        let mut seq_rev: Vec<InputRun> = Vec::new();
304        while let Some(run) = self.runs.back() {
305            if run.layout != target_layout || run.origin != target_origin {
306                break;
307            }
308            let run = self.runs.pop_back()?;
309            self.total_chars = self.total_chars.saturating_sub(run.text.chars().count());
310            seq_rev.push(run);
311        }
312
313        if seq_rev.is_empty() {
314            self.restore_suffix(&mut suffix_runs);
315            return None;
316        }
317
318        seq_rev.reverse();
319        suffix_runs.reverse();
320        Some((seq_rev, suffix_runs))
321    }
322
323    fn take_last_programmatic_sequence_with_suffix(
324        &mut self,
325    ) -> Option<(Vec<InputRun>, Vec<InputRun>)> {
326        let mut suffix_runs = self.pop_suffix_whitespace();
327
328        if self
329            .runs
330            .back()
331            .is_none_or(|run| run.origin != RunOrigin::Programmatic)
332        {
333            self.restore_suffix(&mut suffix_runs);
334            return None;
335        }
336
337        let mut seq_rev: Vec<InputRun> = Vec::new();
338        while let Some(run) = self.runs.back() {
339            if run.origin != RunOrigin::Programmatic {
340                break;
341            }
342            let run = self.runs.pop_back()?;
343            self.total_chars = self.total_chars.saturating_sub(run.text.chars().count());
344            seq_rev.push(run);
345        }
346
347        if seq_rev.is_empty() {
348            self.restore_suffix(&mut suffix_runs);
349            return None;
350        }
351
352        seq_rev.reverse();
353        suffix_runs.reverse();
354        Some((seq_rev, suffix_runs))
355    }
356
357    fn pop_suffix_whitespace(&mut self) -> Vec<InputRun> {
358        let mut suffix_runs: Vec<InputRun> = Vec::new();
359        while self
360            .runs
361            .back()
362            .is_some_and(|run| run.kind == RunKind::Whitespace)
363        {
364            let Some(run) = self.runs.pop_back() else {
365                break;
366            };
367            self.total_chars = self.total_chars.saturating_sub(run.text.chars().count());
368            suffix_runs.push(run);
369        }
370        suffix_runs
371    }
372
373    fn restore_suffix(&mut self, suffix_runs: &mut Vec<InputRun>) {
374        // `suffix_runs` is expected to be in reverse order (from repeated `pop_back`).
375        while let Some(run) = suffix_runs.pop() {
376            self.total_chars += run.text.chars().count();
377            self.runs.push_back(run);
378        }
379    }
380}
381
382#[cfg(windows)]
383#[derive(Debug)]
384struct DecodedText {
385    text: String,
386    layout: LayoutTag,
387}
388
389#[cfg(windows)]
390#[derive(Copy, Clone, Debug, Eq, PartialEq)]
391struct KeyboardStateOverrides {
392    shift_down: bool,
393    left_shift_down: bool,
394    right_shift_down: bool,
395    caps_lock_on: bool,
396}
397
398#[cfg(windows)]
399pub fn layout_tag_from_hkl(hkl: HKL) -> LayoutTag {
400    let hkl_raw = hkl.0 as usize;
401
402    if hkl_raw == 0 {
403        return LayoutTag::Unknown;
404    }
405
406    let lang_id = (hkl_raw & 0xFFFF) as u16;
407    let primary = lang_id & 0x03FF;
408
409    match primary {
410        LANG_ENGLISH_PRIMARY => LayoutTag::En,
411        LANG_RUSSIAN_PRIMARY => LayoutTag::Ru,
412        _ => LayoutTag::Other(lang_id),
413    }
414}
415
416#[cfg(windows)]
417fn current_foreground_layout_tag() -> LayoutTag {
418    let fg = unsafe { GetForegroundWindow() };
419    if fg.0.is_null() {
420        return LayoutTag::Unknown;
421    }
422
423    let tid = unsafe { GetWindowThreadProcessId(fg, None) };
424    let hkl = unsafe { GetKeyboardLayout(tid) };
425    layout_tag_from_hkl(hkl)
426}
427
428pub fn mark_last_token_autoconverted() {
429    with_journal_mut(|j| j.last_token_autoconverted = true);
430}
431
432#[cfg(any(test, windows))]
433#[must_use]
434pub fn last_token_autoconverted() -> bool {
435    with_journal(|j| j.last_token_autoconverted)
436}
437
438#[cfg(windows)]
439fn mods_ctrl_or_alt_down() -> bool {
440    // Keep this module independent from `crate::platform` so it can be built from the minimal lib target.
441    // VK_CONTROL = 0x11, VK_MENU (Alt) = 0x12.
442    let ctrl = unsafe { GetAsyncKeyState(0x11) }.cast_unsigned();
443    let alt = unsafe { GetAsyncKeyState(0x12) }.cast_unsigned();
444    (ctrl & 0x8000) != 0 || (alt & 0x8000) != 0
445}
446
447#[cfg(windows)]
448fn is_modifier_vk(vk: VIRTUAL_KEY) -> bool {
449    matches!(vk.0, 0xA0..=0xA5 | 0x5B | 0x5C)
450}
451
452#[cfg(windows)]
453fn should_clear_for_ctrl_alt_combo(vk: VIRTUAL_KEY, ctrl_or_alt_down: bool) -> bool {
454    ctrl_or_alt_down && !is_modifier_vk(vk)
455}
456
457#[cfg(windows)]
458fn key_is_down(vk: VIRTUAL_KEY) -> bool {
459    let value = unsafe { GetAsyncKeyState(i32::from(vk.0)) }.cast_unsigned();
460    (value & 0x8000) != 0
461}
462
463#[cfg(windows)]
464fn key_is_toggled(vk: VIRTUAL_KEY) -> bool {
465    let value = unsafe { GetKeyState(i32::from(vk.0)) }.cast_unsigned();
466    (value & 0x0001) != 0
467}
468
469#[cfg(windows)]
470fn set_key_down_state(state: &mut [u8; 256], vk: VIRTUAL_KEY, is_down: bool) {
471    let idx = usize::from(vk.0);
472    if idx >= state.len() {
473        return;
474    }
475
476    if is_down {
477        state[idx] |= 0x80;
478    } else {
479        state[idx] &= !0x80;
480    }
481}
482
483#[cfg(windows)]
484fn set_key_toggle_state(state: &mut [u8; 256], vk: VIRTUAL_KEY, is_toggled: bool) {
485    let idx = usize::from(vk.0);
486    if idx >= state.len() {
487        return;
488    }
489
490    if is_toggled {
491        state[idx] |= 0x01;
492    } else {
493        state[idx] &= !0x01;
494    }
495}
496
497#[cfg(windows)]
498fn current_keyboard_state_overrides() -> KeyboardStateOverrides {
499    KeyboardStateOverrides {
500        shift_down: key_is_down(VK_SHIFT),
501        left_shift_down: key_is_down(VK_LSHIFT),
502        right_shift_down: key_is_down(VK_RSHIFT),
503        caps_lock_on: key_is_toggled(VK_CAPITAL),
504    }
505}
506
507#[cfg(windows)]
508fn apply_keyboard_state_overrides(state: &mut [u8; 256], overrides: KeyboardStateOverrides) {
509    // LL-hook decoding runs before the target thread's keyboard state is fully reflected in our
510    // thread-local `GetKeyboardState` snapshot. Patch in the physical modifier/toggle state that
511    // affects character case before calling `ToUnicodeEx`.
512    set_key_down_state(state, VK_SHIFT, overrides.shift_down);
513    set_key_down_state(state, VK_LSHIFT, overrides.left_shift_down);
514    set_key_down_state(state, VK_RSHIFT, overrides.right_shift_down);
515    set_key_toggle_state(state, VK_CAPITAL, overrides.caps_lock_on);
516}
517
518#[cfg(windows)]
519fn decode_typed_text(kb: &KBDLLHOOKSTRUCT, vk: VIRTUAL_KEY) -> Option<DecodedText> {
520    let fg = unsafe { GetForegroundWindow() };
521    if fg.0.is_null() {
522        return None;
523    }
524
525    let tid = unsafe { GetWindowThreadProcessId(fg, None) };
526    let hkl = unsafe { GetKeyboardLayout(tid) };
527    let layout = layout_tag_from_hkl(hkl);
528
529    let mut state = [0u8; 256];
530    if unsafe { GetKeyboardState(&mut state) }.is_err() {
531        return None;
532    }
533
534    apply_keyboard_state_overrides(&mut state, current_keyboard_state_overrides());
535
536    let mut buf = [0u16; 8];
537    let rc = unsafe { ToUnicodeEx(u32::from(vk.0), kb.scanCode, &state, &mut buf, 0, Some(hkl)) };
538
539    if rc == -1 {
540        let _ =
541            unsafe { ToUnicodeEx(u32::from(vk.0), kb.scanCode, &state, &mut buf, 0, Some(hkl)) };
542        return None;
543    }
544
545    if rc <= 0 {
546        return None;
547    }
548
549    let rc = usize::try_from(rc).ok()?;
550    let s = String::from_utf16_lossy(&buf[..rc]);
551
552    if s.chars().any(char::is_control) {
553        return None;
554    }
555
556    Some(DecodedText { text: s, layout })
557}
558
559#[cfg(windows)]
560pub fn record_keydown(kb: &KBDLLHOOKSTRUCT, vk: u32) -> Option<String> {
561    if kb.flags.contains(LLKHF_INJECTED) {
562        return None;
563    }
564
565    let vk_u16 = u16::try_from(vk).ok()?;
566    let vk = VIRTUAL_KEY(vk_u16);
567
568    enum JournalAction {
569        Clear,
570        Backspace,
571        PushText {
572            text: String,
573            layout: LayoutTag,
574            origin: RunOrigin,
575        },
576    }
577
578    let mut action: Option<JournalAction> = None;
579    let mut output: Option<String> = None;
580
581    match vk {
582        VK_ESCAPE | VK_DELETE | VK_INSERT | VK_LEFT | VK_RIGHT | VK_UP | VK_DOWN | VK_HOME
583        | VK_END | VK_PRIOR | VK_NEXT => action = Some(JournalAction::Clear),
584        VK_BACK => action = Some(JournalAction::Backspace),
585        VK_RETURN => {
586            let layout = current_foreground_layout_tag();
587            output = Some("\n".to_string());
588            action = Some(JournalAction::PushText {
589                text: "\n".to_string(),
590                layout,
591                origin: RunOrigin::Physical,
592            });
593        }
594        VK_TAB => {
595            let layout = current_foreground_layout_tag();
596            output = Some("\t".to_string());
597            action = Some(JournalAction::PushText {
598                text: "\t".to_string(),
599                layout,
600                origin: RunOrigin::Physical,
601            });
602        }
603        _ => {}
604    }
605
606    if should_clear_for_ctrl_alt_combo(vk, mods_ctrl_or_alt_down()) {
607        action = Some(JournalAction::Clear);
608    }
609
610    if action.is_none() {
611        let decoded = decode_typed_text(kb, vk)?;
612        output = Some(decoded.text.clone());
613        action = Some(JournalAction::PushText {
614            text: decoded.text,
615            layout: decoded.layout,
616            origin: RunOrigin::Physical,
617        });
618    }
619
620    with_journal_mut(|j| {
621        j.invalidate_if_foreground_changed();
622        if let Some(action) = action {
623            match action {
624                JournalAction::Clear => j.clear(),
625                JournalAction::Backspace => j.backspace(),
626                JournalAction::PushText {
627                    text,
628                    layout,
629                    origin,
630                } => {
631                    if text.chars().any(char::is_alphanumeric) {
632                        j.last_token_autoconverted = false;
633                    }
634                    j.push_text_internal(&text, layout, origin);
635                }
636            }
637        }
638    });
639
640    output
641}
642
643#[must_use]
644pub fn take_last_layout_run_with_suffix() -> Option<(InputRun, Vec<InputRun>)> {
645    with_journal_mut(|j| j.take_last_layout_run_with_suffix())
646}
647
648#[must_use]
649pub fn take_last_layout_sequence_with_suffix() -> Option<(Vec<InputRun>, Vec<InputRun>)> {
650    with_journal_mut(|j| j.take_last_layout_sequence_with_suffix())
651}
652
653#[must_use]
654pub fn take_last_programmatic_sequence_with_suffix() -> Option<(Vec<InputRun>, Vec<InputRun>)> {
655    with_journal_mut(|j| j.take_last_programmatic_sequence_with_suffix())
656}
657
658#[cfg(test)]
659pub fn push_text(s: &str) {
660    with_journal_mut(|j| j.push_text_internal(s, LayoutTag::Unknown, RunOrigin::Programmatic));
661}
662
663pub fn push_run(run: InputRun) {
664    with_journal_mut(|j| j.push_run(run));
665}
666
667pub fn push_runs(runs: impl IntoIterator<Item = InputRun>) {
668    with_journal_mut(|j| j.push_runs(runs));
669}
670
671#[cfg(test)]
672pub fn test_backspace() {
673    with_journal_mut(|j| j.backspace());
674}
675
676#[cfg(test)]
677pub fn runs_snapshot() -> Vec<InputRun> {
678    with_journal(|j| j.runs.iter().cloned().collect())
679}
680
681#[cfg(any(test, windows))]
682pub fn invalidate() {
683    with_journal_mut(|j| j.clear());
684}
685
686#[cfg(any(test, windows))]
687#[must_use]
688pub fn last_char_triggers_autoconvert() -> bool {
689    with_journal(|j| {
690        let Some(last) = j.last_char() else {
691            return false;
692        };
693
694        if matches!(last, '.' | ',' | '!' | '?' | ';' | ':') {
695            return j
696                .prev_char_before_last()
697                .is_some_and(|prev| !prev.is_whitespace());
698        }
699
700        if last.is_whitespace() {
701            return j
702                .prev_char_before_last()
703                .is_some_and(|prev| !prev.is_whitespace());
704        }
705
706        false
707    })
708}
709
710#[cfg(all(test, windows))]
711mod tests {
712    use super::*;
713
714    #[test]
715    fn ctrl_or_alt_combo_preserves_modifier_only_layout_switch_chords() {
716        assert!(!should_clear_for_ctrl_alt_combo(VK_LSHIFT, true));
717        assert!(!should_clear_for_ctrl_alt_combo(VK_RSHIFT, true));
718        assert!(!should_clear_for_ctrl_alt_combo(VIRTUAL_KEY(0xA4), true));
719        assert!(!should_clear_for_ctrl_alt_combo(VIRTUAL_KEY(0xA5), true));
720    }
721
722    #[test]
723    fn ctrl_or_alt_combo_still_clears_non_modifier_keys() {
724        assert!(should_clear_for_ctrl_alt_combo(
725            VIRTUAL_KEY(u16::from(b'A')),
726            true
727        ));
728        assert!(should_clear_for_ctrl_alt_combo(VK_TAB, true));
729        assert!(!should_clear_for_ctrl_alt_combo(
730            VIRTUAL_KEY(u16::from(b'A')),
731            false
732        ));
733    }
734
735    #[test]
736    fn keyboard_state_overrides_apply_caps_lock_toggle_without_touching_high_bit() {
737        let mut state = [0u8; 256];
738        apply_keyboard_state_overrides(
739            &mut state,
740            KeyboardStateOverrides {
741                shift_down: false,
742                left_shift_down: false,
743                right_shift_down: false,
744                caps_lock_on: true,
745            },
746        );
747
748        let caps = state[usize::from(VK_CAPITAL.0)];
749        assert_eq!(caps & 0x01, 0x01);
750        assert_eq!(caps & 0x80, 0x00);
751    }
752
753    #[test]
754    fn keyboard_state_overrides_preserve_shift_and_caps_lock_combination() {
755        let mut state = [0u8; 256];
756        apply_keyboard_state_overrides(
757            &mut state,
758            KeyboardStateOverrides {
759                shift_down: true,
760                left_shift_down: true,
761                right_shift_down: false,
762                caps_lock_on: true,
763            },
764        );
765
766        assert_eq!(state[usize::from(VK_SHIFT.0)] & 0x80, 0x80);
767        assert_eq!(state[usize::from(VK_LSHIFT.0)] & 0x80, 0x80);
768        assert_eq!(state[usize::from(VK_RSHIFT.0)] & 0x80, 0x00);
769        assert_eq!(state[usize::from(VK_CAPITAL.0)] & 0x01, 0x01);
770    }
771}