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