ניהול מספר מסכים באמצעות window Management API

קבלת מידע על מסכים מחוברים ועל מיקום החלונות ביחס למסכים האלה.

ממשק API לניהול חלונות

ה-Window Management API מאפשר לך לספור את המסכים שמחוברים למכונה שלך ולהציב חלונות במסכים ספציפיים.

תרחישים לדוגמה

דוגמאות לאתרים שעשויים להשתמש בממשק API זה:

  • כלי עריכה גרפיים עם ריבוי חלונות יכולים להיעזר ב-Gimp כדי למקם כלי עריכה שונים בחלונות במיקום מדויק.
  • בשולחנות המסחר הווירטואליים אפשר להציג את המגמות בשוק בכמה חלונות, שבכל אחד מהם אפשר להציג במצב מסך מלא.
  • אפליקציות מצגת יכולות להציג הערות דובר במסך הראשי הפנימי ובמצגת במקרן חיצוני.

איך משתמשים ב-Window Management API

הבעיה

לצערנו, הגישה שנבדקה בזמן לשליטה בחלונות, Window.open(), לא מודעת למסכים נוספים. חלק מההיבטים של ה-API הזה נראים קצת מיושנים, למשל הפרמטר windowFeatures DOMString שלו, אבל הוא שירת אותנו היטב לאורך השנים. כדי לציין את המיקום של החלון, אפשר להעביר את הקואורדינטות כ-left ו-top (או screenX ו-screenY בהתאמה) ולהעביר את הגודל הרצוי כ-width ו-height (או innerWidth ו-innerHeight בהתאמה). לדוגמה, כדי לפתוח חלון בגודל 400×300 פיקסלים משמאל ו-50 פיקסלים מלמעלה, תוכלו להשתמש בקוד הבא:

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

על מנת לקבל מידע על המסך הנוכחי, מסתכלים על המאפיין window.screen, שמחזיר אובייקט Screen. זהו הפלט ב-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
*/

כמו רוב האנשים שעובדים בתחום הטכנולוגיה, נאלצתי להתאים את עצמי למציאות בעבודה החדשה ולהגדיר את המשרד הביתי האישי שלי. שלי נראה כמו בתמונה שלמטה (אם אתם מעוניינים, תוכלו לקרוא את הפרטים המלאים על ההגדרה שלי). ה-iPad שליד ה-MacBook מחובר למחשב הנייד באמצעות Sidecar, כך שבמקרה הצורך, אוכל להפוך את ה-iPad למסך שני במהירות.

ספסל בית ספר על שני כיסאות. מעל ספסל בית הספר יש קופסאות נעליים עם תמיכה במחשב נייד ושני מכשירי iPad מקיפים אותו.
הגדרה של ריבוי מסכים.

אם אני רוצה לנצל את המסך הגדול יותר, אני יכול להעביר למסך השני את החלון הקופץ מדוגמת הקוד שלמעלה. אעשה זאת כך:

popup.moveTo(2500, 50);

זוהי ניחוש גס, כי אין דרך לדעת מה המידות של המסך השני. המידע מ-window.screen מתייחס רק למסך המובנה, ולא למסך ה-iPad. ה-width המדווח של המסך המובנה היה 1680 פיקסלים, לכן העברה ל-2500 פיקסלים עשויה להעביר את החלון ל-iPad, כי אני יודעת שהוא ממוקם בצד ימין של ה-MacBook. איך אפשר לעשות את זה במקרה הכללי? מתברר שיש דרך טובה יותר מאשר לנחש. כך אפשר להשתמש ב-Window Management API.

זיהוי תכונות

כדי לבדוק אם יש תמיכה ב-Window Management API, משתמשים ב:

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

ההרשאה window-management

כדי להשתמש ב-Window Management API, עליי לבקש מהמשתמש הרשאה לעשות זאת. אפשר לשלוח שאילתות על ההרשאה window-management דרך Permissions API, באופן הבא:

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

כאשר משתמשים בדפדפנים עם שם ההרשאה הישן והחדש, יש להקפיד להשתמש בקוד ההגנה כשמבקשים הרשאה, כמו בדוגמה שבהמשך.

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;
});

הדפדפן יכול לבחור להציג את הבקשה להרשאה באופן דינמי בניסיון הראשון להשתמש באחת מהשיטות של ה-API החדש. מידע נוסף מפורט בהמשך.

הנכס window.screen.isExtended

כדי לראות אם למכשיר שלי מחובר יותר ממסך אחד, אני ניגשת לנכס window.screen.isExtended. הפונקציה מחזירה את הערך true או false. ההגדרה שלי מחזירה את הערך true.

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

שיטת getScreenDetails()

עכשיו, אחרי שאני יודעת שההגדרה הנוכחית היא של ריבוי מסכים, אוכל לקבל מידע נוסף על המסך השני באמצעות Window.getScreenDetails(). כשקוראים לפונקציה הזו, מוצגת בקשה להרשאה שואלת אם האתר יכול לפתוח ולמקם חלונות במסך. הפונקציה מחזירה הבטחה שמובילה לאובייקט ScreenDetailed. ב-MacBook Pro 13 עם מכשיר iPad מחובר, כולל שדה screens עם שני אובייקטים של ScreenDetailed:

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
  }]
}
*/

מידע על המסכים המחוברים זמין במערך screens. שים לב כיצד הערך של left ב-iPad מתחיל ב-1680, שהוא בדיוק width של המסך המובנה. כך אני יכול לקבוע בדיוק איך המסכים מסודרים באופן לוגי (זה לצד זה, זה מעל זה וכו'). יש עכשיו גם נתונים לכל מסך שמראים אם הוא מסך isInternal ואם הוא מסך isPrimary. שימו לב שהמסך המובנה הוא לא בהכרח המסך הראשי.

השדה currentScreen הוא אובייקט פעיל שתואם לאובייקט window.screen הנוכחי. האובייקט מתעדכן במיקומים בחלונות במסכים שונים או בשינויים במכשיר.

האירוע של screenschange

הדבר היחיד שחסר עכשיו הוא דרך לזהות מתי הגדרת המסך שלי משתנה. אירוע חדש, screenschange, מבצע בדיוק את הפעולה הזו: הוא מופעל בכל פעם שמתבצע שינוי בקבוצת הכוכבים של המסך. (שימו לב שבשם האירוע המילה 'מסכים' היא רבים.) המשמעות היא שהאירוע מופעל בכל פעם שמסך חדש או מסך קיים מחובר (פיזי או וירטואלי, ב-Sidecar) לחשמל או לניתוק.

חשוב לשים לב שצריך לחפש את פרטי המסך החדש באופן אסינכרוני, האירוע screenschange עצמו לא מספק את הנתונים האלה. כדי לחפש את פרטי המסך, משתמשים באובייקט הפעיל מממשק Screens ששמור במטמון.

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;
  }
});

האירוע של currentscreenchange

אם רק רוצים לבצע שינויים במסך הנוכחי (כלומר, הערך של האובייקט הפעיל currentScreen), יש לי אפשרות להאזין לאירוע currentscreenchange.

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

האירוע של change

לסיום, אם אני רוצה לבצע רק שינויים במסך בטון, אוכל להאזין לאירוע change של המסך.

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

אפשרויות חדשות במסך מלא

עד עכשיו אפשר היה לבקש שהרכיבים יוצגו במצב מסך מלא באמצעות השיטה requestFullScreen() בעלת השם המתאים. השיטה מקבלת פרמטר options שאליו אפשר להעביר את הערך FullscreenOptions. עד כה, הנכס היחיד שלו היה navigationUI. ה-Window Management API מוסיף מאפיין screen חדש שמאפשר לבחור באיזה מסך להתחיל את תצוגת המסך המלא. לדוגמה, אם רוצים להציג את המסך הראשי במסך מלא:

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);
}

פוליפיל

אי אפשר למלא את ה-window Management API, אבל אפשר להחליף את הצורה שלו כדי לקודד אך ורק מול ה-API החדש:

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;
}

ההיבטים האחרים של ה-API, כלומר אירועי שינוי המסך השונים והמאפיין screen של FullscreenOptions, פשוט אף פעם לא יופעלו או יתעלמו מהם באופן שקט, בהתאמה, על ידי דפדפנים שלא תומכים.

הדגמה (דמו)

אם כמוני, חשוב מאוד לעקוב מקרוב אחר ההתפתחות של המטבעות הווירטואליים השונים. (במציאות אני מאוד לא אוהב את הכוכב הזה, אבל לצורך המאמר הזה, נראה שאעשה זאת.) כדי לעקוב אחרי המטבעות הקריפטוגרפיים שבבעלותי, פיתחתי אפליקציית אינטרנט שמאפשרת לי לצפות בשווקים בכל מצב חיים, למשל כשהמיטה בבית שלי, שבה יש לי תצוגה סבירה במסך אחד.

מסך טלוויזיה ענק בקצה מיטה, שרגלי המחבר שלו גלויות בחלקן. במסך מוצג דסק מזויף למסחר במטבעות וירטואליים.
רגיעה וצפייה בשווקים.

מדובר במטבעות וירטואליים, ולכן השווקים עלולים להיות עמוסים בכל רגע נתון. במקרה כזה, אוכל לעבור במהירות לשולחן העבודה שלי, שבו יש לי הרבה מסכים. אני יכול ללחוץ על חלון של כל מטבע ולראות במהירות את הפרטים המלאים במסך מלא במסך הנגדי. בהמשך מופיעה תמונה שצילמתי לאחרונה במרחצת הדם האחרונה של YCY. זה תפס אותי לגמרי והשאיר אותי עם הידיים על הפנים.

המחבר עם ידיו על הפנים המבוהלות בוהה בדסק למסחר במטבעות קריפטו מזויף.
פניקי, עדה למרחיצת הדם של YCY.

אפשר לשחק בהדגמה שמוטמעת בהמשך, או לראות את קוד המקור שלה עם תקלה.

אבטחה והרשאות

צוות Chrome תכנן והטמיע את window Management API באמצעות עקרונות הליבה שהוגדרו במאמר שליטה בגישה לתכונות מתקדמות של פלטפורמת אינטרנט, כולל בקרת משתמשים, שקיפות וארגונומיה. ה-Window Management API חושף מידע חדש על המסכים שמחוברים למכשיר, ומגדיל את השטח ליצירת טביעות אצבע של משתמשים, במיוחד משתמשים שיש להם מספר מסכים שמחוברים באופן עקבי למכשירים שלהם. כדי להפחית את הפגיעה בפרטיות, מאפייני המסך החשוף מוגבלים למינימום הדרוש בתרחישים נפוצים של מיקומי מודעות. נדרשת הרשאת משתמש כדי שאתרים יוכלו לקבל מידע על ריבוי מסכים ולהציב חלונות במסכים אחרים. למרות ש-Chromium מחזיר תוויות מסך מפורטות, דפדפנים יכולים להחזיר בצורה פחות תיאורית (או אפילו תוויות ריקות).

בקרת משתמשים

למשתמש יש שליטה מלאה על החשיפה של ההגדרות שלו. הם יכולים לקבל או לדחות את בקשת ההרשאה, ולבטל הרשאה שהוענקה בעבר דרך התכונה 'פרטי אתרים' בדפדפן.

בקרה ארגונית

משתמשי Chrome Enterprise יכולים לשלוט בכמה היבטים של window Management API, כפי שמתואר בקטע הרלוונטי בהגדרות של Atomic Policy Groups.

שקיפות

העובדה אם ההרשאה לשימוש ב-Window Management API הוענקה גלויה בפרטי האתר של הדפדפן וניתן להריץ שאילתות לגביה גם דרך Permissions API.

שמירת הרשאה

הדפדפן ממשיך להעניק הרשאות. ניתן לבטל את ההרשאה באמצעות פרטי האתר של הדפדפן.

משוב

צוות Chrome ישמח לשמוע על החוויה שלך בשימוש ב-Window Management API.

לספר לנו על עיצוב ה-API

האם יש משהו ב-API שלא פועל כצפוי? או האם חסרים שיטות או מאפיינים שאתם צריכים ליישם את הרעיון? יש לכם שאלה או הערה לגבי מודל האבטחה?

  • שלחו בעיה במפרט במאגר GitHub המתאים, או הוסיפו את המחשבות שלכם לבעיה קיימת.

דיווח על בעיה בהטמעה

האם מצאת באג בהטמעה של Chrome? או שההטמעה שונה מהמפרט?

  • אפשר לדווח על באג בכתובת new.crbug.com. חשוב לכלול כמה שיותר פרטים, הוראות פשוטות לשחזור, ולהזין Blink>Screen>MultiScreen בתיבה רכיבים. גליץ' הוא כלי מעולה לשיתוף גיבויים מהירים וקלים.

הבעת תמיכה ב-API

האם בכוונתך להשתמש ב-Window Management API? התמיכה הציבורית עוזרת לצוות Chrome לקבוע סדרי עדיפות לתכונות, ומראה לספקי דפדפנים אחרים עד כמה חשוב התמיכה בהן.

  • נשמח לדעת איך בכוונתך להשתמש בה בשרשור של WiCG ב-Wi-Fi.
  • שלח ציוץ אל @ChromiumDev באמצעות ה-hashtag #WindowManagement וספר לנו איפה ואיך אתם משתמשים בו.
  • בקשו מספקי דפדפנים אחרים להטמיע את ה-API.

קישורים שימושיים

אישורים

המפרט של window Management API נערך על ידי ויקטור קוסטן, ג'ושוע בל ומייק וואסרמן. ה-API הוטמע על ידי Mike Wasserman ו-Adrienne ווקר. המאמר הזה נכתב על ידי Joe Medley, François Beaufort ו-Kayce Basques. תודה ללורה טורנט פואיג על התמונות.