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