1use x11rb::protocol::{
2 xinput,
3 xproto::{self, ModMask},
4};
5
6use crate::{Modifiers, MouseButton, NavigationDirection};
7
8pub(crate) enum ButtonOrScroll {
9 Button(MouseButton),
10 Scroll(ScrollDirection),
11}
12
13pub(crate) enum ScrollDirection {
14 Up,
15 Down,
16 Left,
17 Right,
18}
19
20pub(crate) fn button_or_scroll_from_event_detail(detail: u32) -> Option<ButtonOrScroll> {
21 Some(match detail {
22 1 => ButtonOrScroll::Button(MouseButton::Left),
23 2 => ButtonOrScroll::Button(MouseButton::Middle),
24 3 => ButtonOrScroll::Button(MouseButton::Right),
25 4 => ButtonOrScroll::Scroll(ScrollDirection::Up),
26 5 => ButtonOrScroll::Scroll(ScrollDirection::Down),
27 6 => ButtonOrScroll::Scroll(ScrollDirection::Left),
28 7 => ButtonOrScroll::Scroll(ScrollDirection::Right),
29 8 => ButtonOrScroll::Button(MouseButton::Navigate(NavigationDirection::Back)),
30 9 => ButtonOrScroll::Button(MouseButton::Navigate(NavigationDirection::Forward)),
31 _ => return None,
32 })
33}
34
35pub(crate) fn modifiers_from_state(state: xproto::KeyButMask) -> Modifiers {
36 Modifiers {
37 control: state.contains(xproto::KeyButMask::CONTROL),
38 alt: state.contains(xproto::KeyButMask::MOD1),
39 shift: state.contains(xproto::KeyButMask::SHIFT),
40 platform: state.contains(xproto::KeyButMask::MOD4),
41 function: false,
42 }
43}
44
45pub(crate) fn modifiers_from_xinput_info(modifier_info: xinput::ModifierInfo) -> Modifiers {
46 Modifiers {
47 control: modifier_info.effective as u16 & ModMask::CONTROL.bits()
48 == ModMask::CONTROL.bits(),
49 alt: modifier_info.effective as u16 & ModMask::M1.bits() == ModMask::M1.bits(),
50 shift: modifier_info.effective as u16 & ModMask::SHIFT.bits() == ModMask::SHIFT.bits(),
51 platform: modifier_info.effective as u16 & ModMask::M4.bits() == ModMask::M4.bits(),
52 function: false,
53 }
54}
55
56pub(crate) fn pressed_button_from_mask(button_mask: u32) -> Option<MouseButton> {
57 Some(if button_mask & 2 == 2 {
58 MouseButton::Left
59 } else if button_mask & 4 == 4 {
60 MouseButton::Middle
61 } else if button_mask & 8 == 8 {
62 MouseButton::Right
63 } else {
64 return None;
65 })
66}
67
68pub(crate) fn get_valuator_axis_index(
69 valuator_mask: &Vec<u32>,
70 valuator_number: u16,
71) -> Option<usize> {
72 // XInput valuator masks have a 1 at the bit indexes corresponding to each
73 // valuator present in this event's axisvalues. Axisvalues is ordered from
74 // lowest valuator number to highest, so counting bits before the 1 bit for
75 // this valuator yields the index in axisvalues.
76 if bit_is_set_in_vec(valuator_mask, valuator_number) {
77 Some(popcount_upto_bit_index(valuator_mask, valuator_number) as usize)
78 } else {
79 None
80 }
81}
82
83/// Returns the number of 1 bits in `bit_vec` for all bits where `i < bit_index`.
84fn popcount_upto_bit_index(bit_vec: &Vec<u32>, bit_index: u16) -> u32 {
85 let array_index = bit_index as usize / 32;
86 let popcount: u32 = bit_vec
87 .get(array_index)
88 .map_or(0, |bits| keep_bits_upto(*bits, bit_index % 32).count_ones());
89 if array_index == 0 {
90 popcount
91 } else {
92 // Valuator numbers over 32 probably never occur for scroll position, but may as well
93 // support it.
94 let leading_popcount: u32 = bit_vec
95 .iter()
96 .take(array_index)
97 .map(|bits| bits.count_ones())
98 .sum();
99 popcount + leading_popcount
100 }
101}
102
103fn bit_is_set_in_vec(bit_vec: &Vec<u32>, bit_index: u16) -> bool {
104 let array_index = bit_index as usize / 32;
105 bit_vec
106 .get(array_index)
107 .is_some_and(|bits| bit_is_set(*bits, bit_index % 32))
108}
109
110fn bit_is_set(bits: u32, bit_index: u16) -> bool {
111 bits & (1 << bit_index) != 0
112}
113
114/// Sets every bit with `i >= bit_index` to 0.
115fn keep_bits_upto(bits: u32, bit_index: u16) -> u32 {
116 if bit_index == 0 {
117 0
118 } else if bit_index >= 32 {
119 u32::MAX
120 } else {
121 bits & ((1 << bit_index) - 1)
122 }
123}
124
125#[cfg(test)]
126mod tests {
127 use super::*;
128
129 #[test]
130 fn test_get_valuator_axis_index() {
131 assert!(get_valuator_axis_index(&vec![0b11], 0) == Some(0));
132 assert!(get_valuator_axis_index(&vec![0b11], 1) == Some(1));
133 assert!(get_valuator_axis_index(&vec![0b11], 2) == None);
134
135 assert!(get_valuator_axis_index(&vec![0b100], 0) == None);
136 assert!(get_valuator_axis_index(&vec![0b100], 1) == None);
137 assert!(get_valuator_axis_index(&vec![0b100], 2) == Some(0));
138 assert!(get_valuator_axis_index(&vec![0b100], 3) == None);
139
140 assert!(get_valuator_axis_index(&vec![0b1010, 0], 0) == None);
141 assert!(get_valuator_axis_index(&vec![0b1010, 0], 1) == Some(0));
142 assert!(get_valuator_axis_index(&vec![0b1010, 0], 2) == None);
143 assert!(get_valuator_axis_index(&vec![0b1010, 0], 3) == Some(1));
144
145 assert!(get_valuator_axis_index(&vec![0b1010, 0b1], 0) == None);
146 assert!(get_valuator_axis_index(&vec![0b1010, 0b1], 1) == Some(0));
147 assert!(get_valuator_axis_index(&vec![0b1010, 0b1], 2) == None);
148 assert!(get_valuator_axis_index(&vec![0b1010, 0b1], 3) == Some(1));
149 assert!(get_valuator_axis_index(&vec![0b1010, 0b1], 32) == Some(2));
150 assert!(get_valuator_axis_index(&vec![0b1010, 0b1], 33) == None);
151
152 assert!(get_valuator_axis_index(&vec![0b1010, 0b101], 34) == Some(3));
153 }
154}