Skip to content

Commit

Permalink
fix: duplicate windows shown after login (closes #292)
Browse files Browse the repository at this point in the history
  • Loading branch information
lwouis committed May 13, 2020
1 parent ab76320 commit 2e63644
Show file tree
Hide file tree
Showing 3 changed files with 7 additions and 5 deletions.
3 changes: 2 additions & 1 deletion src/api-wrappers/AXUIElement.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,14 @@ extension AXUIElement {
}

func isActualWindow(_ bundleIdentifier: String?) -> Bool {
// Some non-windows have cgWindowId == 0 (e.g. windows of apps starting at login with the checkbox "Hidden" checked)
// Some non-windows have title: nil (e.g. some OS elements)
// Some non-windows have subrole: nil (e.g. some OS elements), "AXUnknown" (e.g. Bartender), "AXSystemDialog" (e.g. Intellij tooltips)
// Minimized windows or windows of a hidden app have subrole "AXDialog"
// Activity Monitor main window subrole is "AXDialog" for a brief moment at launch; it then becomes "AXStandardWindow"
// CGWindowLevel == .normalWindow helps filter out iStats Pro and other top-level pop-overs
let subrole_ = subrole()
return subrole_ != nil &&
return cgWindowId() != 0 && subrole_ != nil &&
(["AXStandardWindow", "AXDialog"].contains(subrole_) ||
// All Steam windows have subrole = AXUnknown
// some dropdown menus are not desirable; they have title == "", or sometimes role == nil when switching between menus quickly
Expand Down
6 changes: 3 additions & 3 deletions src/api-wrappers/HelperExtensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ extension NSObject {

extension Array where Element == Window {
func firstIndexThatMatches(_ element: AXUIElement) -> Self.Index? {
// == is safer than comparing `CGWindowID` because it will succeed even if the window is deallocated
// by the OS, in which case the `CGWindowID` will be `-1`
return firstIndex(where: { $0.axUiElement == element })
// the window can be deallocated by the OS, in which case its `CGWindowID` will be `-1`
// we check for equality both on the AXUIElement, and the CGWindowID, in order to catch all scenarios
return firstIndex(where: { $0.axUiElement == element || ($0.cgWindowId != -1 && $0.cgWindowId == element.cgWindowId()) })
}

mutating func insertAndScaleRecycledPool(_ elements: [Element], at i: Int) {
Expand Down
3 changes: 2 additions & 1 deletion src/logic/Application.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ class Application: NSObject {
func observeNewWindows() {
if let windows = (axUiElement!.windows()?
.filter { $0.isActualWindow(runningApplication.bundleIdentifier) }) {
let actualWindows = windows.filter { Windows.list.firstIndexThatMatches($0) == nil }
// bug in macOS: sometimes the OS returns multiple duplicate windows (e.g. Mail.app starting at login)
let actualWindows = Array(Set(windows.filter { Windows.list.firstIndexThatMatches($0) == nil }))
if actualWindows.count > 0 {
addWindows(actualWindows)
}
Expand Down

0 comments on commit 2e63644

Please sign in to comment.