[v3, macOS] Screen.Bounds.X/Y on non-primary screens are off by the screen's scale factor (mixed-DPI)
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 36.3k
- Forks
- 1.9k
- Avg merge
- 2d 11h
- Merged PRs (30d)
- 33
Description
Description
In pkg/application/screen_darwin.go, cScreenToScreen multiplies screen.x and screen.y by the screen's own backingScaleFactor before assigning them to Bounds.X / Bounds.Y:
sf := float64(screen.scaleFactor)
toPhysical := func(points C.int) int { return int(float64(points) * sf) }
return &Screen{
X: toPhysical(screen.x),
Y: toPhysical(screen.y),
Bounds: Rect{
X: toPhysical(screen.x),
Y: toPhysical(screen.y),
...
},
...
}
The intent (per the code comment) is a round-trip workaround for applyDPIScaling in screenmanager.go, which divides Bounds.Width / Bounds.Height by the scale factor. But Bounds.X / Bounds.Y are not divided again in applyDPIScaling — they're left in points × ownScaleFactor space, while Bounds.Width / Bounds.Height end up in logical points.
After #5304 + the GetScreens normalization, Position() / SetPosition() are in logical points across screens. So on mixed-DPI setups (e.g., a Retina secondary above a 1x primary), SetPosition(s.Bounds.X, s.Bounds.Y) for a non-primary screen lands at the wrong location by a factor of that screen's ScaleFactor.
Steps to Reproduce
- Primary monitor: 1x (non-Retina).
- Secondary monitor: 2x Retina, positioned above the primary in macOS Displays settings.
- Read
secondary.Bounds.YfromGetScreens(). - Call
SetPosition(secondary.Bounds.X, secondary.Bounds.Y). - Window does not land at the top-left of the secondary; it's off by
secondary.ScaleFactor.
The same ScaleFactor doubling exists today but only becomes user-visible once Position/SetPosition are normalized to logical points (which is the goal of #5117 / #5304).
Notes
- The flofreud setup in #5117 (Retina primary + 1x external above) is not affected, because the non-primary screen has
ScaleFactor=1and the multiplication is a no-op. - This is a pre-existing macOS-only
Boundsunit bug — exposed (not introduced) by the GetScreens normalization landing alongside #5304. - The fix is likely: drop the
* sfmultiplication on X/Y incScreenToScreen(keep it forPhysicalBounds.X/Y), and verify nothing inscreenmanager.go(e.g.,move()placement adjustment,getScreenAlignment) regresses on the cross-DPI tests.
Environment
- Wails v3 (
pkg/application/screen_darwin.go,pkg/application/screenmanager.go) - macOS, mixed-DPI multi-monitor
Related
- #5117 — coordinate-system audit
- #5304 — absolute coordinate normalization PR (where this case became user-visible)
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with cScreenToScreen in pkg/application/screen_darwin.go, then inspect applyDPIScaling, move(), and getScreenAlignment in pkg/application/screenmanager.go. Run the cross-DPI tests and verify that Bounds coordinates remain in logical points, PhysicalBounds coordinates retain physical scaling, and SetPosition can round-trip a non-primary screen position.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go, macos
- Domain
- desktop
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 58/100