electron: Per monitor DPI awareness causes issues with window positioning and sizing
As per https://github.com/electron/electron/issues/9560 where previous issues regarding this were collapsed into.
If you have one monitor with non-100% scaling and one monitor with 100% scaling both setSize and setPosition do not act as expected (they always set incorrectly by a factor exactly equal to the scale factor of the screen)
A proposed workaround is #9708 but that simply reverts the addition of the feature instead of fixing the underlying cause.
All future issues regarding DPI scaling and window positioning / sizing should be merged into this one.
About this issue
- Original URL
- State: open
- Created 7 years ago
- Reactions: 28
- Comments: 45 (15 by maintainers)
Commits related to this issue
- electron 2.x - remove workaround for https://github.com/electron/electron/issues/10862 — committed to microsoft/vscode by bpasero 6 years ago
- GPII-2890: Various fixes for the DPI issue that comes with Electron v1.8.1 There is a severe, still unresolved issue with window positioning and dimensions that comes with the new versions of Electro... — committed to danailbd/gpii-app by georgitodorov 6 years ago
- Update to Electron v2.0.0-beta.6 (#46552) * electron@2.0.0-beta-1 * Update distro * Update electron.d.ts to 2.0.0-beta.1 * Disable asar as it causes a native crash * Adopt Module._resolve... — committed to microsoft/vscode by Tyriar 6 years ago
- Adds workaround for Linux setSize bug https://github.com/electron/electron/issues/10862 — committed to hovancik/stretchly by hovancik 4 years ago
- Adds workaround for Linux setSize bug https://github.com/electron/electron/issues/10862 — committed to hovancik/stretchly by hovancik 4 years ago
- Update to Electron v2.0.0-beta.6 (#46552) * electron@2.0.0-beta-1 * Update distro * Update electron.d.ts to 2.0.0-beta.1 * Disable asar as it causes a native crash * Adopt Module._resolve... — committed to code-oss-dev/code by Tyriar 6 years ago
- Add helpers from goblin-vm to WindowManager class Fix a case where we create a window on first display and move it to a second display with different scale factor. Electron isn't handling properly s... — committed to Xcraft-Inc/xcraft-core-host by skynightz a year ago
- Move helpers in WindowManager class and rework initialization of window state Fix a case where we create a window on first display and move it to a second display with different scale factor. Electr... — committed to Xcraft-Inc/goblin-wm by skynightz a year ago
- Electron has a long standing bug where it does not properly restore the window size on multi monitor setups with different scaling factors: https://github.com/electron/electron/issues/10862 We work ar... — committed to ltogniolli/exile-diary-reborn by ltogniolli 6 months ago
This seems like a very basic issue that is likely to affect every single electron application on windows. I hope this will eventually get prioritized.
Recently I encounter a bug may be caused by this issue. I’m working on a feature that implements customize window drag without using
-webkit-app-regionin my app. I add listeners to handle the mouse down/move/up event, so that I can change the position of the window usingsetPositionin event handlers.This is what I’ve done: https://gist.github.com/4ab3274ed4455e184cdfaf8820b6c6b3
The code runs fine on the screen whose DPI is 100% / 200%, but on the screen whose DPI is 125% / 150% / 175%, when I drag the window, the window‘s region expand UNEXPECTEDLY and continuously - the size of the window shouldn’t change during dragging.
https://user-images.githubusercontent.com/26410985/198053643-1c9a2135-ac9a-4b06-bcf3-02d127006b1d.mp4
I find that the
mousemoveevent sometimes may be triggered immediately after themousedownevent being triggered even if I don’t move my mouse aftet mouse down.Emmmmm, so there is any way to prevent this ?
Electron 18.3.15 (Provide by Electon Fiddle), Window 10 (1909)
As a general tip: I’m currently fighting my way through this and have learned (so far) that calling
setPositionon Windows 10 seems to do the right thing, even if callingnew BrowserWindow({ x, y })didn’t result in the correct positioning.I have tracked down the error I described to a bug in Chromium. There are a few things going on here which affect the outcome. First you must have an internal display (e.g. a laptop screen) which is not the primary display and at least two external displays. There is on other caveat related to the ‘id’ of the display.
When Chromium first initializes the displays it makes the calculations correctly, however before the Electron app is ready it (or somewhere in Chromium) requests a refresh of the display layout (which also happens when a monitor is added/removed). It’s this second refresh that causes the problem because it thinks that the new layout is not valid.
I have a fix so I’ll look into how to commit it to the Chromium source code.
Again, note that this is the cause of only one of the behaviours raised in this thread - other issues will NOT be fixed by this.
Update: The fix for the issue I highlighted has now hit the Chromium source code: https://chromium-review.googlesource.com/c/chromium/src/+/4673217
I was working the other day with electron and also needed to place newly created window on some specific screen, and also encountered that position was sometimes off, if user has monitors with different scaleFactor-s. However, I discovered that first teleporting window to 0,0 of needed monitor and THEN placing it correctly removed all inconsistency!
Seems like setPosition can sometimes use scaleFactor of another display, and placing it firstly on correct display fixes that.
I am yet to discover flaws of this method, might be not a panacea though.
Please see the update in the memo below (not this one)- After testing further monitor configurations, it seems getAllDisplays() is providing unreliable and inconsistent information in different windows setups.
I believe many of these issues come from the information supplied getAllDisplays() (apparently a chromium function). Although the information is ‘correct’, electron appears to take the bounds values at face value (rather than appling the scaleFactor) when performing numerous functions related to sizing and position. I have a laptop which is scaled at 250% and a monitor at 100%, to the left of the laptop screen - the 100% scale monitor is the primary display (this is important so that x and y of the bounds are 0). getAllDisplays returns bounds and scale factor as follows: Laptop: { x: 768, y: 0, width: 1536, height: 864}, scaleFactor 2.5 Monitor: { x: 0, y: 0, width: 1920, height: 1080 }, scaleFactor 1
Note that the laptop screens location and dimensions returned by the display functions are divided by the scaleFactor, rather than set at their real values. e.g. the x location of the laptop screen would normally (in Windows) be 1920 but is returned as 1920 / 2.5 = 768. Obviously, to get the real values we can multiply the bounds by the scaleFactor to get the original values.
The problem comes when we start using function to find which monitor a point or rectangle is on e.g. screen.getDisplayNearestPoint and screen.getDisplayMatching. These appear to be using the bounds information at face value without applying the scaleFactor so they will return the incorrect monitor. In the example above, if we call screen.getDisplayNearestPoint({x:800, y: 10}) it will return the laptop display NOT the monitor display because it came first in the list and the coordinates overlap. In fact, this point should really be the monitor display. In the core Microsoft Windows display functions these coordinates do not overlap.
Calling BrowserWindow.getPosition or BrowserWindow.getBounds has similar issues. With this monitor configuration there are two completely different locations on screen which return exact the same position/bounds and its impossible to reliably determine which monitor they are on due to the behaviour of the screen.getDisplay… functions (see above)
I suspect this is directly or indirectly leading to many of the scaling issues reported
Just to update, this issue is still occurring on Electron 20
A multi-screen setup with different scale factors still causes issues as of electron@5.0.6 on Windows 10
I used
screen.getAllDisplays()and the results are really wildly inconsistent if scale factors are different.Monitor setup (2) is the main display
First case: both displays have the same scale factor --> everything is okay
Second case: external display has a different scale factor -->
My use case: I’ve implemented desktop sharing via WebRTC and then I’m highlighting the display shared by the user (via a native window) based on
screen.boundsresult. In the second case, if the user chooses to share the second screen this is the result:Any suggestions are really appreciated.
@MarshallOfSound Well, the #10659 problem I raised, although clearly related with DPI, isn’t specific to “per-monitor” DPI, and you don’t even need multiple monitors to reproduce it. So it doesn’t fit this new issue as currently described, and the way to solve both these issues most certainly involves different fixes in the code.
I’m afraid merging all the DPI-related bugs into this one will just make everybody forget about the sub-issues, as they are now closed. If you want to merge all these issues into one, the description of the main issue should probably be made much more general than it currently is.
Just my 2 cents.
OK, so I compiled Chromium using the Spotify automated builds (Chromium Version: 115.0.5790.40) and ran CefDisplay::GetAllDisplays(displays) from within the cefsimple test application. I’ve highlighted the differences in bold. the output from chromium appears correct and the output in electron is not.
For the Setup 3 above it returns the following results:
Compared to the following in Electron 25.2.0 and 26.0.0 beta 1:
So it looks like Electron is doing some sort of manipulation of the GetAllDisplays() data from Chromium and then changing the values incorrectly
@bushmin I’d imagine that just just doing two setPosition or setBounds calls with the same coordinates would also work. Probably no need to move to 0,0 first.
@BertholetDamien thanks for the info - I’ll keep my current synchronous workaround, but will update if I experience problems. It could be that you have a better sample size than me!
@jameshfisher The Chromium issue seems really close to that. I experienced the same issue with a window.open in chrome 2 weeks ago and I was thinking about that electron issue. It can be the same root cause.
Your work around, it’s exactly what I do since months now… It’s really awful but that seem to work for now. I adjust it with some tricky default value out of screen, in some case, that was the only thing that works for me. Here my code, if that can be helpfull.