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:
LGUG2Z
2023-01-21 13:29:06 -08:00
parent f8120f6b11
commit 925f3bd87b
9 changed files with 52 additions and 2 deletions

View File

@@ -104,6 +104,7 @@ pub enum SocketMessage {
ReloadConfiguration,
WatchConfiguration(bool),
CompleteConfiguration,
AltFocusHack(bool),
ActiveWindowBorder(bool),
ActiveWindowBorderColour(WindowKind, u32, u32, u32),
ActiveWindowBorderWidth(i32),

View File

@@ -168,6 +168,7 @@ lazy_static! {
pub static INITIAL_CONFIGURATION_LOADED: AtomicBool = AtomicBool::new(false);
pub static CUSTOM_FFM: AtomicBool = AtomicBool::new(false);
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_HWND: AtomicIsize = AtomicIsize::new(0);
pub static BORDER_HIDDEN: AtomicBool = AtomicBool::new(false);

View File

@@ -42,6 +42,7 @@ use crate::window_manager::WindowManager;
use crate::windows_api::WindowsApi;
use crate::Notification;
use crate::NotificationEvent;
use crate::ALT_FOCUS_HACK;
use crate::BORDER_COLOUR_CURRENT;
use crate::BORDER_COLOUR_SINGLE;
use crate::BORDER_COLOUR_STACK;
@@ -907,6 +908,9 @@ impl WindowManager {
WindowsApi::invalidate_border_rect()?;
}
SocketMessage::AltFocusHack(enable) => {
ALT_FOCUS_HACK.store(enable, Ordering::SeqCst);
}
SocketMessage::NotificationSchema => {
let notification = schema_for!(Notification);
let schema = serde_json::to_string_pretty(&notification)?;

View File

@@ -97,7 +97,9 @@ impl WindowManager {
//
// 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.
if window.class()? != "OleMainThreadWndClass" {
if window.class()? != "OleMainThreadWndClass"
&& self.focused_monitor_idx() != monitor_idx
{
self.focus_monitor(monitor_idx)?;
}
}

View File

@@ -2,6 +2,7 @@ use std::convert::TryFrom;
use std::fmt::Display;
use std::fmt::Formatter;
use std::fmt::Write as _;
use std::sync::atomic::Ordering;
use color_eyre::eyre::anyhow;
use color_eyre::Result;
@@ -11,6 +12,9 @@ use serde::ser::SerializeStruct;
use serde::Serialize;
use serde::Serializer;
use windows::Win32::Foundation::HWND;
use winput::press;
use winput::release;
use winput::Vk;
use komorebi_core::ApplicationIdentifier;
use komorebi_core::HidingBehaviour;
@@ -20,6 +24,7 @@ use crate::styles::ExtendedWindowStyle;
use crate::styles::WindowStyle;
use crate::window_manager_event::WindowManagerEvent;
use crate::windows_api::WindowsApi;
use crate::ALT_FOCUS_HACK;
use crate::BORDER_OVERFLOW_IDENTIFIERS;
use crate::FLOAT_IDENTIFIERS;
use crate::HIDDEN_HWNDS;
@@ -268,7 +273,13 @@ impl Window {
let mut foregrounded = false;
let mut tried_resetting_foreground_access = false;
let mut max_attempts = 10;
let hotkey_uses_alt = WindowsApi::alt_is_pressed();
while !foregrounded && max_attempts > 0 {
if ALT_FOCUS_HACK.load(Ordering::SeqCst) {
press(Vk::Alt);
}
match WindowsApi::set_foreground_window(self.hwnd()) {
Ok(_) => {
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

View File

@@ -388,9 +388,11 @@ impl WindowManager {
}
// Gotta reset the focus or the movement will feel "off"
if before_count != after_count {
focused_ws.focus_container(focused_container_idx);
}
}
}
let invisible_borders = self.invisible_borders;
let offset = self.work_area_offset;

View File

@@ -52,6 +52,7 @@ use windows::Win32::System::Threading::PROCESS_NAME_WIN32;
use windows::Win32::System::Threading::PROCESS_QUERY_INFORMATION;
use windows::Win32::UI::HiDpi::SetProcessDpiAwarenessContext;
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::SetFocus;
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_LEFTUP;
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::GetScaleFactorForMonitor;
use windows::Win32::UI::WindowsAndMessaging::AllowSetForegroundWindow;
@@ -797,6 +799,13 @@ impl WindowsApi {
.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 {
let inputs = [
INPUT {

View File

@@ -288,6 +288,10 @@ CompleteConfiguration() {
RunWait, komorebic.exe complete-configuration, , Hide
}
AltFocusHack(boolean_state) {
RunWait, komorebic.exe alt-focus-hack %boolean_state%, , Hide
}
WindowHidingBehaviour(hiding_behaviour) {
RunWait, komorebic.exe window-hiding-behaviour %hiding_behaviour%, , Hide
}

View File

@@ -513,6 +513,12 @@ struct FormatAppSpecificConfiguration {
path: String,
}
#[derive(Parser, AhkFunction)]
struct AltFocusHack {
#[clap(value_enum)]
boolean_state: BooleanState,
}
#[derive(Parser)]
#[clap(author, about, version)]
struct Opts {
@@ -719,6 +725,9 @@ enum SubCommand {
WatchConfiguration(WatchConfiguration),
/// Signal that the final configuration option has been sent
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
#[clap(arg_required_else_help = true)]
WindowHidingBehaviour(WindowHidingBehaviour),
@@ -1319,6 +1328,9 @@ fn main() -> Result<()> {
SubCommand::CompleteConfiguration => {
send_message(&SocketMessage::CompleteConfiguration.as_bytes()?)?;
}
SubCommand::AltFocusHack(arg) => {
send_message(&SocketMessage::AltFocusHack(arg.boolean_state.into()).as_bytes()?)?;
}
SubCommand::IdentifyObjectNameChangeApplication(target) => {
send_message(
&SocketMessage::IdentifyObjectNameChangeApplication(target.identifier, target.id)