1use std::{
2 cell::Cell,
3 ffi::{c_uint, c_void},
4};
5
6use ::util::ResultExt;
7use windows::Win32::UI::{
8 Shell::{ABM_GETSTATE, ABM_GETTASKBARPOS, ABS_AUTOHIDE, APPBARDATA, SHAppBarMessage},
9 WindowsAndMessaging::{
10 SPI_GETWHEELSCROLLCHARS, SPI_GETWHEELSCROLLLINES, SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS,
11 SystemParametersInfoW,
12 },
13};
14
15use crate::*;
16
17use super::WindowsDisplay;
18
19/// Windows settings pulled from SystemParametersInfo
20/// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-systemparametersinfow
21#[derive(Default, Debug, Clone)]
22pub(crate) struct WindowsSystemSettings {
23 pub(crate) mouse_wheel_settings: MouseWheelSettings,
24 pub(crate) auto_hide_taskbar_position: Cell<Option<AutoHideTaskbarPosition>>,
25}
26
27#[derive(Default, Debug, Clone)]
28pub(crate) struct MouseWheelSettings {
29 /// SEE: SPI_GETWHEELSCROLLCHARS
30 pub(crate) wheel_scroll_chars: Cell<u32>,
31 /// SEE: SPI_GETWHEELSCROLLLINES
32 pub(crate) wheel_scroll_lines: Cell<u32>,
33}
34
35impl WindowsSystemSettings {
36 pub(crate) fn new(display: WindowsDisplay) -> Self {
37 let mut settings = Self::default();
38 settings.init(display);
39 settings
40 }
41
42 fn init(&self, display: WindowsDisplay) {
43 self.mouse_wheel_settings.update();
44 self.auto_hide_taskbar_position
45 .set(AutoHideTaskbarPosition::new(display).log_err().flatten());
46 }
47
48 pub(crate) fn update(&self, display: WindowsDisplay, wparam: usize) {
49 match wparam {
50 // SPI_SETWORKAREA
51 47 => self.update_taskbar_position(display),
52 // SPI_GETWHEELSCROLLLINES, SPI_GETWHEELSCROLLCHARS
53 104 | 108 => self.update_mouse_wheel_settings(),
54 _ => {}
55 }
56 }
57
58 fn update_mouse_wheel_settings(&self) {
59 self.mouse_wheel_settings.update();
60 }
61
62 fn update_taskbar_position(&self, display: WindowsDisplay) {
63 self.auto_hide_taskbar_position
64 .set(AutoHideTaskbarPosition::new(display).log_err().flatten());
65 }
66}
67
68impl MouseWheelSettings {
69 fn update(&self) {
70 self.update_wheel_scroll_chars();
71 self.update_wheel_scroll_lines();
72 }
73
74 fn update_wheel_scroll_chars(&self) {
75 let mut value = c_uint::default();
76 let result = unsafe {
77 SystemParametersInfoW(
78 SPI_GETWHEELSCROLLCHARS,
79 0,
80 Some((&mut value) as *mut c_uint as *mut c_void),
81 SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS::default(),
82 )
83 };
84
85 if result.log_err() != None && self.wheel_scroll_chars.get() != value {
86 self.wheel_scroll_chars.set(value);
87 }
88 }
89
90 fn update_wheel_scroll_lines(&self) {
91 let mut value = c_uint::default();
92 let result = unsafe {
93 SystemParametersInfoW(
94 SPI_GETWHEELSCROLLLINES,
95 0,
96 Some((&mut value) as *mut c_uint as *mut c_void),
97 SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS::default(),
98 )
99 };
100
101 if result.log_err() != None && self.wheel_scroll_lines.get() != value {
102 self.wheel_scroll_lines.set(value);
103 }
104 }
105}
106
107#[derive(Debug, Clone, Copy, Default)]
108pub(crate) enum AutoHideTaskbarPosition {
109 Left,
110 Right,
111 Top,
112 #[default]
113 Bottom,
114}
115
116impl AutoHideTaskbarPosition {
117 fn new(display: WindowsDisplay) -> anyhow::Result<Option<Self>> {
118 if !check_auto_hide_taskbar_enable() {
119 // If auto hide taskbar is not enable, we do nothing in this case.
120 return Ok(None);
121 }
122 let mut info = APPBARDATA {
123 cbSize: std::mem::size_of::<APPBARDATA>() as u32,
124 ..Default::default()
125 };
126 let ret = unsafe { SHAppBarMessage(ABM_GETTASKBARPOS, &mut info) };
127 if ret == 0 {
128 anyhow::bail!(
129 "Unable to retrieve taskbar position: {}",
130 std::io::Error::last_os_error()
131 );
132 }
133 let taskbar_bounds: Bounds<DevicePixels> = Bounds::new(
134 point(info.rc.left.into(), info.rc.top.into()),
135 size(
136 (info.rc.right - info.rc.left).into(),
137 (info.rc.bottom - info.rc.top).into(),
138 ),
139 );
140 let display_bounds = display.physical_bounds();
141 if display_bounds.intersect(&taskbar_bounds) != taskbar_bounds {
142 // This case indicates that taskbar is not on the current monitor.
143 return Ok(None);
144 }
145 if taskbar_bounds.bottom() == display_bounds.bottom()
146 && taskbar_bounds.right() == display_bounds.right()
147 {
148 if taskbar_bounds.size.height < display_bounds.size.height
149 && taskbar_bounds.size.width == display_bounds.size.width
150 {
151 return Ok(Some(Self::Bottom));
152 }
153 if taskbar_bounds.size.width < display_bounds.size.width
154 && taskbar_bounds.size.height == display_bounds.size.height
155 {
156 return Ok(Some(Self::Right));
157 }
158 log::error!(
159 "Unrecognized taskbar bounds {:?} give display bounds {:?}",
160 taskbar_bounds,
161 display_bounds
162 );
163 return Ok(None);
164 }
165 if taskbar_bounds.top() == display_bounds.top()
166 && taskbar_bounds.left() == display_bounds.left()
167 {
168 if taskbar_bounds.size.height < display_bounds.size.height
169 && taskbar_bounds.size.width == display_bounds.size.width
170 {
171 return Ok(Some(Self::Top));
172 }
173 if taskbar_bounds.size.width < display_bounds.size.width
174 && taskbar_bounds.size.height == display_bounds.size.height
175 {
176 return Ok(Some(Self::Left));
177 }
178 log::error!(
179 "Unrecognized taskbar bounds {:?} give display bounds {:?}",
180 taskbar_bounds,
181 display_bounds
182 );
183 return Ok(None);
184 }
185 log::error!(
186 "Unrecognized taskbar bounds {:?} give display bounds {:?}",
187 taskbar_bounds,
188 display_bounds
189 );
190 Ok(None)
191 }
192}
193
194/// Check if auto hide taskbar is enable or not.
195fn check_auto_hide_taskbar_enable() -> bool {
196 let mut info = APPBARDATA {
197 cbSize: std::mem::size_of::<APPBARDATA>() as u32,
198 ..Default::default()
199 };
200 let ret = unsafe { SHAppBarMessage(ABM_GETSTATE, &mut info) } as u32;
201 ret == ABS_AUTOHIDE
202}