Skip to content

Commit

Permalink
fix: switch app does not list apps with borderless windows (#174)
Browse files Browse the repository at this point in the history
  • Loading branch information
sigoden authored Feb 17, 2025
1 parent cc305d7 commit 514fb7a
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 97 deletions.
56 changes: 22 additions & 34 deletions src/utils/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,40 +16,28 @@ use windows::Win32::{
PROCESS_VM_READ,
},
},
UI::{
Controls::STATE_SYSTEM_INVISIBLE,
WindowsAndMessaging::{
EnumWindows, GetCursorPos, GetForegroundWindow, GetTitleBarInfo, GetWindow,
GetWindowLongPtrW, GetWindowPlacement, GetWindowTextW, GetWindowThreadProcessId,
IsIconic, IsWindowVisible, SetForegroundWindow, SetWindowPos, ShowWindow, GWL_EXSTYLE,
GWL_USERDATA, GW_OWNER, SWP_NOZORDER, SW_RESTORE, TITLEBARINFO, WINDOWPLACEMENT,
WS_EX_TOPMOST,
},
UI::WindowsAndMessaging::{
EnumWindows, GetCursorPos, GetForegroundWindow, GetWindow, GetWindowLongPtrW,
GetWindowPlacement, GetWindowTextW, GetWindowThreadProcessId, IsIconic,
SetForegroundWindow, SetWindowPos, ShowWindow, GWL_EXSTYLE, GWL_STYLE, GWL_USERDATA,
GW_OWNER, SWP_NOZORDER, SW_RESTORE, WINDOWPLACEMENT, WS_EX_TOOLWINDOW, WS_ICONIC,
WS_VISIBLE,
},
};

pub fn is_iconic_window(hwnd: HWND) -> bool {
unsafe { IsIconic(hwnd) }.as_bool()
}

pub fn is_visible_window(hwnd: HWND) -> bool {
let ret = unsafe { IsWindowVisible(hwnd) };
if !ret.as_bool() {
return false;
}
pub fn get_window_state(hwnd: HWND) -> (bool, bool, bool) {
let style = unsafe { GetWindowLongPtrW(hwnd, GWL_STYLE) } as u32;
let exstyle = unsafe { GetWindowLongPtrW(hwnd, GWL_EXSTYLE) } as u32;

// Some "visible" windows are cloaked with `DWM_CLOAKED_SHELL` but always have
// this invisible flag set, so this filters out those unusual cases:
let mut title_info = TITLEBARINFO::default();
title_info.cbSize = std::mem::size_of_val(&title_info) as u32;
let _ = unsafe { GetTitleBarInfo(hwnd, &mut title_info) };
let is_visible = style & WS_VISIBLE.0 != 0;
let is_iconic = style & WS_ICONIC.0 != 0;
let is_tool = exstyle & WS_EX_TOOLWINDOW.0 != 0;

title_info.rgstate[0] & STATE_SYSTEM_INVISIBLE.0 == 0
(is_visible, is_iconic, is_tool)
}

pub fn is_topmost_window(hwnd: HWND) -> bool {
let ex_style = unsafe { GetWindowLongPtrW(hwnd, GWL_EXSTYLE) } as u32;
ex_style & WS_EX_TOPMOST.0 != 0
pub fn is_iconic_window(hwnd: HWND) -> bool {
unsafe { IsIconic(hwnd) }.as_bool()
}

pub fn get_window_cloak_type(hwnd: HWND) -> u32 {
Expand Down Expand Up @@ -200,6 +188,7 @@ pub fn get_owner_window(hwnd: HWND) -> HWND {
pub fn get_window_user_data(hwnd: HWND) -> i32 {
unsafe { windows::Win32::UI::WindowsAndMessaging::GetWindowLongW(hwnd, GWL_USERDATA) }
}

#[cfg(not(target_arch = "x86"))]
pub fn get_window_user_data(hwnd: HWND) -> isize {
unsafe { windows::Win32::UI::WindowsAndMessaging::GetWindowLongPtrW(hwnd, GWL_USERDATA) }
Expand Down Expand Up @@ -230,16 +219,15 @@ pub fn list_windows(
let mut valid_hwnds = vec![];
let mut owner_hwnds = vec![];
for hwnd in hwnds.iter().cloned() {
let mut valid = is_visible_window(hwnd)
let (is_visible, is_iconic, is_tool) = get_window_state(hwnd);
let ok = is_visible
&& (if ignore_minimal { !is_iconic } else { true })
&& !is_tool
&& !is_cloaked_window(hwnd, only_current_desktop)
&& !is_topmost_window(hwnd)
&& !is_small_window(hwnd);
if valid && ignore_minimal && is_iconic_window(hwnd) {
valid = false;
}
if valid {
if ok {
let title = get_window_title(hwnd);
if !title.is_empty() {
if !title.is_empty() && title != "Windows Input Experience" {
valid_hwnds.push((hwnd, title));
}
}
Expand Down
77 changes: 14 additions & 63 deletions tools/inspect-windows/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,83 +6,39 @@ use windows::Win32::Graphics::Dwm::{DWM_CLOAKED_APP, DWM_CLOAKED_INHERITED, DWM_
use windows::Win32::UI::WindowsAndMessaging::{EnumWindows, GetWindow, GW_OWNER};

fn main() -> Result<()> {
let output = collect_windows_info()?
.iter()
.map(|v| v.stringify())
.collect::<Vec<String>>()
.join("\n");
println!("{output}");
Ok(())
}

#[derive(Debug)]
struct WindowInfo {
hwnd: HWND,
title: String,
owner_hwnd: HWND,
owner_title: String,
size: (usize, usize),
is_visible: bool,
cloak_type: u32,
is_iconic: bool,
is_topmost: bool,
}

impl WindowInfo {
pub fn stringify(&self) -> String {
let size = format!("{}x{}", self.size.0, self.size.1);
format!(
"visible:{}cloak:{}iconic:{}topmost:{} {:>10} {:>10}:{} {}:{}",
pretty_bool(self.is_visible),
pretty_cloak(self.cloak_type),
pretty_bool(self.is_iconic),
pretty_bool(self.is_topmost),
size,
self.hwnd.0 as isize,
self.title,
self.owner_hwnd.0 as isize,
self.owner_title
)
}
}

fn collect_windows_info() -> anyhow::Result<Vec<WindowInfo>> {
let mut hwnds: Vec<HWND> = Default::default();
unsafe { EnumWindows(Some(enum_window), LPARAM(&mut hwnds as *mut _ as isize)) }
.with_context(|| "Fail to enum windows".to_string())?;
let mut output = vec![];
for hwnd in hwnds {
let title = get_window_title(hwnd);
let cloak_type = get_window_cloak_type(hwnd);
let is_iconic = is_iconic_window(hwnd);
let is_topmost = is_topmost_window(hwnd);
let is_visible = is_visible_window(hwnd);
let (is_visible, is_iconic, is_tool) = get_window_state(hwnd);
let (width, height) = get_window_size(hwnd);
let owner_hwnd: HWND = unsafe { GetWindow(hwnd, GW_OWNER) }.unwrap_or_default();
let owner_title = if !owner_hwnd.is_invalid() {
get_window_title(owner_hwnd)
} else {
"".into()
};
let window_info = WindowInfo {
hwnd,
println!(
"visible:{}iconic:{}tool:{}cloak:{} {:>10} {:>10}:{} {}:{}",
pretty_bool(is_visible),
pretty_bool(is_iconic),
pretty_bool(is_tool),
pretty_cloak(cloak_type),
format!("{}x{}", width, height),
hwnd.0 as isize,
title,
owner_hwnd,
owner_title,
size: (width as usize, height as usize),
is_visible,
cloak_type,
is_iconic,
is_topmost,
};
output.push(window_info);
owner_hwnd.0 as isize,
owner_title
);
}
Ok(output)
Ok(())
}

fn pretty_bool(value: bool) -> String {
if value {
"".into()
"*".into()
} else {
" ".into()
}
Expand All @@ -103,8 +59,3 @@ extern "system" fn enum_window(hwnd: HWND, lparam: LPARAM) -> BOOL {
windows.push(hwnd);
BOOL(1)
}

#[test]
fn test_collect_windows_info() {
collect_windows_info().unwrap();
}

0 comments on commit 514fb7a

Please sign in to comment.