system_settings.rs

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