DPI scaling, display coordinates and window bounds

If you position windows programmatically and your users run mixed-DPI or high-DPI monitors, windows can end up in the wrong place or wrong size, because of mixing two different coordinate systems.

The three coordinate systems

The Displays API documents three coordinate systems, used as the type and targetType values when converting bounds:

Value Description
"electron" Electron DIP pixels.
"logical" Location scaled by the primary display scale, size scaled by the target display scale.
"physical" Physical screen pixels.

"logical’ can be a bit confusing, so here’s an example. If the primary display is at 125% and the secondary display is at 150%, then for a window on the secondary display:

  • left = physicalLeft / 1.25
  • top = physicalTop / 1.25
  • width = physicalWidth / 1.5
  • height = physicalHeight / 1.5

If you assume a single scale factor applies to all four values, your window will be placed or sized incorrectly on any setup where the primary and target displays differ.

Logical vs physical

Take a 1920×1080 monitor at 125% Windows scaling:

  • Physical pixels: 1920×1080 — the actual hardware resolution.
  • Logical pixels: 1536×864 — 1920/1.25 and 1080/1.25. This is what apps see.

This gives you a simple way to identify which coordinate space an unknown value is in — a width you computed yourself, a value from a third-party component, or a DOM measurement. Log it and compare it against the actual screen resolution:

  • If a width reports 1536, it is logical.
  • If it reports 1920, it is physical.

The same check works for a position. If you place an element near the right edge of that screen, roughly 1500 means logical and roughly 1900 means physical.

Diagnosing a positioning bug

When window positioning is wrong on a scaled display, establish the coordinate space of every input:

  1. Log every value that feeds the calculation: the work area, your target coordinates, your width/height constants, any offsets, and the output of getBounds() on a scaled display (for example, 125% or 150%).
  2. Compare each against the actual screen resolution to determine whether it is logical or physical, using the check above.
  3. Normalise everything into one coordinate space before doing any math.
  4. If a value is in a different space, convert it up front, not inline in the middle of the calculation.

A note on io.Connect Browser

If you are positioning windows in io.Connect Browser, the constraints are different:

  • io.windows.open() is a wrapper around the native browser window.open() API. The coordinates and dimensions you pass are forwarded directly to window.open() without modification.
  • The browser does not guarantee the opened window will match those values. In testing, browsers treated the requested bounds as hints and silently adjusted them, so the final top, left, width and height can differ from what was requested. Even when opening at top=0, left=0, Chrome and Firefox applied an invisible offset - the window is never placed at the exact origin of the screen.