diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..0f17867 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 2 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml new file mode 100644 index 0000000..451769a --- /dev/null +++ b/.github/workflows/CI.yml @@ -0,0 +1,66 @@ +###################################################################### +# Copyright (c) 2022 Silvio Clecio (silvioprog) +# +# SPDX-License-Identifier: MIT +###################################################################### + +name: CI/CD + +on: [push, pull_request] + +env: + CARGO_TERM_COLOR: always + +jobs: + build: + name: ${{ matrix.job.target }} + runs-on: ${{ matrix.job.os }} + + strategy: + matrix: + job: + - target: x86_64-unknown-linux-gnu + os: ubuntu-22.04 + test: false + publish: true + + - target: x86_64-apple-darwin + test: false + os: macos-11 + + - target: aarch64-apple-darwin + os: macos-11 + + - target: x86_64-pc-windows-msvc + test: false + os: windows-2022 + + steps: + - uses: actions/checkout@v3 + + - name: Install Rust toolchain + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + target: ${{ matrix.job.target }} + + - name: Cargo build + uses: actions-rs/cargo@v1 + with: + command: build + args: --release --target ${{ matrix.job.target }} + + - name: Cargo test + if: matrix.job.test + uses: actions-rs/cargo@v1 + with: + command: test + args: --target ${{ matrix.job.target }} + + - name: Cargo publish + if: startsWith(github.ref, 'refs/tags/') && matrix.job.publish + uses: actions-rs/cargo@v1 + with: + command: publish + args: --token ${{ secrets.CARGO_TOKEN }} -v diff --git a/.gitignore b/.gitignore index 088ba6b..89fd589 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,9 @@ Cargo.lock # These are backup files generated by rustfmt **/*.rs.bk + +# VSCode files +.vscode + +# macOS files +.DS_Store diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index db3b651..0000000 --- a/.travis.yml +++ /dev/null @@ -1,8 +0,0 @@ -language: rust -rust: - - nightly - - beta - - stable -script: - - cargo build --verbose - - cargo doc --verbose diff --git a/Cargo.toml b/Cargo.toml index ce8003c..f44cda8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,27 +1,27 @@ [package] name = 'lock_keys' -version = '1.0.0' +version = '1.1.0' authors = ['Silvio Clecio (silvioprog) '] license = 'MIT/Apache-2.0' description = 'Rust library for lock keys handling.' homepage = 'https://github.com/risoflora/lock_keys' repository = 'https://github.com/risoflora/lock_keys' readme = 'README.md' -keywords = [ - 'capslock', - 'numlock', - 'numlockx', - 'scrolllock', -] +keywords = ['capslock', 'numlock', 'numlockx', 'scrolllock'] categories = [ - 'api-bindings', - 'hardware-support', - 'os::macos-apis', - 'os::unix-apis', - 'os::windows-apis', + 'api-bindings', + 'hardware-support', + 'os::macos-apis', + 'os::unix-apis', + 'os::windows-apis', ] +edition = '2021' [dependencies] [target."cfg(windows)".dependencies.winapi] version = '0.3' features = ['winuser'] +[target.'cfg(target_os = "macos")'.dependencies] +core-foundation = '0.9' +mach = '0.3' +io-kit-sys = '0.2' diff --git a/README.md b/README.md index e93de66..fd9fb12 100644 --- a/README.md +++ b/README.md @@ -1,37 +1,14 @@ # `lock_keys` -[![Build Status][travis-badge]][travis-url] +[![CI/CD][ci-cd-badge]][ci-cd-url] [![Crates.io][crates-badge]][crates-url] [![Documentation][docs-badge]][docs-url] [![License][license-badge]][license-url] -[travis-badge]: https://travis-ci.org/risoflora/lock_keys.svg -[travis-url]: https://travis-ci.org/risoflora/lock_keys -[crates-badge]: https://img.shields.io/crates/v/lock_keys.svg -[crates-url]: https://crates.io/crates/lock_keys -[docs-badge]: https://docs.rs/lock_keys/badge.svg -[docs-url]: https://docs.rs/lock_keys -[license-badge]: https://img.shields.io/crates/l/lock_keys.svg -[license-url]: https://github.com/risoflora/lock_keys#license - `lock_keys` provides a cross platform way for lock keys handling. -Supported platforms: Linux ([Xlib](https://en.wikipedia.org/wiki/Xlib) static) and Windows ([winuser API](https://docs.microsoft.com/en-us/windows/win32/api/winuser)). - -## Example - -The example below shows how to turn on the Number Lock key: - -```rust -extern crate lock_keys; - -use lock_keys::*; - -fn main() { - let lockkey = LockKey::new(); - lockkey.enable(LockKeys::NumberLock).unwrap(); -} -``` +Supported platforms: Linux ([Xlib][xlib-wiki-url] static), +Windows ([winuser API][winuser-api-url]) and macOS ([IOKit][iokit-url]). ## Usage @@ -39,22 +16,39 @@ Add this to your `Cargo.toml`: ```ini [dependencies] -lock_keys = "1.0.0" +lock_keys = "*" ``` -and this to your crate root: +and then: ```rust -extern crate lock_keys; +use lock_keys::*; + +fn main() { + let lock_key = LockKey::new(); + lock_key.enable(LockKeys::CapitalLock).unwrap(); +} ``` ## Contributions -Pull Requests and Issues are welcome! +Pull Requests are welcome! =) ## License `lock_keys` is licensed under either of the following, at your option: -- Apache License 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) -- MIT License ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) +- [Apache License 2.0](LICENSE-APACHE) +- [MIT License](LICENSE-MIT) + +[ci-cd-badge]: https://github.com/risoflora/lock_keys/actions/workflows/CI.yml/badge.svg +[ci-cd-url]: https://github.com/risoflora/lock_keys/actions/workflows/CI.yml +[crates-badge]: https://img.shields.io/crates/v/lock_keys.svg +[crates-url]: https://crates.io/crates/lock_keys +[docs-badge]: https://docs.rs/lock_keys/badge.svg +[docs-url]: https://docs.rs/lock_keys +[license-badge]: https://img.shields.io/crates/l/lock_keys.svg +[license-url]: https://github.com/risoflora/lock_keys#license +[xlib-wiki-url]: https://en.wikipedia.org/wiki/Xlib +[winuser-api-url]: https://docs.microsoft.com/en-us/windows/win32/api/winuser +[iokit-url]: https://developer.apple.com/documentation/iokit diff --git a/examples/capital_lock.rs b/examples/capital_lock.rs new file mode 100644 index 0000000..bf3d87d --- /dev/null +++ b/examples/capital_lock.rs @@ -0,0 +1,8 @@ +//! How to toggle the state of the Capital Lock key. + +use lock_keys::*; + +fn main() { + let lock_key = LockKey::new(); + lock_key.toggle(LockKeys::CapitalLock).unwrap(); +} diff --git a/examples/numlock.rs b/examples/numlock.rs deleted file mode 100644 index 511a7e4..0000000 --- a/examples/numlock.rs +++ /dev/null @@ -1,10 +0,0 @@ -//! How to turn on the Number Lock key. - -extern crate lock_keys; - -use lock_keys::*; - -fn main() { - let lockkey = LockKey::new(); - lockkey.enable(LockKeys::NumberLock).unwrap(); -} diff --git a/src/lib.rs b/src/lib.rs index 25bc5fd..923eac6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,31 +1,29 @@ //! `lock_keys` provides a cross platform way for lock keys handling. //! -//! Supported platforms: Linux ([Xlib](https://en.wikipedia.org/wiki/Xlib) static) and Windows ([winuser API](https://docs.microsoft.com/en-us/windows/win32/api/winuser)). +//! Supported platforms: Linux ([Xlib](https://en.wikipedia.org/wiki/Xlib) static), Windows ([winuser API](https://docs.microsoft.com/en-us/windows/win32/api/winuser)) and macOS ([IOKit](https://developer.apple.com/documentation/iokit)). //! //! # Example //! -//! The example below shows how to turn on the Number Lock key: +//! The example below shows how to toggle the state of the Capital Lock key: //! //! ```rust -//! extern crate lock_keys; -//! //! use lock_keys::*; //! //! fn main() { -//! let lockkey = LockKey::new(); -//! lockkey.enable(LockKeys::NumberLock).unwrap(); +//! let lock_key = LockKey::new(); +//! lock_key.enable(LockKeys::CapitalLock).unwrap(); //! } //! ``` -#[cfg(target_os = "windows")] -extern crate winapi; - #[cfg(target_os = "linux")] mod linux; #[cfg(target_os = "windows")] mod windows; +#[cfg(target_os = "macos")] +mod macos; + use std::fmt; use std::io; @@ -39,6 +37,15 @@ pub enum LockKeyState { Disabled, } +impl LockKeyState { + pub fn toggle(self) -> Self { + match self { + Self::Enabled => Self::Disabled, + Self::Disabled => Self::Enabled, + } + } +} + impl From for LockKeyState { fn from(val: bool) -> Self { if val { @@ -108,111 +115,111 @@ mod tests { #[test] fn set() { - let lockkey = LockKey::new(); - let old_lockkey_state = lockkey.state(LockKeys::NumberLock).unwrap(); + let lock_key = LockKey::new(); + let old_lock_key_state = lock_key.state(LockKeys::CapitalLock).unwrap(); assert_eq!( - lockkey - .set(LockKeys::NumberLock, LockKeyState::Disabled) + lock_key + .set(LockKeys::CapitalLock, LockKeyState::Disabled) .unwrap(), LockKeyState::Disabled ); assert_eq!( - lockkey.state(LockKeys::NumberLock).unwrap(), + lock_key.state(LockKeys::CapitalLock).unwrap(), LockKeyState::Disabled ); assert_eq!( - lockkey - .set(LockKeys::NumberLock, LockKeyState::Enabled) + lock_key + .set(LockKeys::CapitalLock, LockKeyState::Enabled) .unwrap(), LockKeyState::Enabled ); assert_eq!( - lockkey.state(LockKeys::NumberLock).unwrap(), + lock_key.state(LockKeys::CapitalLock).unwrap(), LockKeyState::Enabled ); - lockkey - .set(LockKeys::NumberLock, old_lockkey_state) + lock_key + .set(LockKeys::CapitalLock, old_lock_key_state) .unwrap(); } #[test] fn enable() { - let lockkey = LockKey::new(); - let old_lockkey_state = lockkey.state(LockKeys::CapitalLock).unwrap(); - lockkey.disable(LockKeys::CapitalLock).unwrap(); + let lock_key = LockKey::new(); + let old_lock_key_state = lock_key.state(LockKeys::CapitalLock).unwrap(); + lock_key.disable(LockKeys::CapitalLock).unwrap(); assert_eq!( - lockkey.state(LockKeys::CapitalLock).unwrap(), + lock_key.state(LockKeys::CapitalLock).unwrap(), LockKeyState::Disabled ); assert_eq!( - lockkey.enable(LockKeys::CapitalLock).unwrap(), + lock_key.enable(LockKeys::CapitalLock).unwrap(), LockKeyState::Enabled ); assert_eq!( - lockkey.state(LockKeys::CapitalLock).unwrap(), + lock_key.state(LockKeys::CapitalLock).unwrap(), LockKeyState::Enabled ); - lockkey - .set(LockKeys::CapitalLock, old_lockkey_state) + lock_key + .set(LockKeys::CapitalLock, old_lock_key_state) .unwrap(); } #[test] fn disable() { - let lockkey = LockKey::new(); - let old_lockkey_state = lockkey.state(LockKeys::CapitalLock).unwrap(); - lockkey.enable(LockKeys::CapitalLock).unwrap(); + let lock_key = LockKey::new(); + let old_lock_key_state = lock_key.state(LockKeys::CapitalLock).unwrap(); + lock_key.enable(LockKeys::CapitalLock).unwrap(); assert_eq!( - lockkey.state(LockKeys::CapitalLock).unwrap(), + lock_key.state(LockKeys::CapitalLock).unwrap(), LockKeyState::Enabled ); assert_eq!( - lockkey.disable(LockKeys::CapitalLock).unwrap(), + lock_key.disable(LockKeys::CapitalLock).unwrap(), LockKeyState::Disabled ); assert_eq!( - lockkey.state(LockKeys::CapitalLock).unwrap(), + lock_key.state(LockKeys::CapitalLock).unwrap(), LockKeyState::Disabled ); - lockkey - .set(LockKeys::CapitalLock, old_lockkey_state) + lock_key + .set(LockKeys::CapitalLock, old_lock_key_state) .unwrap(); } #[test] fn toggle() { - let lockkey = LockKey::new(); - let old_lockkey_state = lockkey.state(LockKeys::ScrollingLock).unwrap(); - lockkey.enable(LockKeys::ScrollingLock).unwrap(); + let lock_key = LockKey::new(); + let old_lock_key_state = lock_key.state(LockKeys::CapitalLock).unwrap(); + lock_key.enable(LockKeys::CapitalLock).unwrap(); assert_eq!( - lockkey.toggle(LockKeys::ScrollingLock).unwrap(), + lock_key.toggle(LockKeys::CapitalLock).unwrap(), LockKeyState::Enabled ); assert_eq!( - lockkey.toggle(LockKeys::ScrollingLock).unwrap(), + lock_key.toggle(LockKeys::CapitalLock).unwrap(), LockKeyState::Disabled ); - lockkey - .set(LockKeys::ScrollingLock, old_lockkey_state) + lock_key + .set(LockKeys::CapitalLock, old_lock_key_state) .unwrap(); } #[test] fn state() { - let lockkey = LockKey::new(); - let old_lockkey_state = lockkey.state(LockKeys::NumberLock).unwrap(); - lockkey.enable(LockKeys::NumberLock).unwrap(); + let lock_key = LockKey::new(); + let old_lock_key_state = lock_key.state(LockKeys::CapitalLock).unwrap(); + lock_key.enable(LockKeys::CapitalLock).unwrap(); assert_eq!( - lockkey.state(LockKeys::NumberLock).unwrap(), + lock_key.state(LockKeys::CapitalLock).unwrap(), LockKeyState::Enabled ); - lockkey.disable(LockKeys::NumberLock).unwrap(); + lock_key.disable(LockKeys::CapitalLock).unwrap(); assert_eq!( - lockkey.toggle(LockKeys::NumberLock).unwrap(), + lock_key.toggle(LockKeys::CapitalLock).unwrap(), LockKeyState::Disabled ); - lockkey - .set(LockKeys::ScrollingLock, old_lockkey_state) + lock_key + .set(LockKeys::CapitalLock, old_lock_key_state) .unwrap(); } } diff --git a/src/linux.rs b/src/linux.rs index 27012c2..2641388 100644 --- a/src/linux.rs +++ b/src/linux.rs @@ -3,7 +3,7 @@ use std::mem; use std::os::raw::{c_char, c_int, c_uchar, c_uint, c_ulong, c_ushort}; use std::ptr; -use {LockKey, LockKeyResult, LockKeyState, LockKeyWrapper, LockKeys}; +use crate::{LockKey, LockKeyResult, LockKeyState, LockKeyWrapper, LockKeys}; #[doc(hidden)] #[allow(non_upper_case_globals)] @@ -44,7 +44,6 @@ pub struct XkbStateRec { #[doc(hidden)] pub type XkbStatePtr = *mut XkbStateRec; -#[doc(hidden)] #[link(name = "X11")] extern "C" { pub fn XOpenDisplay(display_name: *const c_char) -> *mut Display; @@ -65,15 +64,7 @@ extern "C" { #[doc(hidden)] #[macro_export] -macro_rules! xkb_raise_error { - ($ident:expr) => { - Error::new(ErrorKind::Other, $ident) - }; -} - -#[doc(hidden)] -#[macro_export] -macro_rules! xkb_lockkey_mask { +macro_rules! xkb_lock_key_mask { ($handle:expr,$key:expr) => { XkbKeysymToModifiers( $handle as *mut _, @@ -82,38 +73,7 @@ macro_rules! xkb_lockkey_mask { LockKeys::NumberLock => XK_Num_Lock, LockKeys::ScrollingLock => XK_Scroll_Lock, } as KeySym, - ); - }; -} - -#[doc(hidden)] -#[macro_export] -macro_rules! xkb_lockkey_set { - ($handle:expr,$mask:expr,$enabled:expr) => { - XkbLockModifiers( - $handle as *mut _, - XkbUseCoreKbd, - $mask, - if $enabled { $mask } else { 0 }, - ) == 1 - }; -} - -#[doc(hidden)] -#[macro_export] -macro_rules! xkb_lockkey_state { - ($handle:expr,$mask:expr) => {{ - let mut state: XkbStateRec = mem::zeroed(); - XkbGetState($handle as *mut _, XkbUseCoreKbd, &mut state); - (state.locked_mods as c_uint) & $mask != 0 - }}; -} - -#[doc(hidden)] -#[macro_export] -macro_rules! xkb_lockkey_set_error { - () => { - xkb_raise_error!("XkbLockModifiers") + ) }; } @@ -128,12 +88,17 @@ impl LockKeyWrapper for LockKey { /// Sets a new state for the lock key using [Xlib](https://en.wikipedia.org/wiki/Xlib). fn set(&self, key: LockKeys, state: LockKeyState) -> LockKeyResult { unsafe { - let mask = xkb_lockkey_mask!(self.handle, key); - if xkb_lockkey_set!(self.handle, mask, state.into()) { - Ok(state) - } else { - Err(xkb_lockkey_set_error!()) + let mask = xkb_lock_key_mask!(self.handle, key); + if XkbLockModifiers( + self.handle as *mut _, + XkbUseCoreKbd, + mask, + if state.into() { mask } else { 0 }, + ) != 1 + { + return Err(Error::new(ErrorKind::Other, "XkbLockModifiers")); } + Ok(state) } } @@ -149,23 +114,18 @@ impl LockKeyWrapper for LockKey { /// Toggles the lock key state returning its previous state using [Xlib](https://en.wikipedia.org/wiki/Xlib). fn toggle(&self, key: LockKeys) -> LockKeyResult { - unsafe { - let mask = xkb_lockkey_mask!(self.handle, key); - let state = xkb_lockkey_state!(self.handle, mask); - if xkb_lockkey_set!(self.handle, mask, !state) { - Ok(state.into()) - } else { - Err(xkb_lockkey_set_error!()) - } - } + let state = self.state(key)?; + self.set(key, state.toggle())?; + Ok(state) } /// Retrieves the lock key state using [Xlib](https://en.wikipedia.org/wiki/Xlib). fn state(&self, key: LockKeys) -> LockKeyResult { unsafe { - let mask = xkb_lockkey_mask!(self.handle, key); - let state = xkb_lockkey_state!(self.handle, mask); - Ok(state.into()) + let mask = xkb_lock_key_mask!(self.handle, key); + let mut state: XkbStateRec = mem::zeroed(); + XkbGetState(self.handle as *mut _, XkbUseCoreKbd, &mut state); + Ok(((state.locked_mods as c_uint) & mask != 0).into()) } } } diff --git a/src/macos.rs b/src/macos.rs new file mode 100644 index 0000000..4aeead6 --- /dev/null +++ b/src/macos.rs @@ -0,0 +1,131 @@ +use std::{ + io::{Error, ErrorKind}, + os::raw::{c_char, c_int, c_uint}, +}; + +use core_foundation::base::{CFRelease, CFTypeRef}; +use io_kit_sys::{ + kIOMasterPortDefault, types::io_connect_t, IOObjectRelease, IOServiceClose, + IOServiceGetMatchingService, IOServiceMatching, IOServiceOpen, +}; +use mach::{ + kern_return::{kern_return_t, KERN_SUCCESS}, + traps::mach_task_self, +}; + +use crate::{LockKey, LockKeyHandle, LockKeyResult, LockKeyState, LockKeyWrapper, LockKeys}; + +#[allow(non_upper_case_globals)] +const kIOHIDSystemClass: &[u8; 12] = b"IOHIDSystem\0"; +#[allow(non_upper_case_globals)] +const kIOHIDParamConnectType: c_uint = 1; +#[allow(non_upper_case_globals)] +const kIOHIDCapsLockState: c_int = 0x00000001; +#[allow(non_upper_case_globals)] +const kIOHIDNumLockState: c_int = 0x00000002; + +extern "C" { + #[doc(hidden)] + pub fn IOHIDSetModifierLockState( + handle: io_connect_t, + selector: c_int, + state: bool, + ) -> kern_return_t; + #[doc(hidden)] + pub fn IOHIDGetModifierLockState( + handle: io_connect_t, + selector: c_int, + state: &bool, + ) -> kern_return_t; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! io_kit_raise_error { + ($ident:expr) => { + Error::new(ErrorKind::Other, $ident) + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! io_kit_check_modifier_lock_state { + ($ident:expr) => { + if $ident != KERN_SUCCESS { + return Err(io_kit_raise_error!("IOHIDModifierLockState")); + } + }; +} + +impl LockKeyWrapper for LockKey { + /// Creates a new lock key object using [IOKit](https://developer.apple.com/documentation/iokit) for handling. + fn new() -> Self { + let mut ioc: io_connect_t = 0; + unsafe { + let mdict = IOServiceMatching(kIOHIDSystemClass.as_ptr() as *const c_char); + let ios = IOServiceGetMatchingService(kIOMasterPortDefault, mdict); + if ios == 0 && !mdict.is_null() { + CFRelease(mdict as CFTypeRef); + panic!("IOServiceGetMatchingService() failed"); + } + let kr = IOServiceOpen(ios, mach_task_self(), kIOHIDParamConnectType, &mut ioc); + IOObjectRelease(ios); + if kr != KERN_SUCCESS { + panic!("IOServiceOpen() failed"); + } + } + LockKey { + handle: ioc as *mut LockKeyHandle, + } + } + + /// Sets a new state for the lock key using [IOKit](https://developer.apple.com/documentation/iokit). + fn set(&self, key: LockKeys, state: LockKeyState) -> LockKeyResult { + io_kit_check_modifier_lock_state!(unsafe { + IOHIDSetModifierLockState(self.handle as io_connect_t, key.into(), state.into()) + }); + Ok(state) + } + + /// Enables the lock key using [IOKit](https://developer.apple.com/documentation/iokit). + fn enable(&self, key: LockKeys) -> LockKeyResult { + self.set(key, LockKeyState::Enabled) + } + + /// Disables the lock key using [IOKit](https://developer.apple.com/documentation/iokit). + fn disable(&self, key: LockKeys) -> LockKeyResult { + self.set(key, LockKeyState::Disabled) + } + + /// Toggles the lock key state returning its previous state using [IOKit](https://developer.apple.com/documentation/iokit). + fn toggle(&self, key: LockKeys) -> LockKeyResult { + let state = self.state(key)?; + self.set(key, state.toggle())?; + Ok(state) + } + + /// Retrieves the lock key state using [IOKit](https://developer.apple.com/documentation/iokit). + fn state(&self, key: LockKeys) -> LockKeyResult { + let state: bool = false; + io_kit_check_modifier_lock_state!(unsafe { + IOHIDGetModifierLockState(self.handle as io_connect_t, key.into(), &state) + }); + Ok(state.into()) + } +} + +impl From for c_int { + fn from(val: LockKeys) -> Self { + match val { + LockKeys::CapitalLock => kIOHIDCapsLockState, + LockKeys::NumberLock => kIOHIDNumLockState, + LockKeys::ScrollingLock => todo!(), + } + } +} + +impl Drop for LockKey { + fn drop(&mut self) { + unsafe { IOServiceClose(self.handle as io_connect_t) }; + } +} diff --git a/src/windows.rs b/src/windows.rs index ebdf10a..213dd52 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -6,11 +6,11 @@ use winapi::um::winuser::{ VK_SCROLL, }; -use {LockKey, LockKeyResult, LockKeyState, LockKeyWrapper, LockKeys}; +use crate::{LockKey, LockKeyResult, LockKeyState, LockKeyWrapper, LockKeys}; #[doc(hidden)] #[macro_export] -macro_rules! lockkey_to_vkkey { +macro_rules! lock_key_to_vkkey { ($key:expr) => { match $key { LockKeys::CapitalLock => VK_CAPITAL, @@ -20,24 +20,6 @@ macro_rules! lockkey_to_vkkey { }; } -#[doc(hidden)] -#[macro_export] -macro_rules! vk_state { - ($key:expr) => { - GetKeyState(lockkey_to_vkkey!($key)) == 1 - }; -} - -#[doc(hidden)] -#[macro_export] -macro_rules! send_vkkey { - ($key:expr) => { - let key = lockkey_to_vkkey!($key) as BYTE; - keybd_event(key, 0x45, KEYEVENTF_EXTENDEDKEY | 0, 0); - keybd_event(key, 0x45, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0); - }; -} - impl LockKeyWrapper for LockKey { /// Creates a new lock key object using [winuser API](https://docs.microsoft.com/en-us/windows/win32/api/winuser) for handling. fn new() -> Self { @@ -49,10 +31,9 @@ impl LockKeyWrapper for LockKey { /// Sets a new state for the lock key using [winuser API](https://docs.microsoft.com/en-us/windows/win32/api/winuser). fn set(&self, key: LockKeys, state: LockKeyState) -> LockKeyResult { unsafe { - let key_state = vk_state!(key); - if key_state != state.into() { - send_vkkey!(key); - } + let key = lock_key_to_vkkey!(key) as BYTE; + keybd_event(key, 0x45, KEYEVENTF_EXTENDEDKEY | 0, 0); + keybd_event(key, 0x45, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0); } Ok(state) } @@ -69,16 +50,14 @@ impl LockKeyWrapper for LockKey { /// Toggles the lock key state returning its previous state using [winuser API](https://docs.microsoft.com/en-us/windows/win32/api/winuser). fn toggle(&self, key: LockKeys) -> LockKeyResult { - unsafe { - let old_key_state = vk_state!(key); - send_vkkey!(key); - Ok(old_key_state.into()) - } + let state = self.state(key)?; + self.set(key, state.toggle())?; + Ok(state) } /// Retrieves the lock key state using [winuser API](https://docs.microsoft.com/en-us/windows/win32/api/winuser). fn state(&self, key: LockKeys) -> LockKeyResult { - let key_state = unsafe { GetKeyState(lockkey_to_vkkey!(key)) == 1 }; + let key_state = unsafe { GetKeyState(lock_key_to_vkkey!(key)) == 1 }; Ok(key_state.into()) } }