diff --git a/src/app.rs b/src/app.rs index 09b2f31..f4984bf 100644 --- a/src/app.rs +++ b/src/app.rs @@ -10,6 +10,7 @@ use crate::utils::{ }; use anyhow::{anyhow, Result}; +use indexmap::IndexSet; use once_cell::sync::Lazy; use std::collections::HashMap; use windows::core::PCWSTR; @@ -208,7 +209,7 @@ impl App { if modifier == app.config.switch_apps_hotkey.get_modifier() { if let Some(state) = app.switch_apps_state.take() { if let Some((_, id)) = state.apps.get(state.index) { - set_foregound_window(*id)?; + set_foregound_window(*id); } for (hicon, _) in state.apps { unsafe { DestroyIcon(hicon) }; @@ -220,11 +221,12 @@ impl App { WM_USER_HOOTKEY => { debug!("message WM_USER_HOOTKEY {}", wparam.0); let app = get_app(hwnd)?; + let reverse = lparam.0 == 1; let hotkey_id = wparam.0 as u32; if hotkey_id == app.config.switch_windows_hotkey.id { - app.switch_windows()?; + app.switch_windows(reverse)?; } else if hotkey_id == app.config.switch_apps_hotkey.id { - app.switch_apps()?; + app.switch_apps(reverse)?; unsafe { RedrawWindow(hwnd, None, HRGN::default(), RDW_ERASE | RDW_INVALIDATE) }; @@ -263,8 +265,8 @@ impl App { Ok(unsafe { DefWindowProcW(hwnd, msg, wparam, lparam) }) } - pub fn switch_windows(&mut self) -> Result { - debug!("switch windows enter {:?}", self.switch_apps_state); + pub fn switch_windows(&mut self, reverse: bool) -> Result { + debug!("switch windows enter {:?}", self.switch_windows_state); let windows = list_windows(false)?; let foreground_window = get_foreground_window(); let foreground_pid = get_window_pid(foreground_window); @@ -283,8 +285,9 @@ impl App { let current_id = windows[0]; let mut index = 1; let mut state_id = current_id; + let mut state_windows = vec![]; if windows_len > 2 { - if let Some((cache_module_path, cache_id, cache_index)) = + if let Some((cache_module_path, cache_id, cache_index, cache_windows)) = self.switch_windows_state.cache.as_ref() { if cache_module_path == &module_path { @@ -297,35 +300,63 @@ impl App { } } } else { - index = (cache_index + 1).min(windows_len - 1); state_id = *cache_id; + let mut windows_set: IndexSet = + windows.iter().map(|v| v.0).collect(); + for id in cache_windows { + if windows_set.contains(id) { + state_windows.push(*id); + windows_set.remove(id); + } + } + state_windows.extend(windows_set); + index = if reverse { + if *cache_index == 0 { + windows_len - 1 + } else { + cache_index - 1 + } + } else if *cache_index >= windows_len - 1 { + 0 + } else { + cache_index + 1 + }; } } } } + if state_windows.is_empty() { + state_windows = windows.iter().map(|v| v.0).collect(); + } + let hwnd = HWND(state_windows[index]); self.switch_windows_state = SwitchWindowsState { - cache: Some((module_path, state_id, index)), + cache: Some((module_path, state_id, index, state_windows)), modifier_released: false, }; - let hwnd = windows[index]; debug!( "switch windows done {:?} {:?}", hwnd, self.switch_windows_state ); - set_foregound_window(hwnd)?; + set_foregound_window(hwnd); Ok(true) } } } - fn switch_apps(&mut self) -> Result<()> { + fn switch_apps(&mut self, reverse: bool) -> Result<()> { debug!("switch apps enter {:?}", self.switch_apps_state); if let Some(state) = self.switch_apps_state.as_mut() { - if state.index + 1 < state.apps.len() { - state.index += 1; - } else { + if reverse { + if state.index == 0 { + state.index = state.apps.len() - 1; + } else { + state.index -= 1; + } + } else if state.index == state.apps.len() - 1 { state.index = 0; + } else { + state.index += 1; }; return Ok(()); } @@ -466,7 +497,7 @@ fn get_app(hwnd: HWND) -> Result<&'static mut App> { #[derive(Debug)] struct SwitchWindowsState { - cache: Option<(String, HWND, usize)>, + cache: Option<(String, HWND, usize, Vec)>, modifier_released: bool, } diff --git a/src/keyboard.rs b/src/keyboard.rs index 1f00702..db38bd3 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -5,16 +5,20 @@ use crate::{ }; use anyhow::{anyhow, Result}; -use windows::Win32::Foundation::{HWND, LPARAM, LRESULT, WPARAM}; use windows::Win32::System::LibraryLoader::GetModuleHandleW; use windows::Win32::UI::Input::KeyboardAndMouse::VIRTUAL_KEY; use windows::Win32::UI::WindowsAndMessaging::{ CallNextHookEx, SendMessageW, SetWindowsHookExW, UnhookWindowsHookEx, HHOOK, KBDLLHOOKSTRUCT, WH_KEYBOARD_LL, }; +use windows::Win32::{ + Foundation::{HWND, LPARAM, LRESULT, WPARAM}, + UI::Input::KeyboardAndMouse::{VK_LSHIFT, VK_RSHIFT}, +}; static mut KEYBOARD_STATE: Vec = vec![]; static mut WINDOW: HWND = HWND(0); +static mut IS_SHIFT_PRESSED: bool = false; #[derive(Debug)] pub struct KeyboardListener { @@ -37,7 +41,6 @@ impl KeyboardListener { .map(|hotkey| HotKeyState { hotkey: (*hotkey).clone(), is_modifier_pressed: false, - count_code_key_pressed: 0, }) .collect() } @@ -59,7 +62,6 @@ impl Drop for KeyboardListener { struct HotKeyState { hotkey: Hotkey, is_modifier_pressed: bool, - count_code_key_pressed: u32, } unsafe extern "system" fn keyboard_proc(code: i32, w_param: WPARAM, l_param: LPARAM) -> LRESULT { @@ -68,6 +70,9 @@ unsafe extern "system" fn keyboard_proc(code: i32, w_param: WPARAM, l_param: LPA let vk_code = VIRTUAL_KEY(kbd_data.vkCode as _); let mut is_modifier = false; let is_key_pressed = || kbd_data.flags.0 & 128 == 0; + if [VK_LSHIFT, VK_RSHIFT].contains(&vk_code) { + IS_SHIFT_PRESSED = is_key_pressed(); + } for state in KEYBOARD_STATE.iter_mut() { if state.hotkey.modifier.contains(&vk_code) { is_modifier = true; @@ -84,18 +89,16 @@ unsafe extern "system" fn keyboard_proc(code: i32, w_param: WPARAM, l_param: LPA ) }; } - state.count_code_key_pressed = 0; } } if !is_modifier { for state in KEYBOARD_STATE.iter_mut() { if vk_code.0 == state.hotkey.code && is_key_pressed() && state.is_modifier_pressed { - let count = state.count_code_key_pressed; - state.count_code_key_pressed += 1; let id = state.hotkey.id; if id != SWITCH_WINDOWS_HOTKEY_ID || !IS_FOREGROUND_IN_BLACKLIST { + let reverse = if IS_SHIFT_PRESSED { 1 } else { 0 }; unsafe { - SendMessageW(WINDOW, WM_USER_HOOTKEY, WPARAM(id as _), LPARAM(count as _)) + SendMessageW(WINDOW, WM_USER_HOOTKEY, WPARAM(id as _), LPARAM(reverse)) }; return LRESULT(1); } diff --git a/src/utils/window.rs b/src/utils/window.rs index fe77aca..e0efed9 100644 --- a/src/utils/window.rs +++ b/src/utils/window.rs @@ -196,13 +196,13 @@ pub fn get_window_exe(hwnd: HWND) -> Option { module_path.split('\\').map(|v| v.to_string()).last() } -pub fn set_foregound_window(hwnd: HWND) -> Result<()> { +pub fn set_foregound_window(hwnd: HWND) { unsafe { if is_iconic_window(hwnd) { ShowWindow(hwnd, SW_RESTORE); } if hwnd == get_foreground_window() { - return Ok(()); + return; } if SetForegroundWindow(hwnd).ok().is_err() { AllocConsole(); @@ -212,7 +212,6 @@ pub fn set_foregound_window(hwnd: HWND) -> Result<()> { SetForegroundWindow(hwnd); } }; - Ok(()) } pub fn get_foreground_window() -> HWND {