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