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, SPI_SETWORKAREA,
11 SYSTEM_PARAMETERS_INFO_ACTION, SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS, 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(&mut 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 SYSTEM_PARAMETERS_INFO_ACTION(wparam as u32) {
50 SPI_SETWORKAREA => self.update_taskbar_position(display),
51 SPI_GETWHEELSCROLLLINES | SPI_GETWHEELSCROLLCHARS => self.update_mouse_wheel_settings(),
52 _ => {}
53 }
54 }
55
56 fn update_mouse_wheel_settings(&self) {
57 self.mouse_wheel_settings.update();
58 }
59
60 fn update_taskbar_position(&self, display: WindowsDisplay) {
61 self.auto_hide_taskbar_position
62 .set(AutoHideTaskbarPosition::new(display).log_err().flatten());
63 }
64}
65
66impl MouseWheelSettings {
67 fn update(&self) {
68 self.update_wheel_scroll_chars();
69 self.update_wheel_scroll_lines();
70 }
71
72 fn update_wheel_scroll_chars(&self) {
73 let mut value = c_uint::default();
74 let result = unsafe {
75 SystemParametersInfoW(
76 SPI_GETWHEELSCROLLCHARS,
77 0,
78 Some((&mut value) as *mut c_uint as *mut c_void),
79 SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS::default(),
80 )
81 };
82
83 if result.log_err() != None && self.wheel_scroll_chars.get() != value {
84 self.wheel_scroll_chars.set(value);
85 }
86 }
87
88 fn update_wheel_scroll_lines(&self) {
89 let mut value = c_uint::default();
90 let result = unsafe {
91 SystemParametersInfoW(
92 SPI_GETWHEELSCROLLLINES,
93 0,
94 Some((&mut value) as *mut c_uint as *mut c_void),
95 SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS::default(),
96 )
97 };
98
99 if result.log_err() != None && self.wheel_scroll_lines.get() != value {
100 self.wheel_scroll_lines.set(value);
101 }
102 }
103}
104
105#[derive(Debug, Clone, Copy, Default)]
106pub(crate) enum AutoHideTaskbarPosition {
107 Left,
108 Right,
109 Top,
110 #[default]
111 Bottom,
112}
113
114impl AutoHideTaskbarPosition {
115 fn new(display: WindowsDisplay) -> anyhow::Result<Option<Self>> {
116 if !check_auto_hide_taskbar_enable() {
117 // If auto hide taskbar is not enable, we do nothing in this case.
118 return Ok(None);
119 }
120 let mut info = APPBARDATA {
121 cbSize: std::mem::size_of::<APPBARDATA>() as u32,
122 ..Default::default()
123 };
124 let ret = unsafe { SHAppBarMessage(ABM_GETTASKBARPOS, &mut info) };
125 if ret == 0 {
126 anyhow::bail!(
127 "Unable to retrieve taskbar position: {}",
128 std::io::Error::last_os_error()
129 );
130 }
131 let taskbar_bounds: Bounds<DevicePixels> = Bounds::new(
132 point(info.rc.left.into(), info.rc.top.into()),
133 size(
134 (info.rc.right - info.rc.left).into(),
135 (info.rc.bottom - info.rc.top).into(),
136 ),
137 );
138 let display_bounds = display.physical_bounds();
139 if display_bounds.intersect(&taskbar_bounds) != taskbar_bounds {
140 // This case indicates that taskbar is not on the current monitor.
141 return Ok(None);
142 }
143 if taskbar_bounds.bottom() == display_bounds.bottom()
144 && taskbar_bounds.right() == display_bounds.right()
145 {
146 if taskbar_bounds.size.height < display_bounds.size.height
147 && taskbar_bounds.size.width == display_bounds.size.width
148 {
149 return Ok(Some(Self::Bottom));
150 }
151 if taskbar_bounds.size.width < display_bounds.size.width
152 && taskbar_bounds.size.height == display_bounds.size.height
153 {
154 return Ok(Some(Self::Right));
155 }
156 log::error!(
157 "Unrecognized taskbar bounds {:?} give display bounds {:?}",
158 taskbar_bounds,
159 display_bounds
160 );
161 return Ok(None);
162 }
163 if taskbar_bounds.top() == display_bounds.top()
164 && taskbar_bounds.left() == display_bounds.left()
165 {
166 if taskbar_bounds.size.height < display_bounds.size.height
167 && taskbar_bounds.size.width == display_bounds.size.width
168 {
169 return Ok(Some(Self::Top));
170 }
171 if taskbar_bounds.size.width < display_bounds.size.width
172 && taskbar_bounds.size.height == display_bounds.size.height
173 {
174 return Ok(Some(Self::Left));
175 }
176 log::error!(
177 "Unrecognized taskbar bounds {:?} give display bounds {:?}",
178 taskbar_bounds,
179 display_bounds
180 );
181 return Ok(None);
182 }
183 log::error!(
184 "Unrecognized taskbar bounds {:?} give display bounds {:?}",
185 taskbar_bounds,
186 display_bounds
187 );
188 Ok(None)
189 }
190}
191
192/// Check if auto hide taskbar is enable or not.
193fn check_auto_hide_taskbar_enable() -> bool {
194 let mut info = APPBARDATA {
195 cbSize: std::mem::size_of::<APPBARDATA>() as u32,
196 ..Default::default()
197 };
198 let ret = unsafe { SHAppBarMessage(ABM_GETSTATE, &mut info) } as u32;
199 ret == ABS_AUTOHIDE
200}