Get width and height of the display that the currently active window is on – with a multi-monitor setup

Hi everybody,

I'd like to make me a button on my stream deck that maximises the currently active window, if not already maximised, and restores the previous window size if it is already maximised.

TL;DR

Is there a reliable way to get the width and height of the display that the currently active window is on, if there are external monitors and/or an iPad in sidecar mode attached to the computer?

Long version

Since I'm a JavaScript coder by trade, I'm using the “real JavaScript” action to make it happen, which is excellent, by the way!

My algorithm goes like this, in pseudo-code:

maximised_window_width = display_width - stage_manager_margin_width
maximised_window_height = display_height - menu_bar_height

if 
  active_window_width is not maximised_window_width or
  active_window_height is not maximised_window_height
then
  remember_window_width
  maximise_active_window
  exit_script

if 
  active_window_width is maximised_window_width and
  active_window_height is maximised_window_height
then
  restore_previous_size
  exit_script

The algorithm calculates the size a maximised window has, based on the screen width and height, with stage manager margin on the left subtracted from the width, and menu bar height subtracted from the height.

If then checks if the currently active window matches that maximised window size.

If it doesn't, the algorithm assumes the window is not maximised. It “remembers” the current window size (i.e. stores it in variables) and maximises the active window.

If the active window size matches the calculated maximised window size, the algorithm assumes the active window is already maximised and restores its previous size.

Of course, the algorithm would also have to deal with X and Y coordinates of the active window, I've left that out here for simplicity's sake.

This is all nice and well, my actual JS code gets the screen dimensions by running the shell command system_profiler SPDisplaysDataType -json and parsing its output. It gets the active window's width, height and X/Y coordinates by running an AppleScript and parsing its output.

However, the issues start with a multi monitor setup. I usually have two monitors attached to my MacBook, one big Dell widescreen display and an iPad in Sidecar-Mode.

I need to get the size of the display that the currently active window is located on for my script to work properly in multi-monitor mode.

I try to do this with this JavaScript / AppleScript:

async function getWindowAndDesktopInfo() {
  const appleScript = `
    tell application "System Events"
      set appOfInterest to name of application processes whose frontmost is true
      set currentApplication to item 1 of appOfInterest
      set firstWindow to the first window of application process currentApplication
      set windowDimensions to size of firstWindow
      set windowPosition to position of firstWindow
    end tell
    tell application "Finder"
      set screenData to bounds of window of desktop
    end tell
    return "[" & (item 1 of windowPosition as text) & "," & (item 2 of windowPosition as text) & "," & (item 1 of windowDimensions as text) & "," & (item 2 of windowDimensions as text) & "," & (item 1 of screenData as text) & "," & (item 2 of screenData as text) & "," & (item 3 of screenData as text) & "," & (item 4 of screenData as text) & "]"
    `;

  const resultStr = await runAppleScript(appleScript);
  const result = JSON.parse(resultStr);
  const [
    winPosX,
    winPosY,
    winWidth,
    winHeight,
    deskPosX,
    deskPosY,
    deskWidth,
    deskHeight
  ] = result.map(str => parseInt(str, 10));

  return {
    winPosX,
    winPosY,
    winWidth,
    winHeight,
    deskPosX,
    deskPosY,
    deskWidth,
    deskHeight
  };
}

The variables deskWidth and deskHeight, I was hoping, would give me the dimensions of the display that the active window is on. Alas, that's not the case. On my multi-monitor-setup, I get the width and height of my displays, added up – kind of, the iPad is added in a way I don't really understand. And the position coordinates I don't get, either…

On my setup, I get:

desktop_position_x = -1366
desktop_position_y = -1440
desktop_width = 2647
desktop_height = 1117

These values are dependent on which display is the main display. In the above example, it's the Mac's built in screen, which is located below the big Dell monitor. If I change the main display to be the Dell monitor, this is what I get:

desktop_position_x = -573
desktop_position_y = 0
desktop_width = 3440
desktop_height = 2557

What I'd really need is a way to find out which of my three displays the currently active window, then I could easily determine the size a maximised window would have, and thus determine if the currently active window is currently maximised.

Is there something I've overlooked here?

BTT allows you to query all variables available in the "advanced conditions". There is one set you might be able to use:

Frame of screen with focused window:
focused_screen_x
focused_screen_y
focused_screen_width
focused_screen_height

let x = await callBTT('get_number_variable', {variable_name:'focused_screen_x'})

Excellent, just what I was looking for!

I can get these variables from real JavaScript, but they don't show up in the advanced condition editor, that's why I wasn't aware of them…

Does BTT also know the X/Y coordinates and width/height of the active window, so I don't have to resort to using AppleScript to get them?

I've tried implementing a script using the variables you suggested.

Unfortunately, it doesn't work: The variables always are set to the coordinates and dimensions of the screen that currently is the main screen, i.e. that has the menu bar on it.

I need to get the coordinates and dimensions of the screen that the currently focused window is on.

See my failed attempt on GitHub

That's weird, it seems to return the correct values here.
Basically BTT loops through all screens and checks whether the screen contains the currently active window - if so it will return that screen's dimensions.

I'm afraid I have to disagree. :grinning:

I did a series of tests with 3 displays and 2 displays. No matter where the focused window was located, focused_screen_width, focused_screen_width, focused_screen_x and focused_screen_y were always the same values – the position and dimension of my largest screen, an external Dell monitor.

Could it be, perhaps, that there is a bug in the loop you mentioned so that it always picks the first element in the array of screens?

Here is the preset I've used for testing:

screen-and-window-info.bttpreset (26.0 KB)

It logs all relevant info to the BTT console when you hit the key combination Ctrl+Option+Cmd+Shift+S. Like I said, the focused_screen values are always the same, regardless of location of the focused window

Weird, I don't think the function that retrieves the focused screen is buggy as it has been used for many years in many places in BTT. Maybe something is wrong with the variables / variable retrieval as that's a pretty new feature.

Do the variables also not update in the advanced trigger conditions when you move the focused window between screens?

:thinking: In the advanced conditions editor, they do update on my machine and reflect the data of the screen with the focused window correctly

Ah interesting, I'll check whether I can reproduce it with the variable retrieval - maybe it uses some cached values or something like that.

Interestingly, the advanced conditions editor always shows 3440x1440 (size of my big monitor) when I move the BTT window on another screen. Only after I click in the BTT window again does it update to show the proper values

Here it updates regardless of whether the BTT window is active :expressionless:
Which version of macOS are you currently running (13.2 here)?

image

Here's a screen recording that shows how it behaves on my machine:

Ah, that should be ok, because the window was not focused before you clicked on it (that's a special case with the modal currently showing in BTT).

How does it behave if you move another window (not BTT)?

The same, actually. I've tried it with a finder window. When I move the window from the big screen to the built in screen, it seems to lose focus, i.e. title bar is greyed out. Only when I click the finder window's title bar again, the window is active and BTT shows the bounds of the screen with the finder window.

Maybe it has something to do with stage manager mode?

Yup, I've turned off stage manager and the finder window stays focused when I move it to another screen.

My real JS that logs the focused screen variables now shows 1366 x 1024, regardless of which screen the focused window is on. That's the size of my iPad that's attached in Sidecar mode.

I think you are right that there's some caching problem.

Unfortunately I'm not yet able to reproduce it.
If I enter this in the macOS Script editor:

tell application "BetterTouchTool"
	
	return get_number_variable "focused_screen_visible_frame_x"
	
end tell

and move the script editor from one screen to another, the output changes everytime I run the script. Does this for you always output the same? Even if the script editor is moved to another screen?

No, I also get a different value when I move the script editor to another screen. focused_screen_visible_frame_width always returns the correct value, the width of the screen I've moved the window to.

I think I'm able to reproduce the issue with Java Script. Let's see!

1 Like

In the latest alpha version 4.022, getting calling get_number_variable for focused_screen_x, focused_screen_y, focused_screen_width and focused_screen_height now all return undefined.