Stacking Overlapping Windows: Can The Back Window Be Placed Without Flashing

The below macro arranges three scanner windows into a deliberately overlapping stack, offset downward so a slice of each stays visible:

EPSON ET-5850          (Print Center)   back
Image Capture          (Image Capture)  middle
Scan Results           (Image Capture)  front

It works. Everything below is about one cosmetic artefact, and I have done the reading before posting.

A copy of the well document but somewhat long macro:

Macro Image

Position Scan Windows.kmmacros (95.5 KB)

THE ARTEFACT

Because the only tool available is "bring a window to the front", building the stack means lifting EPSON first, which necessarily puts it on top for about a tenth of a second until Image Capture is lifted over it. What makes it catch the eye is not the depth change but the traffic lights: lifting EPSON makes it the active window, so its buttons colour and Image Capture's grey out, then they swap straight back. On, off, on.

WHAT I HAVE ALREADY RULED OUT

  • Peter's answer in "Send the front most window to back? bring the back to front?": there is no send-to-back API, and re-stacking by lifting each in turn is the only method. That is exactly what I do.

  • Keyboard Maestro's Manipulate a Window action has no send-to-back, and Bring a Window to the Front has no option to raise without activating.

  • Moom is installed here and its AppleScript dictionary exposes apply layout and arrange windows according to snapshot. The challenge is that I would need multiple layouts as I use a multiple monitors and therefore need different layouts depending on which monitor the stack was on which could / would become a bit of a challenge to maintain. That said, @griffman. should you know an easy way please share.

  • yabai solves it via a scripting addition injected into Dock.app, which needs SIP partially disabled. No thank you!

WHAT I HAVE ALREADY DONE

  • Separating the moving from the lifting, so all three windows are already in position before anything is lifted, which makes the flash happen against a settled layout rather than while a window is travelling.

  • A guard that counts how many of the three windows exist when lifting begins and compares it against how many this run actually placed, refusing to lift at all if they differ. Screen captures at 15 fps put this at roughly a fourfold reduction, from a flash on essentially every open to about one in three.

THE QUESTIONS

Is there anything else, within the public APIs and without touching SIP, that either:

  1. Places a window behind another without lifting it, or

  2. Lifts a window without making it the active window, so the traffic lights do not flick, or

  3. Improves (i.e., limits) the flashing further than I have currently limited it?

I am not looking for a workaround for the positioning, which is solved. Only the momentary z-order inversion and the activation that comes with it.

Thank in advance!

I believe the process WindowServer is the macOS process that handles the order of windows. It does not have a public API to allow for the changing of windows “without lifting it.” (Lifting it to the front is the ONLY window-order change allowed. I think.)

You can probably write Swift code that runs in KM’s Swift action that fetches certain window details in front to back order. But I’m not sure how that would help you since you CANNOT change the z-order of windows. Act least not through Apple’s public APIs. Maybe you can find software on the Internet that has found private APIs and uses that to achieve your goal. But then this sort of thing is likely to break whenever Apple releases a new OS. KM’s philosophy is to never use private APIs. However nothing stops you from invoking a utility in a KM macro that accesses private APIs. So if you can find one, go for it. (I googled it, and found two utilities that might help you: (1) yabai; (2) Rift. Go try them out.)

There are people on this site who may be able to get you a more authoritative or correct answer than I can give you. My answers on advanced topics can sometimes be wrong.

Appreciated.

I am aware of yabai and others as I too researched them.

I am looking for solutions that i) use public APIs and ii) do not touch SIP.

I should add that there are two cosmetic problems that I would like to tackle, both related to the flashing:

  1. The ordering of the windows without lifting as detailed above.

  2. The deferral / control of the timing that the windows are ordered / stacked such that it occurs after all windows are positioned. This is the purpose of the guard IF Local_MoveCount = Local_LifeCount in the below action in Step 8b of the macro.

The point or purpose of the guard is to avoid ordering the windows until the on-screen windows are in their final position to avoid multiple flashes / orderings (i.e., the final flash / ordering cannot be avoided subject solving 1 above). The guard, in testing, has proven to greatly reduce but not eliminate multiple flashes / orderings.

This can also be asked as, is there any way in Keyboard Maestro to test the stacking order of two named windows, that is, whether window A is currently in front of window B?

If that were testable, the lifting could be skipped entirely whenever the order is already correct, which would remove the flash from every run that does not actually need reordering. I can test positions and sizes easily enough, but position tells you nothing about depth, and depth is what the lifting exists to fix.

Much thanks

Partial answer to my own question, in case it saves anyone the trouble.

%WindowName%All% does return stacking order, "all of the windows, one per line, from front to back". But it appears to be scoped to the frontmost application only: with the EPSON window open in Print Center and Image Capture frontmost, it returned just Image Capture's two windows and omitted EPSON entirely.

That makes it the wrong half for this problem. The two windows I can compare belong to the same application, and lifting between them is a Select that causes no flash. The comparison I actually need is across two applications, EPSON in Print Center against the windows in Image Capture, because that one needs an Activate and the Activate is what flicks the traffic lights.

So the question stands, but more precisely: is there any way to compare the stacking order of two windows belonging to DIFFERENT applications?

What you want is the "UI stack order" -- what you see on the screen. A quick web search for

macos get ui stack order of windows

...should get you started. Whether you can then do anything to change the order is another matter...

The only way I know to manage stacking order is to front each window in turn, in reverse order. But an alternative in this case would be to front Image Capture and then hide everything but Image Capture and Print Center.

You'll then tell us you want to watch YouTube while scanning :wink:

You could always go old-school and put a bit of masking tape on your monitor, just where the Epson window's traffic lights are. Problem solved!

As far as I know, unless your name is Apple and you have macOS source code :), the answer to the question is "no." Unlike Windows, which apparently has a very nice window stack that you can work with, from a developer/user perspective, there's no accessible stack. The only way to reorder windows is cycle them to the foreground in the reverse order in which you want them to wind up.

About the only thing I can think of trying is to first move the windows to the very edges of the screen while they're still in the background. macOS will let you position and size a window such that almost all its chrome, including the traffic light window buttons, are off the edge of the screen.

tell application "System Events"
	tell process "Safari"
        -- set size of window 1 to {100,100}
		set position of window 1 to {-1447,39}
	end tell
end tell

That puts my Safari window almost completely off the left edge of my display (and makes it tiny, if you uncomment the size line).

So you could first move all three windows nearly offscreen, then activate them in the right order, then move them onscreen.

I have no idea, though, if this would be better or worse than your current solution. It pretty much gets rid of the flashing, but moves windows more often.

-rob.

As I said earlier, it is possible to write Swift code that fetches window details from front to back order. But the WindowServer application doesn't define "windows" the same way that you do. So you will see a lot more items listed if you run this code, and you will have to interpret the data. For starters, I think you are only interested in objects with "Layer: 25" so I had the code generate a list of objects with that layer at the end of this script. So give it a test and see how close it gets for your situation. Place the following text into a KM Execute Swift Script action and check the results.

import Foundation
import CoreGraphics

// ------------------------------------------------------------
// Get the WindowServer window list.
// Apple documents this list as being ordered front-to-back.
// ------------------------------------------------------------

let options: CGWindowListOption = [
.optionOnScreenOnly,
.excludeDesktopElements
]

guard let windowList =
CGWindowListCopyWindowInfo(options, kCGNullWindowID)
as? [[String: Any]]
else {
print("Unable to obtain the window list.")
exit(1)
}

// ------------------------------------------------------------
// Helper functions
// ------------------------------------------------------------

func stringValue(_ value: Any?) -> String {
if let value = value as? String {
return value
}
return ""
}

func intValue(_ value: Any?) -> Int {
if let value = value as? Int {
return value
}
if let value = value as? NSNumber {
return value.intValue
}
return 0
}

func rectValue(_ value: Any?) -> CGRect? {
guard let dictionary = value as? NSDictionary else {
return nil
}

var rect = CGRect.zero

if CGRectMakeWithDictionaryRepresentation(dictionary, &rect) {
    return rect
}

return nil

}

// ------------------------------------------------------------
// Format one window
// ------------------------------------------------------------

func describeWindow(
_ window: [String: Any],
number: Int
) -> String {

let windowID =
    intValue(window[kCGWindowNumber as String])

let owner =
    stringValue(window[kCGWindowOwnerName as String])

let title =
    stringValue(window[kCGWindowName as String])

let layer =
    intValue(window[kCGWindowLayer as String])

let bounds =
    rectValue(window[kCGWindowBounds as String])

var result = ""

result += "\(number). \(owner)"

if !title.isEmpty {
    result += " — \(title)"
}

result += "\n"

result += "   Window ID: \(windowID)\n"
result += "   Layer: \(layer)\n"

if let bounds = bounds {

    result += String(
        format: "   Position: %.0f, %.0f\n",
        bounds.origin.x,
        bounds.origin.y
    )

    result += String(
        format: "   Size: %.0f × %.0f\n",
        bounds.size.width,
        bounds.size.height
    )
}

return result

}

// ============================================================
// LIST #1 — ALL WINDOWS
// ============================================================

var output = ""

output += "============================================================\n"
output += "ALL ON-SCREEN WINDOWS — FRONT TO BACK\n"
output += "============================================================\n\n"

for (index, window) in windowList.enumerated() {

output += describeWindow(
    window,
    number: index + 1
)

output += "\n"

}

// ============================================================
// LIST #2 — ONLY LAYER 0
// ============================================================

let layerZeroWindows = windowList.filter {

intValue($0[kCGWindowLayer as String]) == 0

}

output += "\n"
output += "============================================================\n"
output += "LAYER 0 WINDOWS — FRONT TO BACK\n"
output += "============================================================\n\n"

for (index, window) in layerZeroWindows.enumerated() {

output += describeWindow(
    window,
    number: index + 1
)

output += "\n"

}

// ============================================================
// SUMMARY
// ============================================================

output += "============================================================\n"
output += "SUMMARY\n"
output += "============================================================\n"

output += "Total WindowServer windows: (windowList.count)\n"
output += "Layer 0 windows: (layerZeroWindows.count)\n"

print(output)

Execute a Swift Script Action (v11.1.1)

Execute a Swift Script.kmactions (4.7 KB)

Layer 25 is menu bar items, I think. Layer 0 will be the one we want, e.g.

Layer: 0, ID: 9460, App: Safari, Title: Stacking Overlapping Windows: Can The Back Window Be Placed Without Flashing - Questions & Suggestions - Keyboard Maestro Discourse

...for this window, for me, right now.

List Windows in UI Stack Order.kmactions (1.4 KB)

That's exactly what I meant to say. I accidentally said the opposite of what I meant to say.

@DocOck and @Nige_S appreciate both of your responses, very helpful. I have a heavy workload today so will look at this later tonight / tomorrow as soon as time permits. I said hopeful that this will reduce the flashes to one; that which is needed to re-order the windows. Stay tuned!

@Nige_S

You'll then tell us you want to watch YouTube while scanning :wink:

And how did you know, that is coming next!

You could always go old-school and put a bit of masking tape on your monitor, just where the Epson window's traffic lights are. Problem solved!

I tried and rejected that, I kept running out of tape! :winking_face_with_tongue: :face_with_peeking_eye:

@griffman, much thanks for the suggestion. I agree that tis will work but I am trying to minimize teh amount of window movement so that rules it out!

Apologies for the delay, the day job got it the way over the last few days.

As a start, the macro is as follows:

Macro Image

Position Scan Windows.kmmacros (131.4 KB)

The swift code used in the macro is as follows:

Swift Code
import Foundation
import CoreGraphics

/// Print the answer and a diagnostic, then stop.
func emit(_ startAt: Int, _ why: String) -> Never {
    print(startAt)
    FileHandle.standardError.write("\(startAt): \(why)\n".data(using: .utf8)!)
    exit(0)
}

/// The three windows, in intended order, top first. Index + 1 is the position
/// number this program prints.
enum Scanner: Int, CaseIterable {
    case scanResults = 0, imageCapture = 1, epson = 2
    var position: Int { rawValue + 1 }
    var label: String {
        switch self {
        case .scanResults:  return "SCANRESULTS"
        case .imageCapture: return "IMAGECAPTURE"
        case .epson:        return "EPSON"
        }
    }
}

let existStart = Int(ProcessInfo.processInfo.environment["KMVAR_Local_ExistStart"] ?? "") ?? -1

let options = CGWindowListOption([.optionOnScreenOnly, .excludeDesktopElements])
guard let list = CGWindowListCopyWindowInfo(options, kCGNullWindowID) as? [[String: Any]] else {
    emit(3, "window list unavailable, falling back to raising everything")
}

// Ordinary application windows only, front to back. Layer 25 is menu bar items,
// layer 20 the Dock, and so on; only layer 0 is a real application window.
// Each entry is the window's own id, plus which scanner window it is, if any.
var stack: [(id: Int, which: Scanner?, frame: CGRect)] = []
var titlesSeen = false

for w in list {
    guard (w[kCGWindowLayer as String] as? Int) == 0 else { continue }
    let id    = w[kCGWindowNumber as String] as? Int ?? -1
    let owner = w[kCGWindowOwnerName as String] as? String ?? ""
    let title = w[kCGWindowName as String] as? String ?? ""
    if !title.isEmpty { titlesSeen = true }

    var frame = CGRect.zero
    if let b = w[kCGWindowBounds as String] as? NSDictionary {
        CGRectMakeWithDictionaryRepresentation(b, &frame)
    }

    var which: Scanner? = nil
    if owner == "Print Center",  title.hasPrefix("EPSON ET-5850") { which = .epson }
    if owner == "Image Capture", title == "Image Capture"         { which = .imageCapture }
    if owner == "Image Capture", title == "Scan Results"          { which = .scanResults }
    stack.append((id: id, which: which, frame: frame))
}

if !titlesSeen {
    emit(3, "no window titles returned, Screen Recording permission missing, falling back to raising everything")
}

let present = Scanner.allCases.filter { s in stack.contains { $0.which == s } }
let count = present.count

let seen = present.map { s -> String in
    let at = stack.firstIndex { $0.which == s } ?? -1
    return "\(s.label)@\(at)"
}.joined(separator: " ")
let detail = "front-to-back \(seen) count=\(count) existStart=\(existStart)"

if count == 0 { emit(0, "no scanner windows on screen. " + detail) }

// SAFETY CHECK. A window arrived after the move phase, so one of these has
// never been positioned. Raise nothing and let the next run handle it.
if existStart >= 0 && count != existStart {
    emit(0, "a window arrived after the move phase, raising would shuffle an unpositioned window. " + detail)
}

/// True when the scanner windows are in the intended order Scan Results, Image
/// Capture, EPSON, and nothing that overlaps them sits in front of them.
///
/// Overlap, not list position. The window order is one global list spanning
/// every display, so windows on the other screen appear ahead of the stack
/// without being able to obscure any of it. Only an intersecting rectangle
/// counts as an obstruction.
func isCorrect(_ order: [(id: Int, which: Scanner?, frame: CGRect)]) -> Bool {
    let scannerOrder = order.compactMap { $0.which }
    if scannerOrder != present { return false }

    for (i, entry) in order.enumerated() where entry.which == nil {
        for behind in order[(i + 1)...] where behind.which != nil {
            if entry.frame.intersects(behind.frame) { return false }
        }
    }
    return true
}

/// Raising a window removes it from wherever it is and puts it at the front.
func raised(_ order: [(id: Int, which: Scanner?, frame: CGRect)], _ s: Scanner) -> [(id: Int, which: Scanner?, frame: CGRect)] {
    guard let i = order.firstIndex(where: { $0.which == s }) else { return order }
    var copy = order
    let w = copy.remove(at: i)
    copy.insert(w, at: 0)
    return copy
}

// Try the cheapest answer first. Raise everything from `startAt` upwards,
// bottom-first, so the topmost is raised last and finishes on top.
for startAt in 0...3 {
    var simulated = stack
    for s in Scanner.allCases.reversed() where s.position <= startAt {
        simulated = raised(simulated, s)
    }
    if isCorrect(simulated) {
        let what = startAt == 0 ? "nothing needs raising"
                                : "raise from position \(startAt) upwards"
        emit(startAt, what + ". " + detail)
    }
}

emit(3, "simulation found no working answer, which should be impossible. " + detail)

Thanks to all three of you. Reporting back, because the answer turned out to be more interesting than I expected, and because the thread deserves a conclusion.

SHORT VERSION

The flash cannot be removed, exactly as Peter has said elsewhere: there is no send to back, so a pile can only be built by lifting from the bottom up, and lifting makes a window active, which is what flicks the traffic lights. What can be removed is every lift that was not necessary. My macro now produces no flicker at all when the stack is already correct, one flicker when one window has genuinely been dragged out of place, and three only when something like a browser has been dropped across the whole stack. One flicker per window that actually had to move, which I think is the floor on this operating system.

THE THING THAT MADE IT POSSIBLE

CGWindowListCopyWindowInfo is a public Quartz call that returns every on screen window in true front to back order, across all applications, with owner, title, layer and bounds. Keyboard Maestro cannot do this natively, since %WindowName%All% only sees the frontmost application, and my three windows belong to two applications. Reading that list is what lets a macro ask "is this stack actually wrong?" before doing anything about it.

@Nige_S , your correction about window layers turned out to be load bearing. Filtering to layer 0 is what keeps menu bar items, the Dock and various overlays out of the answer. Without it the readings were nonsense.

FOUR THINGS I LEARNED THAT MIGHT SAVE SOMEONE ELSE THE TIME

Moving a window does not raise it. I could not find this documented anywhere and had to test it: a window moved into the pile slid underneath rather than over. That single fact is what lets you split the work into a move phase, where everything is put in its final place and size, and a lift phase afterwards. The flash then happens against a settled layout instead of while a window is still travelling, which makes it far less noticeable.

Compile the Swift, do not use Execute a Swift Script. The action recompiles on every run and cost me about 0.9 seconds. The same code compiled once with swiftc -O and called from Execute a Shell Script runs in about 0.03 seconds.

Window titles need Screen Recording permission for the calling process. Owner names do not. Keyboard Maestro has it, so this works fine from a shell script action, but it is worth knowing that the program will see the windows and not their names if that permission is ever missing.

Judge obstruction by overlapping rectangles, not by position in the list. The window order is one global list spanning every display, so windows on your second screen sit ahead of the stack in that list while being completely unable to obscure it. My first version required the stack to be the frontmost entries outright, and the result was that every click on the stack after using the other screen re-raised all three windows for nothing.

ONE KEYBOARD MAESTRO SPECIFIC TRAP, UNRELATED TO STACKING, BUT IT COST ME MOST OF A DAY

The macro uses a Semaphore Lock so it cannot trigger itself, which means any window that opens while a run is in progress has its own trigger event turned away and thrown away. It never comes back, and that window is simply never positioned. I had patched this with a special case at the end that looked for one window by name, which rescued that one window and no other.

The general fix is to put the working part of the macro inside a Repeat, count the relevant windows at the start of each pass and again at the end, break out when the two counts agree, and go round again when they do not. It mentions no window names in its logic, so it cannot be right for one window and wrong for another, and it costs nothing on a normal run because it breaks on the first pass.

THE MACRO

Attached above. It is heavily commented, more or less an essay per action, including everything that was tried and rejected, so it should be readable even though it is specific to an Epson multi-function printer that is being used as scanner. It calls a small compiled Swift helper, which appears in the macro as

"$KMVAR_Global_Code/Scan/bin/get_window_order"

That is the CGWindowListCopyWindowInfo program described above. It is a separate file and is also attached.

Thanks again. I would not have got to the Swift route without the nudge.