koekeishiya / yabai

A tiling window manager for macOS based on binary space partitioning
MIT License
23.97k stars 653 forks source link

Preserve layout after coming back from sleep #259

Open dmitrym0 opened 5 years ago

dmitrym0 commented 5 years ago

I'm using a macbook pro with an external screen. Typically when it comes back from sleep, the macbook wakes up first, with the external screen coming in 10-20 seconds later.

Before the laptop goes to sleep, I have a nice layout with windows uniformly distributed between two monitors. Upon wakeup, because the second monitor takes its time, Yabai lays all of the windows on the laptop screen, and the external monitor is empty. I then manually reshuffle windows.

I dont mind it too much, but was hoping that there's maybe a way to avoid this that I'm missing.

My complements for a wonderful piece of software. I really missed something like this since I switched from Linux and ion3 a number of years ago.

dominiklohmann commented 5 years ago

It's weird that it takes your monitor so long to wake up. On my setup the display is available instantly if I didn't disconnect it while the device was sleeping.

If the display disconnects and reconnects there isn't much we can do I think.

Best you can do is subscribe to the display_added signal to move any visible window that satisfies a set of criteria to the newly added display.

knoguchi commented 5 years ago

I noticed that too. I just locked my screen for bathroom break, and logon again. When I have two external monitors it takes much longer to come back. This is especially noticeable with 5K external monitors. ..and when it comes back the layout is all lost.

I think Mac OS keeps multiple profiles of the arrangement of the monitors. It would be nice if the layout would be saved per profile. For example monitors at my home and the monitors at office are different, but Mac remembers the arrangement for each.

koekeishiya commented 5 years ago

See https://github.com/koekeishiya/chunkwm/issues/355 and https://github.com/koekeishiya/kwm/issues/108 and related issues for some form of history. I don't remember much, if anything, about this issue, other than macOS being absolute crazytown in terms of how it deals with 4k (and 5k?) displays. I don't own a 4k or 5k display and I don't plan on getting one any time soon, so this is not something I will bother spending time on.

dominiklohmann commented 5 years ago

I wonder if it'd be a good solution to let the user set a timeout that ticks down when a display gets disconnected, causing yabai to freeze all internal state and queue up events until either a display is connected or the timeout runs down.

dmitrym0 commented 4 years ago

As a stop gap, I increased sleep threshhold for my system. Downside: not as energy efficient. Upside: re-layout only happens once a day usually (in the morning).

koekeishiya commented 4 years ago

So this issue can be resolved by using the space uuid as the way to identify spaces, rather than the cgsspaceid. I'll have to see if this is a reasonable change to make with the way yabai is designed.. hopefully it will be fine to make that switch.

koekeishiya commented 4 years ago

I believe this issue is fixed on the latest master, but as mentioned previously I have no way of actually testing this.

dmitrym0 commented 4 years ago

Can confirm that it's fixed on my MacOS 10.14. Thank you!

dmitrym0 commented 4 years ago

I take it back. Woke my mac from overnight sleep and the windows are all jumbled up on the primary monitor.

koekeishiya commented 4 years ago

Can someone confirm that the layout now persist when a GPU switch occur?

I believe the wake from sleep issue is a combination of another step that needs to be handled. The fix I did above should remap our internal spaces structure when a new display is added (which happens on a GPU switch, and also when a 4k/5k display wake from sleep). However, I believe that in some cases it will also report a display removed event before the added event. This causes yabai to untile windows in the remove event, and then it has nothing to map back to when we process the add event afterwards.

Can someone run with debug_output on and paste a dump of the event log of what happens in this scenario?

zach-source commented 3 years ago

I've had some weird issues with windows so I made a quick script for anyone who is interested:

filename=${1:-desktop}
directory=$HOME/.config/yabai/layouts/$(hostname)

mkdir -p $directory

yabai -m query --windows | jq -re '.[] | select(.minimized != 1) | "yabai -m window \(.id) --display \(.display) --space \(.space) --move abs:\(.frame.x):\(.frame.y) --resize abs:\(.frame.w):\(.frame.h)"' > $directory/$filename.sh

if [[ $? != 0 ]] ; then
  echo -n "Yabai: failed to save"
  exit 1
fi

chmod +x $directory/$filename.sh

You can restore a layout just by executing the file it creates. I do this to help between laptop and desktop switches. You could probably tie it into events with yabai but I haven't taken it to that degree.

dimitarvdimitrov commented 1 year ago

I managed to get debug output after waking up from sleep. Without timestamps it's difficult to tell when is the last log line from before the sleep, but I think I have it. I suppose EVENT_HANDLER_SYSTEM_WOKE indicates when I woke my macbook from sleep.

Details ``` EVENT_HANDLER_APPLICATION_ACTIVATED: Slack EVENT_HANDLER_DAEMON_MESSAGE: query --windows --space EVENT_HANDLER_DAEMON_MESSAGE: query --windows --space EVENT_HANDLER_DAEMON_MESSAGE: query --windows --window EVENT_HANDLER_DAEMON_MESSAGE: query --windows --space EVENT_HANDLER_MOUSE_DOWN: 116517 1446.42, 2864.19 EVENT_HANDLER_MOUSE_UP: 1446.42, 2864.19 EVENT_HANDLER_MOUSE_DOWN: 116517 1446.42, 2864.19 EVENT_HANDLER_MOUSE_UP: 1446.42, 2864.19 EVENT_HANDLER_MOUSE_DOWN: 116517 1474.43, 2756.97 EVENT_HANDLER_MOUSE_UP: 1474.43, 2756.97 EVENT_HANDLER_MOUSE_DOWN: 116517 1394.89, 2944.96 EVENT_HANDLER_MOUSE_UP: 1394.89, 2944.96 EVENT_HANDLER_MOUSE_DOWN: 116517 895.93, 2563.24 EVENT_HANDLER_MOUSE_UP: 895.93, 2563.24 EVENT_HANDLER_MOUSE_DOWN: 116517 870.54, 2556.38 EVENT_HANDLER_MOUSE_UP: 870.54, 2556.38 EVENT_HANDLER_MOUSE_DOWN: 116517 624.71, 2779.22 EVENT_HANDLER_MOUSE_UP: 624.71, 2779.22 EVENT_HANDLER_MOUSE_DOWN: 116517 753.15, 2701.45 EVENT_HANDLER_MOUSE_UP: 753.15, 2701.45 EVENT_HANDLER_MOUSE_DOWN: 116517 1492.66, 2952.71 EVENT_HANDLER_MOUSE_UP: 1492.66, 2952.71 EVENT_HANDLER_MOUSE_DOWN: 116517 1472.55, 2959.61 EVENT_HANDLER_MOUSE_DRAGGED: 1472.55, 2959.72 EVENT_HANDLER_MOUSE_UP: 1472.55, 2959.72 EVENT_HANDLER_MOUSE_DOWN: 116517 632.64, 2798.76 EVENT_HANDLER_MOUSE_UP: 632.64, 2798.76 EVENT_HANDLER_MOUSE_DOWN: 116517 1481.50, 2951.96 EVENT_HANDLER_MOUSE_UP: 1481.50, 2951.96 EVENT_HANDLER_MOUSE_DOWN: 116517 1331.20, 3021.20 EVENT_HANDLER_MOUSE_UP: 1331.20, 3021.20 EVENT_HANDLER_MOUSE_DOWN: 116517 1744.50, 2716.18 EVENT_HANDLER_MOUSE_UP: 1744.50, 2716.18 EVENT_HANDLER_MOUSE_DOWN: 116517 1282.78, 2947.10 EVENT_HANDLER_MOUSE_UP: 1282.78, 2947.10 EVENT_HANDLER_MOUSE_DOWN: 116517 1698.50, 2810.02 EVENT_HANDLER_MOUSE_UP: 1698.50, 2810.02 EVENT_HANDLER_MOUSE_DOWN: 116517 1505.23, 2863.45 EVENT_HANDLER_MOUSE_DRAGGED: 1505.23, 2863.32 EVENT_HANDLER_MOUSE_UP: 1505.23, 2863.32 EVENT_HANDLER_MOUSE_DOWN: 116517 1299.90, 2950.55 EVENT_HANDLER_MOUSE_UP: 1299.90, 2950.55 EVENT_HANDLER_MOUSE_DOWN: 116517 1500.53, 2949.73 EVENT_HANDLER_MOUSE_UP: 1500.53, 2949.73 EVENT_HANDLER_MOUSE_DOWN: 116517 1501.04, 2802.16 EVENT_HANDLER_MOUSE_UP: 1501.04, 2802.16 EVENT_HANDLER_MOUSE_DOWN: 116517 735.36, 2831.68 EVENT_HANDLER_MOUSE_UP: 735.36, 2831.68 EVENT_HANDLER_MOUSE_DOWN: 116517 819.68, 2752.35 EVENT_HANDLER_MOUSE_UP: 819.68, 2752.35 EVENT_HANDLER_APPLICATION_TERMINATED: Bitwarden (90179) EVENT_HANDLER_APPLICATION_LAUNCHED: Google Chrome Helper (Plugin) (93470) is not finished launching, subscribing to finishedLaunching changes -[workspace_context observeValueForKeyPath:ofObject:change:context:]: Google Chrome Helper (Plugin) (93470) finished launching EVENT_HANDLER_APPLICATION_LAUNCHED: Google Chrome Helper (Plugin) (93470) EVENT_HANDLER_APPLICATION_TERMINATED: Google Chrome Helper (Plugin) (93470) EVENT_HANDLER_APPLICATION_LAUNCHED: Google Chrome Helper (Plugin) (93475) is not finished launching, subscribing to finishedLaunching changes -[workspace_context observeValueForKeyPath:ofObject:change:context:]: Google Chrome Helper (Plugin) (93475) finished launching EVENT_HANDLER_APPLICATION_LAUNCHED: Google Chrome Helper (Plugin) (93475) EVENT_HANDLER_APPLICATION_TERMINATED: Google Chrome Helper (Plugin) (93475) EVENT_HANDLER_DISPLAY_CHANGED: newly activated display 1 was already active (1)! ignoring event.. EVENT_HANDLER_DISPLAY_REMOVED: 2 EVENT_HANDLER_DISPLAY_REMOVED: 2 EVENT_HANDLER_DISPLAY_MOVED: 1 EVENT_HANDLER_SPACE_CHANGED: 6 space_manager_refresh_application_windows: Alacritty has windows that are not yet resolved EVENT_HANDLER_DISPLAY_CHANGED: 2 6 space_manager_refresh_application_windows: Alacritty has windows that are not yet resolved EVENT_HANDLER_DISPLAY_ADDED: 2 EVENT_HANDLER_DISPLAY_MOVED: 1 EVENT_HANDLER_SPACE_CHANGED: 6 space_manager_refresh_application_windows: Alacritty has windows that are not yet resolved EVENT_HANDLER_DISPLAY_CHANGED: newly activated display 2 was already active (2)! ignoring event.. EVENT_HANDLER_DISPLAY_ADDED: 3 EVENT_HANDLER_DISPLAY_MOVED: 1 EVENT_HANDLER_DISPLAY_RESIZED: 2 EVENT_HANDLER_SYSTEM_WOKE: process_create: could not retrieve process name! ignoring.. EVENT_HANDLER_APPLICATION_LAUNCHED: Google Chrome Helper (Plugin) (93519) EVENT_HANDLER_APPLICATION_FRONT_SWITCHED: Slack (72721) EVENT_HANDLER_APPLICATION_DEACTIVATED: Slack EVENT_HANDLER_APPLICATION_ACTIVATED: Slack event_signal_flush: transmitting window_focused to 1 subscriber(s) ck EVENT_HANDLER_DISPLAY_CHANGED: 3 865 space_manager_refresh_application_windows: Alacritty has windows that are not yet resolved EVENT_HANDLER_DAEMON_MESSAGE: query --windows --window EVENT_HANDLER_DAEMON_MESSAGE: query --windows --space EVENT_HANDLER_SPACE_CHANGED: 698 space_manager_refresh_application_windows: Alacritty has windows that are not yet resolved EVENT_HANDLER_WINDOW_RESIZED: Slack 116517 EVENT_HANDLER_WINDOW_MOVED:DEBOUNCED Slack 116517 EVENT_HANDLER_WINDOW_RESIZED:DEBOUNCED Slack 116517 EVENT_HANDLER_WINDOW_TITLE_CHANGED: Slack 116517 window_manager_create_and_add_window: ignoring AXUnknown window Bartender 4 120615 window_manager_create_and_add_window: ignoring AXUnknown window Bartender 4 120616 window_manager_create_and_add_window: ignoring AXUnknown window Bartender 4 120617 EVENT_HANDLER_APPLICATION_TERMINATED: Google Chrome Helper (Plugin) (93519) EVENT_HANDLER_APPLICATION_FRONT_SWITCHED: Google Chrome (42686) EVENT_HANDLER_DISPLAY_CHANGED: 2 6 space_manager_refresh_application_windows: Alacritty has windows that are not yet resolved EVENT_HANDLER_APPLICATION_DEACTIVATED: Slack EVENT_HANDLER_APPLICATION_ACTIVATED: Google Chrome event_signal_flush: transmitting window_focused to 1 subscriber(s) EVENT_HANDLER_SPACE_CHANGED: 698 space_manager_refresh_application_windows: Alacritty has windows that are not yet resolved EVENT_HANDLER_WINDOW_RESIZED: Slack 116517 EVENT_HANDLER_WINDOW_MOVED:DEBOUNCED Slack 116517 EVENT_HANDLER_WINDOW_RESIZED:DEBOUNCED Slack 116517 EVENT_HANDLER_WINDOW_TITLE_CHANGED: Slack 116517 window_manager_create_and_add_window: ignoring AXUnknown window Bartender 4 120615 window_manager_create_and_add_window: ignoring AXUnknown window Bartender 4 120616 window_manager_create_and_add_window: ignoring AXUnknown window Bartender 4 120617 EVENT_HANDLER_APPLICATION_TERMINATED: Google Chrome Helper (Plugin) (93519) EVENT_HANDLER_APPLICATION_FRONT_SWITCHED: Google Chrome (42686) EVENT_HANDLER_DISPLAY_CHANGED: 2 6 space_manager_refresh_application_windows: Alacritty has windows that are not yet resolved EVENT_HANDLER_APPLICATION_DEACTIVATED: Slack EVENT_HANDLER_APPLICATION_ACTIVATED: Google Chrome EVENT_HANDLER_WINDOW_TITLE_CHANGED: Google Chrome 120162 EVENT_HANDLER_DAEMON_MESSAGE: query --windows --window EVENT_HANDLER_DAEMON_MESSAGE: query --windows --space window_manager_create_and_add_window: ignoring AXUnknown window Google Chrome 120621 EVENT_HANDLER_APPLICATION_FRONT_SWITCHED: Alacritty (81157) EVENT_HANDLER_DISPLAY_CHANGED: 1 864 space_manager_refresh_application_windows: Alacritty has windows that are not yet resolved EVENT_HANDLER_APPLICATION_DEACTIVATED: Google Chrome EVENT_HANDLER_APPLICATION_ACTIVATED: Alacritty event_signal_flush: transmitting window_focused to 1 subscriber(s) window_manager_create_and_add_window: ignoring AXUnknown window Google Chrome 120621 EVENT_HANDLER_APPLICATION_FRONT_SWITCHED: Alacritty (81157) EVENT_HANDLER_DISPLAY_CHANGED: 1 864 space_manager_refresh_application_windows: Alacritty has windows that are not yet resolved EVENT_HANDLER_APPLICATION_DEACTIVATED: Google Chrome EVENT_HANDLER_APPLICATION_ACTIVATED: Alacritty EVENT_HANDLER_WINDOW_FOCUSED: Alacritty 118385 event_signal_flush: transmitting window_focused to 1 subscriber(s) window_manager_create_and_add_window: ignoring AXUnknown window Google Chrome 120621 EVENT_HANDLER_APPLICATION_FRONT_SWITCHED: Alacritty (81157) EVENT_HANDLER_DISPLAY_CHANGED: 1 864 space_manager_refresh_application_windows: Alacritty has windows that are not yet resolved EVENT_HANDLER_APPLICATION_DEACTIVATED: Google Chrome EVENT_HANDLER_APPLICATION_ACTIVATED: Alacritty EVENT_HANDLER_WINDOW_FOCUSED: Alacritty 118385 EVENT_HANDLER_WINDOW_RESIZED: Alacritty 118385 EVENT_HANDLER_WINDOW_MOVED:DEBOUNCED Alacritty 118385 EVENT_HANDLER_WINDOW_RESIZED: Alacritty 118369 EVENT_HANDLER_WINDOW_MOVED:DEBOUNCED Alacritty 118369 EVENT_HANDLER_WINDOW_RESIZED: Alacritty 118383 EVENT_HANDLER_WINDOW_MOVED:DEBOUNCED Alacritty 118383 EVENT_HANDLER_DAEMON_MESSAGE: query --windows --window EVENT_HANDLER_DAEMON_MESSAGE: query --windows --window EVENT_HANDLER_DAEMON_MESSAGE: query --windows --space EVENT_HANDLER_DAEMON_MESSAGE: query --windows --space EVENT_HANDLER_WINDOW_FOCUSED: Alacritty 118383 event_signal_flush: transmitting window_focused to 1 subscriber(s) EVENT_HANDLER_WINDOW_FOCUSED: Alacritty 118383 EVENT_HANDLER_DAEMON_MESSAGE: query --windows --window EVENT_HANDLER_DAEMON_MESSAGE: query --windows --space EVENT_HANDLER_WINDOW_FOCUSED: Alacritty 118369 event_signal_flush: transmitting window_focused to 1 subscriber(s) EVENT_HANDLER_WINDOW_FOCUSED: Alacritty 118369 EVENT_HANDLER_DAEMON_MESSAGE: query --windows --window EVENT_HANDLER_DAEMON_MESSAGE: query --windows --space ```

I'm running on an M1 Pro with Ventura 13.3.1 (22E261) and yabai v5.0.3 6bfbbc4

geneccx commented 1 year ago

I don't think this issue is caused by Yabai, but rather an annoying underlying issue with macOS space management:

When you connect/remove a monitor the first space on that monitor merges with the first space on your laptop.

https://superuser.com/a/1297522

Would love to be able to prevent this behaviour without having to leave my first space empty.

tau31 commented 9 months ago

Hey, was this ever solved? Thanks

koekeishiya commented 9 months ago

I have no idea. Feedback stopped after my last comment on Jan24 2020.

dimitarvdimitrov commented 9 months ago

Can someone run with debug_output on and paste a dump of the event log of what happens in this scenario?

is this debug output sufficient? https://github.com/koekeishiya/yabai/issues/259#issuecomment-1545720471

koekeishiya commented 9 months ago

I no longer remember what I was even looking for in the logs. However, looking at my last comment in this issue:

I believe the wake from sleep issue is a combination of another step that needs to be handled. The fix I did above should remap our internal spaces structure when a new display is added (which happens on a GPU switch, and also when a 4k/5k display wake from sleep). However, I believe that in some cases it will also report a display removed event before the added event. This causes yabai to untile windows in the remove event, and then it has nothing to map back to when we process the add event afterwards.

My assumption seems to be correct:

However, I believe that in some cases it will also report a display removed event before the added event. This causes yabai to untile windows in the remove event, and then it has nothing to map back to when we process the add event afterwards.

By looking at the following snippet from your debug output:

EVENT_HANDLER_DISPLAY_CHANGED: newly activated display 1 was already active (1)! ignoring event.. EVENT_HANDLER_DISPLAY_REMOVED: 2 EVENT_HANDLER_DISPLAY_REMOVED: 2 EVENT_HANDLER_DISPLAY_MOVED: 1 EVENT_HANDLER_SPACE_CHANGED: 6 space_manager_refresh_application_windows: Alacritty has windows that are not yet resolved EVENT_HANDLER_DISPLAY_CHANGED: 2 6 space_manager_refresh_application_windows: Alacritty has windows that are not yet resolved EVENT_HANDLER_DISPLAY_ADDED: 2 EVENT_HANDLER_DISPLAY_MOVED: 1 EVENT_HANDLER_SPACE_CHANGED: 6 space_manager_refresh_application_windows: Alacritty has windows that are not yet resolved EVENT_HANDLER_DISPLAY_CHANGED: newly activated display 2 was already active (2)! ignoring event.. EVENT_HANDLER_DISPLAY_ADDED: 3 EVENT_HANDLER_DISPLAY_MOVED: 1 EVENT_HANDLER_DISPLAY_RESIZED: 2

rubyroobs commented 4 months ago

This causes yabai to untile windows in the remove event, and then it has nothing to map back to when we process the add event afterwards.

I'd like to see if I'm able to make a patch for this as this is really disruptive for my workflow (2x 5k monitors...), but I wanted to ask first @koekeishiya if you know what the best approach would be to handling that behavior? Would something like a configurable delay before handling the display removal be appropriate? (and then after the delay, if the display index is still present it can just skip running the logic?)

I'm open to playing around and reporting my progress, but I have never touched yabai (or any window manager) before, so if you had a way in mind I'd appreciate if you could point me in the right direction 🙏

rubyroobs commented 3 months ago

This causes yabai to untile windows in the remove event, and then it has nothing to map back to when we process the add event afterwards.

I'd like to see if I'm able to make a patch for this as this is really disruptive for my workflow (2x 5k monitors...), but I wanted to ask first @koekeishiya if you know what the best approach would be to handling that behavior? Would something like a configurable delay before handling the display removal be appropriate? (and then after the delay, if the display index is still present it can just skip running the logic?)

I'm open to playing around and reporting my progress, but I have never touched yabai (or any window manager) before, so if you had a way in mind I'd appreciate if you could point me in the right direction 🙏

Hi @koekeishiya - sorry to bump here; I haven't actually had time myself yet to even clone the source and give this a go but I was wondering if you could take a look at my comment if you have a chance? It would be much less daunting a task if I had a semi-idea of what I was supposed to be doing 😅