Windows keep moving between screens after sleep

Hi. I have a 24" iMac connected to a 21" Ultrafine. Since installing Monterey most (not all) of the windows on the Ultrafine move back to the iMac every time it goes to sleep. It’s rather frustrating. Has anyone else had this and, if so, found a fix?

1 Like

I am experiencing the same issue when using Universal Control with my iMac and iPad. Its so frustrating that I have turned off Universal Control. Maybe the latest update has fixed it.

Sidecar is playing up for me too. It either freezes or keeps disconnecting. Multiple screen management on Mac at the moment is a mess.

This. Cordless Dog: Stay

Been using it for years. It enables you to save app window positions in a multi-monitor setup, and then automatically moves them back to the right place when the mac wakes from sleep.

I find this is a “feature” of MacOS. I don’t think it is new.

I have Things and Mail on my second monitor. I use a Keyboard Maestro macro triggered at system wake to move the windows back to where they belong.

1 Like

Could you please share the macro?

Curious. I’ve been using a multi-monitor setup for over 10 years. My current setup is four monitors, ten spaces, and an iPad Pro connected via Universal Control.

And the only time windows change monitors on their own is when I switch one monitor from/to my Mac to my work PC. That is, when the monitor is/is not available to the Mac. I never have this “moving window” issue when the system wakes from sleep.

Clearly it is happening, and I wonder if it has to do with the particular monitors being used. Note that I have used a 2010 MacPro, a 2018 Mac mini, and now a Mac Studio, none of which have a built in monitor. Perhaps that is significant?


I have a slightly different use case, as hinted at above. I share a monitor between my work PC and personal Mac, via an HDMI switch. At the end of the day I shut down the PC, and the monitor automatically switches to the Mac. I’d like to have some windows now move to that monitor. Do you know if Stay can handle this? I’ll probably download it and try it out regardless (it’s what I do with software :slight_smile: ), but knowing might save me some time. Thanks in advance.

Thanks for comments so far. I’m using the same set up as I was on Big Sur - it didn’t;t happen until I updated to Monterey. I too use about 5 spaces on each monitor and have an iPad in Sidecar.

Happy to. At work right now, but I will post tonight when I get home. It’s a bit complex as I have a whole system for positioning windows but I can post the relevant sub macros with a description.

OK, here is a bunch of stuff that will explain how I position windows. There are a bunch of macros, all but one are utility subroutines that are used by my other window placement macros. I have written out an explanation. I’m sure there are plenty of other ways to do this, but this is what I put together. You might want to download this screenshots and view in Preview where you can make them much bigger to see what is going on. I don’t know if there is a way to enbiggen them here, so apologies on that.

As I wrote up the notes I found a few bugs. Things that do not affect the way that I use the windows, but in some of the parameter checks, which won’t work exactly as they should. Since I am careful with the parameters I feed into these macros I haven’t had any issues, but I will shortly fix this stuff up.

I can upload a macro library if anyone is interested, but this may be more overhead than most people need or want. I also want to modify my subroutines, because they all assume they are working with the frontmost window of the frontmost app, and KM does provide a way to handle other windows, which I have not yet played with. I might modify my subs to handle that as well and default to the front window/front app if no window is specified, to make them more general.

A) This is the macro that actually positions the windows. I actually have a macro group called “Contexts” which basically sets up apps and windows for different functions, and this is one of them. I can fire these off via Alfred, or via a KM palette, or from the KM menu bar icon.

  1. I check to see if I have 1 or 2 screens. If 1, I just assume it’s my laptop open without attached monitors and will put the windows on the laptop screen, and if 2 I assume my laptop is in clamshell with my two external displays and I want these windows on external screen 2. It works for now but is kludgey and not very elegant so someday I will redo this logic.
  2. Active Things if it is not already running
  3. Wait until Things is running
  4. This AppleScript is supposed to make sure there is actually a Things window open and if not open one
  5. Move the window to the proper location, full height, left 1/3, on the proper screen. How this works comes up later in the description for the “Set Front Window To Location” subroutine.
  6. Do the same steps for mail, putting the window as full height, right 2/3. Note that the AppleScript to ensure there is a message viewer open is not reliable, but I am not sure why, so when I have time I will play with this.
  7. If you have Moom, you could create a snapshot for these windows and just have Moom position them for you using this AppleScript.

Note that this macro implicitly assumes that Things and Mail have only one window open (or none and one will be opened as in step 4), which is nearly always the case for me, so it works in my usage well enough. The logic could be expanded and some day may make it smarter about handling the situation where there are multiple windows open, especially if Mail has a few messages open in addition to one or more message viewers. To do that I need to update the subroutines given below, all of which assume they will work on the front window of the frontmost app. KM can handle other windows but I haven’t needed that and so haven’t added that into these subroutines yet.

B) Subroutine “Set Front Window To Location” is used to position the frontmost window.

  1. It takes a bunch of arguments, specifically the vertical and horizontal location for the window, the vertical and horizontal grid size, vertical and horizontal span values, and a screen to position on. The logic is that the screen is considered to be divided into a grid of m rows by n columns with each row and each column of equal size. A window’s location is the box on that grid where the top left corner of the window is placed. A window can span 1 or more columns and rows. This is just like how Moom does it when you set up custom windows, except that with Moom you set the rows and columns and that applies to every window template, while here you can call this subroutine with different values any time you use it. Thus, if you make the verticalgrid and horizontal grid 5 x 5, set vertical and horizontal location both to 2, and both spans at 3, you would get a 3 x 3 box window with 1 box around all of the margins. (I hope that makes sense). That way, to make a window that is full screen in height at the right 2/3 of the screen, I set the grid to be vertical 3, horizontal 1 (1 row x 3 cols), make the location vertical 1 horizontal 2 (there is only one vertical or row in the grid, but start with the top left of the second column) and set the horizontal span to 2 to span 2 columns, each of which is 1/3 the screen width).
    Also: I have another subroutine which takes these parameters as a single argument which is a JSON dictionary which is more convenient when calling these macros outside of KM such as in a kmtrigger: URL callback. That subroutine just unpacks the dictionary and calls this macro.
  2. If a screen was not specified, assume the front window will stay on its current screen. The sub “Get Screen for Front Window” figures out where the front window is.
  3. I pass the arguments over to a sub which calculates the proper coordinates on the proper screen to put the window and return that location. Note the use of one additional variable, “window_control_grid_spacing”. This is a KM global variable that specifies, in pixels, the size of a margin to put around the window when fitting into the grid. I like to have my windows not quite touching, so I generally set this to 1 or 2.
  4. Move the window to the calculated location.

C) “Get Screen For Front Window” figures out what screen the front window is on. I make the assumption that the screen containing the top left corner of the window is the screen it is “on,” so if a window is split between two screens this might not always work as expected. It works for my use, but others might prefer this to return the screen containing the majority of the window, for example.

  1. Get the screen coordinates for the front window.
  2. Initialize counter
  3. Loop through all of the screens
  4. If the top left of the window is on this screen, then:
  5. Return this screen as the window’s location; otherwise increment the counter to check the next screen
  6. In theory we should never get to this error location, and in fact my macros that call this subroutine don’t check the result. I don’t think it is possible to drag a window so that its top left corner is not on any attached screen, but that could happen on wake from sleep, disconnecting and reconnecting monitors, or through the action of a program, perhaps, and then this stuff could fail.

D) Sub “Calculate Window Coordinates” is the guts of figuring out the grid and where the window needs to go on it.

  1. All of the arguments we started with eventually get passed down to here. These three subs are never directly invoked, they are used by my library of window placement macros in various forms to move windows around.
  2. These groups just make sure that the arguments make sense. For example, if you try to place a window in row -1 or set a span > than the respective available rows or columns. This needs to be fixed up a bit as I see looking at it now that there are errors here that I should correct.
  3. Local_screen now contains the boundaries of the screen we are putting the window on
  4. Calculate the coordinates for the window. The calculations are pretty straightforward. For example, the left hand side will go at
    MAX(FLOOR(Local_screen.Left + (Local_screen.Width / Local_horizontalGrid * (Local_horizontalLocation-1)) + Local_margin), Local_screen.Left)
  5. Return the new window coordinates in the format “Left,Top,Width,Height”. KM understands a comma separate string like this and can let you pull out individual coordinates, eg Local_.coordinates.Left.
6 Likes

I’ll answer my own question.

It can. I am happy.

I saw a few people on Twitter discussing a similar issue a couple of days ago:

Ethan Schoonover offered a straight-up AppleScript solution in this reply and there’s a CLI-based solution elsewhere in the thread.

Hope that’s helpful!