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.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}