Blog

All Blog Posts  |  Next Post  |  Previous Post

Managing displays with the Multi-Screen Window Placement API

Tuesday, April 19, 2022

With the release of Google Chrome 100 came the new Multi-Screen Window Placement API. This API allows you to enumerate the displays connected to your machine and to place windows on specific screens. We've wrapped this API with TMS WEB Core, so that you can use this inside your Web Applications with minimal effort. 

Before we had this API, we could use the window.open() to control windows. This method however, is unaware of additional screens. We can specify the position by passing the coordinates as left and top and pass the size as width and height. To get the information about the current screen we could check the window.screen property. 

How to use

First of all we have to check if the API is supported. we can simply do this by calling the GetScreenAvailability function. this will return true if it is supported. 

To get all necessary screen information you can call the following asynchronous function: GetScreensInfo. This will result in a TJSScreenInfo object containing a list of all the available displays as well as the current screen the window is rendered on. 

screens := await(TJSScreeninfo, GetScreensInfo);
screens.OnCurrentScreenChange := DoCurrentScreensChange;
screens.OnScreensChange := DoScreensChanged;

The Screens object also has 2 events:

  • OnCurrentScreenChange: This event updates the screens object when the web application changes screen.
  • OnScreenChange: This event updates the screens object when the browser detects that the displays have been changed.(i.e. adding/removing a display)

TMS Software Delphi  Components

The Screens object looks like this on my machine. As you can see I have to 2 Displays connected to my machine. And my CurrentScreen is "External Display 2" which is not my primary screen. Using the left and top properties of the TJSScreenDetails object, we can accurately position our window wherever we want on the display that you want.

Before we can use the Multi-Screen Window Placement API we must ask the user for permission to use it. To do this, we can call the GetPermissions function. Be aware that this function is asynchronous. so you'll have to await it. you can use it like this:

allowed := await(boolean, GetPermissions);

Weather around the world!

Using this API we made a demo application that let's you check the weather in several locations all around the world. If you have a multi display setup the weather popups will be shown on the screen the web application is not currently on. 

The result will be something like this when clicking the button.

Screen 1:

TMS Software Delphi  Components

Screen 2:

TMS Software Delphi  Components

And a screenshot from both screens together:

TMS Software Delphi  Components

To make this possible, we simply calculate how large the windows can be if we want to show a 5 x 2 table of windows.

w := Math.floor((screens.screens[i].availableWidth - FColumns * 1) / FColumns);
h := Math.floor((screens.screens[i].availableHeight - FRows * 51) / FRows);

After that we loop over the columns and rows and calculate for each window the X and Y offset. we than add it to a WindowFeatures string and call the CreatePopup function. 

    for j := 0 to FColumns -1 do
    begin
      for k := 0 to FRows -1 do
      begin
        x := j * w + screens.screens[i].availableLeft + j * 1;
        y := k * h + screens.screens[i].availableTop + k * 51;
        Features := '' +

          'screenX=' + IntToStr(x)+ ',' +
          'screenY=' + IntToStr(y)+ ',' +
          'width=' + IntToStr(w)+ ',' +
          'height=' + IntToStr(h)+ ',' +
          'menubar=' + BoolToStr(false)+ ',' +
          'toolbar=' + BoolToStr(false)+ ',' +
          'location=' + BoolToStr(false)+ ',' +
          'status=' + BoolToStr(false)+ ',' +
          'resizable=' + BoolToStr(true)+ ',' +
          'scrollbars=' + BoolToStr(false) + '';

        Popup := createPopup(Features, FCountrys[CountryIndex]);

        if Popup = nil then
        begin
          CloseAllPopups;
          window.alert('It looks like you are blocking popup windows. Please allow them as outlined at https://goo.gle/allow-popups.');
          exit;
        end;
        Popup.addEventListener('beforeunload',@DoClosePopUp);
        Popups.push(PopUp);
        inc(CountryIndex);
      end;
    end;
if the popup is nil this means that popup windows are blocked. in this case, the function will stop. Otherwise we will add an eventListener which we be called when you try to close a popup. this function will intercept the closing of the popup and close all popups instead.

try the live demo here and download the source code here!




Bradley Velghe




This blog post has received 1 comment.


1. Wednesday, April 27, 2022 at 5:36:26 AM

Wow, you supported that fast! Thank you so much, this is wonderful!

jeffam70




Add a new comment

You will receive a confirmation mail with a link to validate your comment, please use a valid email address.
All fields are required.



All Blog Posts  |  Next Post  |  Previous Post