Skip to content

Commit

Permalink
feat(wm): add optional alt focus hack
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
LGUG2Z committed Jan 21, 2023
1 parent f8120f6 commit 925f3bd
Show file tree
Hide file tree
Showing 9 changed files with 52 additions and 2 deletions.
1 change: 1 addition & 0 deletions komorebi-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ pub enum SocketMessage {
ReloadConfiguration,
WatchConfiguration(bool),
CompleteConfiguration,
AltFocusHack(bool),
ActiveWindowBorder(bool),
ActiveWindowBorderColour(WindowKind, u32, u32, u32),
ActiveWindowBorderWidth(i32),
Expand Down
1 change: 1 addition & 0 deletions komorebi/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
4 changes: 4 additions & 0 deletions komorebi/src/process_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)?;
Expand Down
4 changes: 3 additions & 1 deletion komorebi/src/process_event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)?;
}
}
Expand Down
15 changes: 15 additions & 0 deletions komorebi/src/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -289,6 +300,10 @@ impl Window {
}
}
};

if ALT_FOCUS_HACK.load(Ordering::SeqCst) && !hotkey_uses_alt {
release(Vk::Alt);
}
}

// Center cursor in Window
Expand Down
4 changes: 3 additions & 1 deletion komorebi/src/window_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,9 @@ impl WindowManager {
}

// 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);
}
}
}

Expand Down
9 changes: 9 additions & 0 deletions komorebi/src/windows_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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 {
Expand Down
4 changes: 4 additions & 0 deletions komorebic.lib.ahk
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
12 changes: 12 additions & 0 deletions komorebic/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit 925f3bd

Please sign in to comment.