mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-04-22 08:38:33 +02:00
feat(wm): add optional alt focus hack
This commit adds an optional focusing hack using simulated ALT key presses to ensure that focus changes always succeed. As noted in the documentation for LockSetForegroundWindow, the system automatically enables calls to SetForegroundWindow if the user presses the ALT key.
This commit is contained in:
@@ -104,6 +104,7 @@ pub enum SocketMessage {
|
|||||||
ReloadConfiguration,
|
ReloadConfiguration,
|
||||||
WatchConfiguration(bool),
|
WatchConfiguration(bool),
|
||||||
CompleteConfiguration,
|
CompleteConfiguration,
|
||||||
|
AltFocusHack(bool),
|
||||||
ActiveWindowBorder(bool),
|
ActiveWindowBorder(bool),
|
||||||
ActiveWindowBorderColour(WindowKind, u32, u32, u32),
|
ActiveWindowBorderColour(WindowKind, u32, u32, u32),
|
||||||
ActiveWindowBorderWidth(i32),
|
ActiveWindowBorderWidth(i32),
|
||||||
|
|||||||
@@ -168,6 +168,7 @@ lazy_static! {
|
|||||||
pub static INITIAL_CONFIGURATION_LOADED: AtomicBool = AtomicBool::new(false);
|
pub static INITIAL_CONFIGURATION_LOADED: AtomicBool = AtomicBool::new(false);
|
||||||
pub static CUSTOM_FFM: AtomicBool = AtomicBool::new(false);
|
pub static CUSTOM_FFM: AtomicBool = AtomicBool::new(false);
|
||||||
pub static SESSION_ID: AtomicU32 = AtomicU32::new(0);
|
pub static SESSION_ID: AtomicU32 = AtomicU32::new(0);
|
||||||
|
pub static ALT_FOCUS_HACK: AtomicBool = AtomicBool::new(false);
|
||||||
pub static BORDER_ENABLED: AtomicBool = AtomicBool::new(false);
|
pub static BORDER_ENABLED: AtomicBool = AtomicBool::new(false);
|
||||||
pub static BORDER_HWND: AtomicIsize = AtomicIsize::new(0);
|
pub static BORDER_HWND: AtomicIsize = AtomicIsize::new(0);
|
||||||
pub static BORDER_HIDDEN: AtomicBool = AtomicBool::new(false);
|
pub static BORDER_HIDDEN: AtomicBool = AtomicBool::new(false);
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ use crate::window_manager::WindowManager;
|
|||||||
use crate::windows_api::WindowsApi;
|
use crate::windows_api::WindowsApi;
|
||||||
use crate::Notification;
|
use crate::Notification;
|
||||||
use crate::NotificationEvent;
|
use crate::NotificationEvent;
|
||||||
|
use crate::ALT_FOCUS_HACK;
|
||||||
use crate::BORDER_COLOUR_CURRENT;
|
use crate::BORDER_COLOUR_CURRENT;
|
||||||
use crate::BORDER_COLOUR_SINGLE;
|
use crate::BORDER_COLOUR_SINGLE;
|
||||||
use crate::BORDER_COLOUR_STACK;
|
use crate::BORDER_COLOUR_STACK;
|
||||||
@@ -907,6 +908,9 @@ impl WindowManager {
|
|||||||
|
|
||||||
WindowsApi::invalidate_border_rect()?;
|
WindowsApi::invalidate_border_rect()?;
|
||||||
}
|
}
|
||||||
|
SocketMessage::AltFocusHack(enable) => {
|
||||||
|
ALT_FOCUS_HACK.store(enable, Ordering::SeqCst);
|
||||||
|
}
|
||||||
SocketMessage::NotificationSchema => {
|
SocketMessage::NotificationSchema => {
|
||||||
let notification = schema_for!(Notification);
|
let notification = schema_for!(Notification);
|
||||||
let schema = serde_json::to_string_pretty(¬ification)?;
|
let schema = serde_json::to_string_pretty(¬ification)?;
|
||||||
|
|||||||
@@ -97,7 +97,9 @@ impl WindowManager {
|
|||||||
//
|
//
|
||||||
// This check ensures that we only update the focused monitor when the window
|
// This check ensures that we only update the focused monitor when the window
|
||||||
// triggering monitor reconciliation is known to not be tied to a specific monitor.
|
// triggering monitor reconciliation is known to not be tied to a specific monitor.
|
||||||
if window.class()? != "OleMainThreadWndClass" {
|
if window.class()? != "OleMainThreadWndClass"
|
||||||
|
&& self.focused_monitor_idx() != monitor_idx
|
||||||
|
{
|
||||||
self.focus_monitor(monitor_idx)?;
|
self.focus_monitor(monitor_idx)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ use std::convert::TryFrom;
|
|||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use std::fmt::Formatter;
|
use std::fmt::Formatter;
|
||||||
use std::fmt::Write as _;
|
use std::fmt::Write as _;
|
||||||
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
use color_eyre::eyre::anyhow;
|
use color_eyre::eyre::anyhow;
|
||||||
use color_eyre::Result;
|
use color_eyre::Result;
|
||||||
@@ -11,6 +12,9 @@ use serde::ser::SerializeStruct;
|
|||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use serde::Serializer;
|
use serde::Serializer;
|
||||||
use windows::Win32::Foundation::HWND;
|
use windows::Win32::Foundation::HWND;
|
||||||
|
use winput::press;
|
||||||
|
use winput::release;
|
||||||
|
use winput::Vk;
|
||||||
|
|
||||||
use komorebi_core::ApplicationIdentifier;
|
use komorebi_core::ApplicationIdentifier;
|
||||||
use komorebi_core::HidingBehaviour;
|
use komorebi_core::HidingBehaviour;
|
||||||
@@ -20,6 +24,7 @@ use crate::styles::ExtendedWindowStyle;
|
|||||||
use crate::styles::WindowStyle;
|
use crate::styles::WindowStyle;
|
||||||
use crate::window_manager_event::WindowManagerEvent;
|
use crate::window_manager_event::WindowManagerEvent;
|
||||||
use crate::windows_api::WindowsApi;
|
use crate::windows_api::WindowsApi;
|
||||||
|
use crate::ALT_FOCUS_HACK;
|
||||||
use crate::BORDER_OVERFLOW_IDENTIFIERS;
|
use crate::BORDER_OVERFLOW_IDENTIFIERS;
|
||||||
use crate::FLOAT_IDENTIFIERS;
|
use crate::FLOAT_IDENTIFIERS;
|
||||||
use crate::HIDDEN_HWNDS;
|
use crate::HIDDEN_HWNDS;
|
||||||
@@ -268,7 +273,13 @@ impl Window {
|
|||||||
let mut foregrounded = false;
|
let mut foregrounded = false;
|
||||||
let mut tried_resetting_foreground_access = false;
|
let mut tried_resetting_foreground_access = false;
|
||||||
let mut max_attempts = 10;
|
let mut max_attempts = 10;
|
||||||
|
|
||||||
|
let hotkey_uses_alt = WindowsApi::alt_is_pressed();
|
||||||
while !foregrounded && max_attempts > 0 {
|
while !foregrounded && max_attempts > 0 {
|
||||||
|
if ALT_FOCUS_HACK.load(Ordering::SeqCst) {
|
||||||
|
press(Vk::Alt);
|
||||||
|
}
|
||||||
|
|
||||||
match WindowsApi::set_foreground_window(self.hwnd()) {
|
match WindowsApi::set_foreground_window(self.hwnd()) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
foregrounded = true;
|
foregrounded = true;
|
||||||
@@ -289,6 +300,10 @@ impl Window {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if ALT_FOCUS_HACK.load(Ordering::SeqCst) && !hotkey_uses_alt {
|
||||||
|
release(Vk::Alt);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Center cursor in Window
|
// Center cursor in Window
|
||||||
|
|||||||
@@ -388,7 +388,9 @@ impl WindowManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Gotta reset the focus or the movement will feel "off"
|
// Gotta reset the focus or the movement will feel "off"
|
||||||
focused_ws.focus_container(focused_container_idx);
|
if before_count != after_count {
|
||||||
|
focused_ws.focus_container(focused_container_idx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ use windows::Win32::System::Threading::PROCESS_NAME_WIN32;
|
|||||||
use windows::Win32::System::Threading::PROCESS_QUERY_INFORMATION;
|
use windows::Win32::System::Threading::PROCESS_QUERY_INFORMATION;
|
||||||
use windows::Win32::UI::HiDpi::SetProcessDpiAwarenessContext;
|
use windows::Win32::UI::HiDpi::SetProcessDpiAwarenessContext;
|
||||||
use windows::Win32::UI::HiDpi::DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2;
|
use windows::Win32::UI::HiDpi::DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2;
|
||||||
|
use windows::Win32::UI::Input::KeyboardAndMouse::GetKeyState;
|
||||||
use windows::Win32::UI::Input::KeyboardAndMouse::SendInput;
|
use windows::Win32::UI::Input::KeyboardAndMouse::SendInput;
|
||||||
use windows::Win32::UI::Input::KeyboardAndMouse::SetFocus;
|
use windows::Win32::UI::Input::KeyboardAndMouse::SetFocus;
|
||||||
use windows::Win32::UI::Input::KeyboardAndMouse::INPUT;
|
use windows::Win32::UI::Input::KeyboardAndMouse::INPUT;
|
||||||
@@ -60,6 +61,7 @@ use windows::Win32::UI::Input::KeyboardAndMouse::INPUT_MOUSE;
|
|||||||
use windows::Win32::UI::Input::KeyboardAndMouse::MOUSEEVENTF_LEFTDOWN;
|
use windows::Win32::UI::Input::KeyboardAndMouse::MOUSEEVENTF_LEFTDOWN;
|
||||||
use windows::Win32::UI::Input::KeyboardAndMouse::MOUSEEVENTF_LEFTUP;
|
use windows::Win32::UI::Input::KeyboardAndMouse::MOUSEEVENTF_LEFTUP;
|
||||||
use windows::Win32::UI::Input::KeyboardAndMouse::MOUSEINPUT;
|
use windows::Win32::UI::Input::KeyboardAndMouse::MOUSEINPUT;
|
||||||
|
use windows::Win32::UI::Input::KeyboardAndMouse::VK_MENU;
|
||||||
use windows::Win32::UI::Shell::Common::DEVICE_SCALE_FACTOR;
|
use windows::Win32::UI::Shell::Common::DEVICE_SCALE_FACTOR;
|
||||||
use windows::Win32::UI::Shell::GetScaleFactorForMonitor;
|
use windows::Win32::UI::Shell::GetScaleFactorForMonitor;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::AllowSetForegroundWindow;
|
use windows::Win32::UI::WindowsAndMessaging::AllowSetForegroundWindow;
|
||||||
@@ -797,6 +799,13 @@ impl WindowsApi {
|
|||||||
.process()
|
.process()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn alt_is_pressed() -> bool {
|
||||||
|
let state = unsafe { GetKeyState(i32::from(VK_MENU.0)) };
|
||||||
|
#[allow(clippy::cast_sign_loss)]
|
||||||
|
let actual = (state as u16) & 0x8000;
|
||||||
|
actual != 0
|
||||||
|
}
|
||||||
|
|
||||||
pub fn left_click() -> u32 {
|
pub fn left_click() -> u32 {
|
||||||
let inputs = [
|
let inputs = [
|
||||||
INPUT {
|
INPUT {
|
||||||
|
|||||||
@@ -288,6 +288,10 @@ CompleteConfiguration() {
|
|||||||
RunWait, komorebic.exe complete-configuration, , Hide
|
RunWait, komorebic.exe complete-configuration, , Hide
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AltFocusHack(boolean_state) {
|
||||||
|
RunWait, komorebic.exe alt-focus-hack %boolean_state%, , Hide
|
||||||
|
}
|
||||||
|
|
||||||
WindowHidingBehaviour(hiding_behaviour) {
|
WindowHidingBehaviour(hiding_behaviour) {
|
||||||
RunWait, komorebic.exe window-hiding-behaviour %hiding_behaviour%, , Hide
|
RunWait, komorebic.exe window-hiding-behaviour %hiding_behaviour%, , Hide
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -513,6 +513,12 @@ struct FormatAppSpecificConfiguration {
|
|||||||
path: String,
|
path: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Parser, AhkFunction)]
|
||||||
|
struct AltFocusHack {
|
||||||
|
#[clap(value_enum)]
|
||||||
|
boolean_state: BooleanState,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
#[clap(author, about, version)]
|
#[clap(author, about, version)]
|
||||||
struct Opts {
|
struct Opts {
|
||||||
@@ -719,6 +725,9 @@ enum SubCommand {
|
|||||||
WatchConfiguration(WatchConfiguration),
|
WatchConfiguration(WatchConfiguration),
|
||||||
/// Signal that the final configuration option has been sent
|
/// Signal that the final configuration option has been sent
|
||||||
CompleteConfiguration,
|
CompleteConfiguration,
|
||||||
|
/// Enable or disable a hack simulating ALT key presses to ensure focus changes succeed
|
||||||
|
#[clap(arg_required_else_help = true)]
|
||||||
|
AltFocusHack(AltFocusHack),
|
||||||
/// Set the window behaviour when switching workspaces / cycling stacks
|
/// Set the window behaviour when switching workspaces / cycling stacks
|
||||||
#[clap(arg_required_else_help = true)]
|
#[clap(arg_required_else_help = true)]
|
||||||
WindowHidingBehaviour(WindowHidingBehaviour),
|
WindowHidingBehaviour(WindowHidingBehaviour),
|
||||||
@@ -1319,6 +1328,9 @@ fn main() -> Result<()> {
|
|||||||
SubCommand::CompleteConfiguration => {
|
SubCommand::CompleteConfiguration => {
|
||||||
send_message(&SocketMessage::CompleteConfiguration.as_bytes()?)?;
|
send_message(&SocketMessage::CompleteConfiguration.as_bytes()?)?;
|
||||||
}
|
}
|
||||||
|
SubCommand::AltFocusHack(arg) => {
|
||||||
|
send_message(&SocketMessage::AltFocusHack(arg.boolean_state.into()).as_bytes()?)?;
|
||||||
|
}
|
||||||
SubCommand::IdentifyObjectNameChangeApplication(target) => {
|
SubCommand::IdentifyObjectNameChangeApplication(target) => {
|
||||||
send_message(
|
send_message(
|
||||||
&SocketMessage::IdentifyObjectNameChangeApplication(target.identifier, target.id)
|
&SocketMessage::IdentifyObjectNameChangeApplication(target.identifier, target.id)
|
||||||
|
|||||||
Reference in New Issue
Block a user