Mehrere Bildschirme mit der Window Management API verwalten

Informationen zu verbundenen Bildschirmen abrufen und Fenster relativ zu diesen Bildschirmen positionieren.

Fensterverwaltungs-API

Mit der Window Management API können Sie die mit Ihrem Computer verbundenen Bildschirme auflisten und Fenster auf bestimmten Bildschirmen platzieren.

Empfohlene Anwendungsfälle

Beispiele für Websites, die diese API möglicherweise verwenden:

  • Gimp-Grafikeditoren mit Mehrfenstermodus können verschiedene Bearbeitungstools in genau positionierten Fenstern platzieren.
  • Virtuelle Handelsplattformen können Markttrends in mehreren Fenstern anzeigen, von denen jedes im Vollbildmodus angezeigt werden kann.
  • Diashow-Apps können Vortragsnotizen auf dem internen Hauptbildschirm und die Präsentation auf einem externen Projektor anzeigen.

Window Management API verwenden

Das Problem

Beim bewährten Ansatz zur Steuerung von Fenstern, Window.open(), gibt es leider keine zusätzlichen Bildschirme. Auch wenn einige Aspekte dieser API, wie der DOMString-Parameter windowFeatures, ein wenig archaisch erscheinen, hat sie uns über die Jahre dennoch sehr geholfen. Zum Angeben der Position eines Fensters können Sie die Koordinaten als left und top (oder screenX bzw. screenY) und die gewünschte Größe als width und height (oder innerWidth bzw. innerHeight) übergeben. Wenn Sie beispielsweise ein Fenster mit 400 × 300 Pixeln 50 Pixel von links und 50 Pixel von oben öffnen möchten, können Sie diesen Code verwenden:

const popup = window.open(
  'https://example.com/',
  'My Popup',
  'left=50,top=50,width=400,height=300',
);

Informationen zum aktuellen Bildschirm erhalten Sie über das Attribut window.screen, das ein Screen-Objekt zurückgibt. Die Ausgabe auf meinem MacBook Pro 13′′:

window.screen;
/* Output from my MacBook Pro 13″:
  availHeight: 969
  availLeft: 0
  availTop: 25
  availWidth: 1680
  colorDepth: 30
  height: 1050
  isExtended: true
  onchange: null
  orientation: ScreenOrientation {angle: 0, type: "landscape-primary", onchange: null}
  pixelDepth: 30
  width: 1680
*/

Wie die meisten anderen IT-Mitarbeiter musste ich mich an die neue Arbeitswelt gewöhnen und mein privates Homeoffice einrichten. Meine Konfiguration sieht so aus wie auf dem Foto unten. Bei Interesse können Sie sich die vollständigen Details zu meiner Einrichtung ansehen. Das iPad neben meinem MacBook ist über Sidecar mit dem Laptop verbunden, sodass ich das iPad bei Bedarf schnell in einen zweiten Bildschirm verwandeln kann.

Schulbank auf zwei Stühlen. Auf der Schulbank stehen Schuhboxen für einen Laptop und zwei iPads, die ihn umgeben.
Eine Multiscreen-Einrichtung.

Wenn ich den größeren Bildschirm nutzen möchte, kann ich das Pop-up aus dem obigen Codebeispiel auf dem zweiten Bildschirm platzieren. Ich mache das wie folgt:

popup.moveTo(2500, 50);

Dies ist nur eine grobe Schätzung, da die Abmessungen des zweiten Bildschirms nicht ermittelt werden können. Die Informationen von window.screen beziehen sich nur auf den integrierten Bildschirm, aber nicht auf den iPad-Bildschirm. Die gemeldete width des integrierten Bildschirms betrug 1680 Pixel. Wenn Sie zu 2500 Pixeln wechseln, kann das Fenster zum iPad verschoben werden, da ich zufällig weiß, dass es sich auf der rechten Seite meines MacBook befindet. Wie kann ich dies im Allgemeinen tun? Es gibt also einen besseren Weg, als zu raten. Das ist die Window Management API.

Funktionserkennung

So prüfen Sie, ob die Window Management API unterstützt wird:

if ('getScreenDetails' in window) {
  // The Window Management API is supported.
}

Die Berechtigung window-management

Bevor ich die Window Management API verwenden kann, muss ich den Nutzer um die entsprechende Berechtigung bitten. Die Berechtigung window-management kann so mit der Permissions API abgefragt werden:

let granted = false;
try {
  const { state } = await navigator.permissions.query({ name: 'window-management' });
  granted = state === 'granted';
} catch {
  // Nothing.
}

Während Browser mit dem alten und dem neuen Berechtigungsnamen bereits verwendet werden, müssen Sie bei der Berechtigungsanfrage den Schutzcode verwenden, wie im folgenden Beispiel dargestellt.

async function getWindowManagementPermissionState() {
  let state;
  // The new permission name.
  try {
    ({ state } = await navigator.permissions.query({
      name: "window-management",
    }));
  } catch (err) {
    return `${err.name}: ${err.message}`;
  }
  return state;
}

document.querySelector("button").addEventListener("click", async () => {
  const state = await getWindowManagementPermissionState();
  document.querySelector("pre").textContent = state;
});

Der Browser kann festlegen, dass die Berechtigungsaufforderung beim ersten Versuch, eine der Methoden der neuen API zu verwenden, dynamisch angezeigt wird. Lesen Sie weiter, um mehr zu erfahren.

Das Attribut window.screen.isExtended

Um herauszufinden, ob mehr als ein Bildschirm mit meinem Gerät verbunden ist, rufe ich die Eigenschaft window.screen.isExtended auf. Sie gibt true oder false zurück. Bei meiner Einrichtung wird true zurückgegeben.

window.screen.isExtended;
// Returns `true` or `false`.

Die Methode getScreenDetails()

Da ich jetzt weiß, dass derzeit Multiscreen eingerichtet ist, kann ich mit Window.getScreenDetails() weitere Informationen zum zweiten Bildschirm abrufen. Durch Aufrufen dieser Funktion wird eine Berechtigungsaufforderung angezeigt, in der ich gefragt werde, ob die Website Fenster auf meinem Bildschirm öffnen und platzieren darf. Die Funktion gibt ein Versprechen zurück, das mit einem ScreenDetailed-Objekt aufgelöst wird. Auf meinem MacBook Pro 13 mit einem verbundenen iPad enthält dies ein screens-Feld mit zwei ScreenDetailed-Objekten:

await window.getScreenDetails();
/* Output from my MacBook Pro 13″ with the iPad attached:
{
  currentScreen: ScreenDetailed {left: 0, top: 0, isPrimary: true, isInternal: true, devicePixelRatio: 2, …}
  oncurrentscreenchange: null
  onscreenschange: null
  screens: [{
    // The MacBook Pro
    availHeight: 969
    availLeft: 0
    availTop: 25
    availWidth: 1680
    colorDepth: 30
    devicePixelRatio: 2
    height: 1050
    isExtended: true
    isInternal: true
    isPrimary: true
    label: "Built-in Retina Display"
    left: 0
    onchange: null
    orientation: ScreenOrientation {angle: 0, type: "landscape-primary", onchange: null}
    pixelDepth: 30
    top: 0
    width: 1680
  },
  {
    // The iPad
    availHeight: 999
    availLeft: 1680
    availTop: 25
    availWidth: 1366
    colorDepth: 24
    devicePixelRatio: 2
    height: 1024
    isExtended: true
    isInternal: false
    isPrimary: false
    label: "Sidecar Display (AirPlay)"
    left: 1680
    onchange: null
    orientation: ScreenOrientation {angle: 0, type: "landscape-primary", onchange: null}
    pixelDepth: 24
    top: 0
    width: 1366
  }]
}
*/

Informationen zu den verbundenen Bildschirmen finden Sie im Array screens. Der Wert von left für das iPad beginnt bei 1680, was genau der width des integrierten Displays entspricht. Dadurch kann ich genau feststellen, wie die Bildschirme logisch angeordnet sind (nebeneinander, übereinander usw.). Es sind jetzt auch Daten für jeden Bildschirm vorhanden, die zeigen, ob es sich um einen isInternal-Bildschirm handelt und ob es sich um einen isPrimary-Bildschirm handelt. Beachten Sie, dass der integrierte Bildschirm nicht unbedingt der Hauptbildschirm ist.

Das Feld currentScreen ist ein Live-Objekt, das dem aktuellen window.screen entspricht. Das Objekt wird bei geräteübergreifender Platzierung von Fenstern oder Geräteänderungen aktualisiert.

Das Ereignis screenschange

Jetzt fehlt nur noch die Möglichkeit zu erkennen, wenn sich meine Bildschirmeinrichtung ändert. Genau das bewirkt das neue Ereignis screenschange: Es wird jedes Mal ausgelöst, wenn die Bildschirmkonstellation geändert wird. (Beachten Sie, dass „screens“ im Ereignisnamen als Plural bezeichnet wird.) Das bedeutet, dass das Ereignis immer dann ausgelöst wird, wenn ein neuer Bildschirm oder ein vorhandener Bildschirm (physisch oder virtuell im Fall von Sidecar) angeschlossen oder ausgesteckt wird.

Sie müssen die neuen Bildschirmdetails asynchron abrufen. Das screenschange-Ereignis selbst liefert diese Daten nicht. Verwenden Sie zum Abrufen der Bildschirmdetails das Live-Objekt aus einer im Cache gespeicherten Screens-Oberfläche.

const screenDetails = await window.getScreenDetails();
let cachedScreensLength = screenDetails.screens.length;
screenDetails.addEventListener('screenschange', (event) => {
  if (screenDetails.screens.length !== cachedScreensLength) {
    console.log(
      `The screen count changed from ${cachedScreensLength} to ${screenDetails.screens.length}`,
    );
    cachedScreensLength = screenDetails.screens.length;
  }
});

Das Ereignis currentscreenchange

Wenn ich nur Änderungen am aktuellen Bildschirm (d. h. am Wert des Live-Objekts currentScreen) interessiert, kann ich auf das currentscreenchange-Ereignis warten.

const screenDetails = await window.getScreenDetails();
screenDetails.addEventListener('currentscreenchange', async (event) => {
  const details = screenDetails.currentScreen;
  console.log('The current screen has changed.', event, details);
});

Das Ereignis change

Wenn mich schließlich nur Änderungen an einem bestimmten Bildschirm interessiert sind, kann ich auf das change-Ereignis dieses Bildschirms warten.

const firstScreen = (await window.getScreenDetails())[0];
firstScreen.addEventListener('change', async (event) => {
  console.log('The first screen has changed.', event, firstScreen);
});

Neue Vollbildoptionen

Bisher konnten Sie die Anzeige von Elementen im Vollbildmodus über die passend benannte Methode requestFullScreen() anfordern. Die Methode verwendet einen options-Parameter, an den Sie FullscreenOptions übergeben können. Bisher lautete seine einzige Property navigationUI. Die Window Management API fügt die neue screen-Eigenschaft hinzu, mit der Sie festlegen können, auf welchem Bildschirm die Vollbildansicht gestartet werden soll. Gehen Sie zum Beispiel so vor, wenn Sie den Hauptbildschirm als Vollbild anzeigen lassen möchten:

try {
  const primaryScreen = (await getScreenDetails()).screens.filter((screen) => screen.isPrimary)[0];
  await document.body.requestFullscreen({ screen: primaryScreen });
} catch (err) {
  console.error(err.name, err.message);
}

Polyfill

Es ist nicht möglich, die Window Management API mit Polyfill zu füllen. Sie können jedoch ihre Form anpassen, um ausschließlich für die neue API zu programmieren:

if (!('getScreenDetails' in window)) {
  // Returning a one-element array with the current screen,
  // noting that there might be more.
  window.getScreenDetails = async () => [window.screen];
  // Set to `false`, noting that this might be a lie.
  window.screen.isExtended = false;
}

Die anderen Aspekte der API, also die verschiedenen Bildschirmänderungsereignisse und die screen-Eigenschaft des FullscreenOptions, würden von nicht unterstützten Browsern einfach nie ausgelöst oder unbemerkt ignoriert werden.

Demo

Wenn Sie so ähnlich wie ich sind, behalten Sie die Entwicklung der verschiedenen Kryptowährungen genau im Auge. (In Wirklichkeit tue ich es eigentlich nicht, weil ich diesen Planeten liebe, aber für diesen Artikel nehme ich einfach an, dass ich das getan habe.) Damit ich den Überblick über meine Kryptowährungen behalten kann, habe ich eine Web-App entwickelt, mit der ich die Märkte in allen Lebenslagen beobachten kann, z. B. bequem vom Bett aus, wo ich eine angemessene Einzelbildschirmeinrichtung habe.

Riesiger Fernsehbildschirm am Ende eines Betts, die Beine des Autors sind teilweise zu sehen. Auf dem Bildschirm ist eine gefälschte Handelsplattform für Kryptowährungen zu sehen.
Entspannen und die Märkte beobachten

In puncto Kryptowährungen kann es an den Märkten jederzeit hektisch werden. In diesem Fall kann ich schnell zu meinem Schreibtisch wechseln, wo ich eine Multiscreen-Einrichtung habe. Ich kann auf das Fenster einer Währung klicken und auf der gegenüberliegenden Seite alle Details in einer Vollbildansicht sehen. Unten siehst du ein aktuelles Foto von mir, das ich während des letzten YCY-Blutbads aufgenommen habe. Das traf mich völlig unvorbereitet und ich hatte die Hände auf dem Gesicht gelassen.

Der Autor hält seine Hände auf sein panisches Gesicht und starrt auf den gefälschten Devisenmarkt für Kryptowährungen.
Panicky, Zeugin des YCY-Blutbads

Du kannst die unten eingebettete Demo ausprobieren oder dir den Quellcode auf „Glitch“ ansehen.

Sicherheit und Berechtigungen

Das Chrome-Team hat die Window Management API nach den unter Zugriff auf leistungsstarke Webplattform-Funktionen steuern definierten Kernprinzipien wie Nutzersteuerung, Transparenz und Ergonomie entworfen und implementiert. Die Window Management API stellt neue Informationen zu den mit einem Gerät verbundenen Bildschirmen bereit, wodurch die Fingerprinting-Oberfläche von Nutzern vergrößert wird, insbesondere wenn mehrere Bildschirme ständig mit ihren Geräten verbunden sind. Um diese Bedenken hinsichtlich des Datenschutzes auszuräumen, sind die Eigenschaften des sichtbaren Displays auf das für gängige Placement-Anwendungsfälle erforderliche Minimum beschränkt. Damit Websites Informationen auf mehreren Bildschirmen abrufen und Fenster auf anderen Bildschirmen platzieren können, ist eine Nutzerberechtigung erforderlich. Chromium gibt zwar detaillierte Bildschirmlabels zurück, Browser können aber auch weniger beschreibende (oder auch leere Labels) zurückgeben.

Nutzersteuerung

Der Nutzer hat die volle Kontrolle über die Offenlegung seiner Einrichtung. Er kann die Berechtigungsaufforderung akzeptieren oder ablehnen und eine zuvor gewährte Berechtigung über die Funktion „Websiteinformationen“ im Browser widerrufen.

Kontrolle über das Unternehmen

Chrome Enterprise-Nutzer können verschiedene Aspekte der Window Management API steuern, wie im entsprechenden Abschnitt der Einstellungen für atomare Richtliniengruppen beschrieben.

Transparenz

Ob die Berechtigung zur Verwendung der Window Management API gewährt wurde, wird in den Websiteinformationen des Browsers angegeben und kann auch über die Berechtigungen API abgefragt werden.

Berechtigungspersistenz

Der Browser behält die Berechtigungserteilungen bei. Die Berechtigung kann über die Websiteinformationen des Browsers widerrufen werden.

Feedback

Das Chrome-Team möchte mehr über Ihre Erfahrungen mit der Window Management API hören.

Informationen zum API-Design

Gibt es etwas an der API, das nicht so funktioniert, wie Sie erwartet haben? Oder fehlen Methoden oder Eigenschaften, um Ihre Idee zu implementieren? Haben Sie eine Frage oder einen Kommentar zum Sicherheitsmodell?

  • Melden Sie ein Spezifikationsproblem im entsprechenden GitHub-Repository oder fügen Sie Ihre Gedanken zu einem vorhandenen Problem hinzu.

Problem mit der Implementierung melden

Haben Sie einen Fehler bei der Implementierung in Chrome gefunden? Oder unterscheidet sich die Implementierung von der Spezifikation?

  • Melden Sie einen Fehler unter new.crbug.com. Geben Sie so viele Details wie möglich und eine einfache Anleitung zum Reproduzieren an. Geben Sie Blink>Screen>MultiScreen in das Feld Komponenten ein. Glitch eignet sich perfekt, um schnelle und einfache Reproduzierungen zu teilen.

Unterstützung für die API zeigen

Möchten Sie die Window Management API verwenden? Ihre öffentliche Unterstützung hilft dem Chrome-Team, Funktionen zu priorisieren, und zeigt anderen Browseranbietern, wie wichtig es ist, sie zu unterstützen.

  • Teile uns im WICG Discourse-Thread mit, wie du es verwenden möchtest.
  • Senden Sie einen Tweet mit dem Hashtag #WindowManagement an @ChromiumDev und teilen Sie uns mit, wo und wie Sie den Dienst verwenden.
  • Bitten Sie andere Browseranbieter, die API zu implementieren.

Nützliche Links

Danksagungen

Die Window Management API-Spezifikation wurde von Victor Costan, Joshua Bell und Mike Wasserman bearbeitet. Die API wurde von Mike Wasserman und Adrienne Walker implementiert. Dieser Artikel wurde von Joe Medley, François Beaufort und Kayce Basques gelesen. Vielen Dank an Laura Torrent Puig für die Fotos.