system_settings.rs

  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}