AdamWagner / stackline

Visualize yabai window stacks on macOS. Works with yabai & hammerspoon.
956 stars 47 forks source link

Multi-monitor support #22

Closed AdamWagner closed 4 years ago

AdamWagner commented 4 years ago

I haven't tested stackline on a multi-monitor setup, but @alin23 has reported some problems in the past.

Todo

AdamWagner commented 4 years ago

I'm going to work on this next since it's blocking @johnallen3d from using stackline (see https://github.com/AdamWagner/stackline/issues/27)

For example, here's what window frames look like when multiple monitors are in play.

canvas_frame = { -- hs.geometry.rect(1440.0,-247.0,2560.0,1440.0)
  _h = 1440.0,
  _w = 2560.0,
  _x = 1440.0,
  _y = -247.0  ←←←←←← this is off screen!
}
AdamWagner commented 4 years ago

This is going to be more difficult than expected. I initially thought I'd simply need to make sure window.frame coordinates were relative to the parent space.

Looks like that's not going to cut it, due to quirks in hs.window.filter that considers all visible spaces to be the active space if System Preferences>Mission Control>Displays have separate Spaces is set (it is set on my machine, which likely holds true for most yabai users).

If System Preferences>Mission Control>Displays have separate Spaces is on, the current Space is defined as the union of all the Spaces that are currently visible

So, looks like dedicated space tracking / modeling will be needed. :/

alin23 commented 4 years ago

@AdamWagner this did the trick for me when working with screen coordinates, maybe it will come handy if you didn't try it yet:

    screen = hs.screen.mainScreen()

    frame = screen:absoluteToLocal(hs.geometry(canvasFrame))
AdamWagner commented 4 years ago

@alin23 awesome! That's much simpler than the manual math I was doing. Thank you! That should solve the coordinate issues.

I think there's a second issue with how windows are queried & grouped into stacks though:

Here are the window filter settings:

wf.new():setOverrideFilter{
    visible = true,
    fullscreen = false,
    currentSpace = true,
    allowRoles = 'AXStandardWindow',
}

currentSpace = true is sufficient when using only 1 screen – all stackline needs to do is group windows with overlapping frames, and voila! – stacks.

But when multiple monitors are in use (and "Displays have own spaces" preference is set), that ↑ window filter returns windows from all visible spaces, which is enough to "confuse" Query:groupWindows() at query.lua:21.

I don't yet understand the nature of what's going wrong in Query:groupWindows(), so that's my next move.

My hunch is that we'll need to update the grouping function to group windows first by screen, then by space, and only then into stacks. We'll also probably need "spaces" and "screens" to perform actions, which means more modules (aka classes).

Alternatively, we might only need to group by screen, and continue to rely on currentSpace = true to exclude windows from non-visible spaces. In fact, that's probably preferable, in order to avoid the additional hs._asm.undocumented.spaces dependency, which can be seen in-use at hhtwm/init.lua

AdamWagner commented 4 years ago

A good example of how not grouping by screen > space causes problems is the workaround for https://github.com/Hammerspoon/hammerspoon/issues/2400.

The only reason stackline keeps track of otherAppWindows is so that we can manually unfocus same-app, same-stack windows: Whenever a window is focused, stackline checks window.otherAppWindows and updates each indicator to appear unfocused, since Hammerspoon doesn't fire unfocus events when switching between windows of the same app.

In a single-monitor scenario, window.otherAppWindows is really window.otherAppWindowsInSameStack. In a multi-monitor scenario, this no longer holds true, and window.otherAppWindows can contain windows from other screens/stacks.

Maybe I should open another issue to reconsider this workaround? It's pretty complicated already, and seems to only become more complicated with each new feature. I don't know if there's really a better alternative (other than fixing the Hammerspoon bug itself, which I'm not yet capable of doing).

AdamWagner commented 4 years ago

I found a shortcut for minimally-viable multi monitor support. It's live on https://github.com/AdamWagner/stackline/tree/feature/multi-monitor

I'm checking to see if the focused screen changed on every windowFocused event, and if it did, refreshing all indicators.

Shortcomings:

I still think the "proper" solution will require updating the data model to track screens (at least) and potentially spaces. This will enable stackline to render on all screens. If spaces are modeled, it will additionally speed up the rendering of stack indicators when switching between spaces with stacks.

AdamWagner commented 4 years ago

Annnd, just kidding.

Found a way to get stackline to render on all monitors without modeling screens / spaces directly. I'm surprised that this worked!

Opened a PR here: https://github.com/AdamWagner/stackline/pull/30