Skip to content

[V3] MacOS + Linux Screen Management Fixes#5067

Open
atterpac wants to merge 10 commits intowailsapp:v3-alphafrom
atterpac:atterpac/improvements/screen
Open

[V3] MacOS + Linux Screen Management Fixes#5067
atterpac wants to merge 10 commits intowailsapp:v3-alphafrom
atterpac:atterpac/improvements/screen

Conversation

@atterpac
Copy link
Copy Markdown
Member

@atterpac atterpac commented Mar 20, 2026

Description

Fixes initialization of both mac and linux to include processAndCacheScreens to match windows to properly populate app.Screens

Adds SetScreen across all OS's including helpers and runtime bindings to support setting from JS runtime.

Fixes # (issue)
#4000

Type of change

Please select the option that is relevant.

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)

How Has This Been Tested?

  • Windows - modified for windows but untested
  • macOS
  • Linux

Test Configuration

MacOS - Macbook Pro M4 Max

# System

┌──────────────────────────────────────────────────┐
| Name          | MacOS                            |
| Version       | 15.7.3                           |
| ID            | 24G419                           |
| Branding      | Sequoia                          |
| Platform      | darwin                           |
| Architecture  | arm64                            |
| Apple Silicon | true                             |
| CPU           | Apple M4 Max                     |
| CPU 1         | Apple M4 Max                     |
| CPU 2         | Apple M4 Max                     |
| GPU           | 32 cores, Metal Support: Metal 3 |
| Memory        | 36 GB                            |
└──────────────────────────────────────────────────┘

# Build Environment

┌─────────────────────────────────────────────────────────────────────────────────────────────────────┐
| Wails CLI      | v3.0.0-alpha.48                                                                    |
| Go Version     | go1.25.2                                                                           |
| -buildmode     | exe                                                                                |
| -compiler      | gc                                                                                 |
| CGO_CFLAGS     |                                                                                    |
| CGO_CPPFLAGS   |                                                                                    |
| CGO_CXXFLAGS   |                                                                                    |
| CGO_ENABLED    | 1                                                                                  |
| CGO_LDFLAGS    |                                                                                    |
| DefaultGODEBUG | containermaxprocs=0,decoratemappings=0,tlssha1=1,updatemaxprocs=0,x509sha256skid=0 |
| GOARCH         | arm64                                                                              |
| GOARM64        | v8.0                                                                               |
| GOOS           | darwin                                                                             |
└─────────────────────────────────────────────────────────────────────────────────────────────────────┘

Linux - Arch + Wayland

 System

┌────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
| Name                | EndeavourOS                                                                                      |
| Version             | Unknown                                                                                          |
| ID                  | endeavouros                                                                                      |
| Branding            |                                                                                                  |
| Platform            | linux                                                                                            |
| Architecture        | amd64                                                                                            |
| Desktop Environment | Hyprland                                                                                         |
| NVIDIA Driver       | N/A                                                                                              |
| XDG_SESSION_TYPE    | wayland                                                                                          |
| CPU                 | AMD Ryzen 9 5900X 12-Core Processor                                                              |
| GPU 1               | Navi 32 [Radeon RX 7700 XT / 7800 XT] (Advanced Micro Devices, Inc. [AMD/ATI]) - Driver: amdgpu  |
| Memory              | 32GB                                                                                             |
└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

# Build Environment

┌────────────────────────────────────┐
| Wails CLI    | v3.0.0-dev          |
| Go Version   | go1.25.6 X:nodwarf5 |
| -buildmode   | exe                 |
| -compiler    | gc                  |
| CGO_CFLAGS   |                     |
| CGO_CPPFLAGS |                     |
| CGO_CXXFLAGS |                     |
| CGO_ENABLED  | 1                   |
| CGO_LDFLAGS  |                     |
| GOAMD64      | v1                  |
| GOARCH       | amd64               |
| GOEXPERIMENT | nodwarf5            |
| GOOS         | linux               |
└────────────────────────────────────┘

Summary by CodeRabbit

  • New Features

    • Screen lookup by display ID or index; programmatic screen selection for windows.
    • Window API supports assigning a target screen and will center/place windows relative to that screen.
  • Bug Fixes

    • Fixed screen population/detection at startup on macOS and Linux.
  • Improvements

    • Window placement logic refined across platforms to respect selected screen and work area.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 20, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 97a47f15-e556-4e61-927b-b840c5698bfd

📥 Commits

Reviewing files that changed from the base of the PR and between 0a288e6 and 81d87b8.

📒 Files selected for processing (12)
  • v3/pkg/application/application_server.go
  • v3/pkg/application/linux_cgo.go
  • v3/pkg/application/linux_purego.go
  • v3/pkg/application/screen_darwin.go
  • v3/pkg/application/screenmanager.go
  • v3/pkg/application/webview_window.go
  • v3/pkg/application/webview_window_android.go
  • v3/pkg/application/webview_window_darwin.go
  • v3/pkg/application/webview_window_ios.go
  • v3/pkg/application/webview_window_linux.go
  • v3/pkg/application/webview_window_windows.go
  • website/src/pages/changelog.mdx
✅ Files skipped from review due to trivial changes (1)
  • website/src/pages/changelog.mdx
🚧 Files skipped from review as they are similar to previous changes (6)
  • v3/pkg/application/webview_window_linux.go
  • v3/pkg/application/webview_window.go
  • v3/pkg/application/linux_cgo.go
  • v3/pkg/application/linux_purego.go
  • v3/pkg/application/screen_darwin.go
  • v3/pkg/application/screenmanager.go

Walkthrough

Adds Screen lookup APIs (GetByID, GetByIndex) and a Window.SetScreen method; implements screen caching/processing at app startup for macOS/Linux; extends message processors and runtime bindings; and updates platform-specific window placement to support screen-relative positioning.

Changes

Cohort / File(s) Summary
Desktop runtime APIs
v3/internal/runtime/desktop/@wailsio/runtime/src/screens.ts, v3/internal/runtime/desktop/@wailsio/runtime/src/window.ts
Added GetByID(id) and GetByIndex(index) (method ids 3 & 4) and SetScreen(screenID) (method id 52) to runtime client surface.
App startup / event wiring
v3/pkg/application/application_darwin.go, v3/pkg/application/application_linux.go, v3/pkg/application/application_linux_gtk4.go
Call processAndCacheScreens() during application startup/activation and on screen-parameter changes; route errors to centralized handler.
Platform screen enumeration & scaling (Linux)
v3/pkg/application/linux_cgo.go, v3/pkg/application/linux_purego.go
Retrieve monitor workarea and scale factor, compute PhysicalBounds/PhysicalWorkArea, update primary monitor detection and returned Screen fields.
Platform screen caching (macOS/Linux)
v3/pkg/application/screen_darwin.go, v3/pkg/application/screen_linux.go
Introduce processAndCacheScreens() and switch getScreens/getPrimaryScreen to read cached values populated via LayoutScreens.
Screen manager & API wrappers
v3/pkg/application/screenmanager.go
Added GetByID, GetByIndex on ScreenManager plus application-level GetScreenByID, GetScreenByIndex; improved LayoutScreens rollback on error.
Message processors
v3/pkg/application/messageprocessor_screens.go, v3/pkg/application/messageprocessor_window.go
Added message IDs/handlers for ScreensGetByID (3), ScreensGetByIndex (4) and WindowSetScreen (52); validate args and resolve screens before invoking window methods.
Window interface & options
v3/pkg/application/window.go, v3/pkg/application/webview_window_options.go
Added Window.SetScreen(screen *Screen) Window and new Screen *Screen option on WebviewWindowOptions.
Window implementations & placement
v3/pkg/application/webview_window.go, v3/pkg/application/webview_window_darwin.go, v3/pkg/application/webview_window_linux.go, v3/pkg/application/webview_window_windows.go, v3/pkg/application/application_server.go, v3/pkg/application/webview_window_android.go, v3/pkg/application/webview_window_ios.go
Added centerOnScreen / applyScreenPlacement logic across platforms; implement no-op variants for server/Android/iOS; apply screen-relative initial placement and deferred positioning when window not yet created.
Changelog
website/src/pages/changelog.mdx
Documented SetScreen API and screen population fixes in Unreleased changelog.

Sequence Diagram

sequenceDiagram
    participant Client as Client (Desktop Runtime)
    participant Handler as Runtime Handler
    participant ScreenMgr as ScreenManager
    participant Window as WebviewWindow
    participant Platform as Platform Impl

    Client->>Handler: SetScreen(screenID)
    activate Handler
    Handler->>ScreenMgr: GetByID(screenID)
    activate ScreenMgr
    ScreenMgr-->>Handler: Screen object
    deactivate ScreenMgr

    Handler->>Window: SetScreen(screen)
    activate Window
    Window->>Window: store options.Screen

    alt window initialized (impl != nil)
        Window->>Platform: centerOnScreen / setPosition (sync)
        activate Platform
        Platform->>Platform: compute pos from screen.WorkArea
        Platform-->>Window: applied position
        deactivate Platform
    else window not initialized
        Window-->>Window: deferred placement
    end

    Window-->>Handler: OK
    deactivate Window
    Handler-->>Client: Success
    deactivate Handler
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

runtime, MacOS, Linux, go, v3-alpha, Enhancement, bindings, Implemented in v3, Documentation, size:XL, lgtm

Suggested reviewers

  • leaanthony

Poem

🐰
I hop from screen to screen with glee,
GetByID, GetByIndex — bring them to me,
SetScreen nudges windows to their place,
Across macOS, Linux, Windows we race,
A cozy cache, a centering embrace!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 38.46% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title '[V3] MacOS + Linux Screen Management Fixes' accurately reflects the primary changes: fixing screen initialization on macOS and Linux, though it only partially conveys the addition of SetScreen functionality across all platforms.
Description check ✅ Passed The PR description covers key requirements: references issue #4000, selects both bug fix and new feature types, provides test configuration with specifics for macOS and Linux, and includes changelog update confirmation.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 OpenGrep (1.16.4)
v3/pkg/application/application_server.go

┌──────────────┐
│ Opengrep CLI │
└──────────────┘

�[32m✔�[39m �[1mOpengrep OSS�[0m
�[32m✔�[39m Basic security coverage for first-party code vulnerabilities.

�[1m Loading rules from local config...�[0m

v3/pkg/application/linux_cgo.go

┌──────────────┐
│ Opengrep CLI │
└──────────────┘

�[32m✔�[39m �[1mOpengrep OSS�[0m
�[32m✔�[39m Basic security coverage for first-party code vulnerabilities.

�[1m Loading rules from local config...�[0m

v3/pkg/application/linux_purego.go

┌──────────────┐
│ Opengrep CLI │
└──────────────┘

�[32m✔�[39m �[1mOpengrep OSS�[0m
�[32m✔�[39m Basic security coverage for first-party code vulnerabilities.

�[1m Loading rules from local config...�[0m

  • 8 others

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@atterpac atterpac marked this pull request as ready for review March 20, 2026 14:54
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
v3/pkg/application/linux_purego.go (1)

771-775: ⚠️ Potential issue | 🔴 Critical

Duplicate function definition will cause compilation error.

The function windowGetPosition is defined twice in this file: once at lines 771-775 and again at lines 821-826. This will cause a Go compilation error.

🐛 Proposed fix - remove the duplicate definition

Remove the first definition at lines 771-775. The second definition at lines 821-826 (which includes a TODO comment) should be kept:

-func windowGetPosition(window pointer) (int, int) {
-	var x, y int
-	gtkWindowGetPosition(window, &x, &y)
-	return x, y
-}
-
 func windowGetCurrentMonitor(window pointer) pointer {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@v3/pkg/application/linux_purego.go` around lines 771 - 775, There are two
definitions of windowGetPosition which causes a compile error; remove the
earlier duplicate definition that calls gtkWindowGetPosition (the one at the
first occurrence) and keep the later definition (the one with the TODO) so only
the single intended windowGetPosition implementation remains.
🧹 Nitpick comments (1)
v3/pkg/application/webview_window_linux.go (1)

342-350: Extract the screen-aware placement math into one helper.

The Screen != nil branch is duplicated before and after show(). Keeping that coordinate calculation in one place will make the next Linux placement fix much less likely to land in only one path.

Also applies to: 398-406

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@v3/pkg/application/webview_window_linux.go` around lines 342 - 350, Extract
the duplicated screen-aware placement logic into a single helper (e.g., a method
on webviewWindow like computeAndSetPosition or getCenteredPosition) that reads
w.parent.options.Screen.WorkArea,
w.parent.options.InitialPosition/WindowCentered, w.parent.options.X/Y and
w.size(), computes the final X/Y, and calls w.setPosition; then replace both
duplicated blocks (the branch before show and the later branch) to call this
helper so the center vs offset math is implemented only once (ensure helper
handles nil Screen by falling back to using options.X/Y).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@v3/pkg/application/linux_cgo.go`:
- Around line 1511-1564: getPrimaryScreen currently hardcodes ID "0", breaking
the ID contract used by getScreenByIndex; instead determine the actual monitor
index and use that as the ID. In getPrimaryScreen(), call
C.gdk_display_get_n_monitors(display) and loop i from 0..n-1 calling
C.gdk_display_get_monitor(display, C.gint(i)) to find the monitor pointer that
equals the primary monitor variable, then set ID = strconv.Itoa(i) (or
C.GoString after formatting) before returning; reference getPrimaryScreen and
getScreenByIndex so the returned ID matches the index-based contract.

In `@v3/pkg/application/screen_darwin.go`:
- Around line 179-185: The getters macosApp.getPrimaryScreen and
macosApp.getScreens currently return the cached m.parent.Screen.primaryScreen /
m.parent.Screen.screens directly which can be nil/empty before
ApplicationDidFinishLaunching populates them; change both getters to lazily
hydrate the cache: if primaryScreen is nil or screens length is 0, call a helper
like Screen.ensureHydrated (create it if missing) that queries the system
display APIs (e.g. NSScreen/CGDisplay) and populates
m.parent.Screen.primaryScreen and m.parent.Screen.screens, then return the
populated values so app.Screen.GetPrimary/GetAll and WebviewWindowOptions.Screen
work during early setup.

In `@v3/pkg/application/screen_linux.go`:
- Around line 9-35: processAndCacheScreens() is publishing a
partially-initialized cache via a.parent.Screen.LayoutScreens(screens) while
calculateScreensDipCoordinates can still be running, causing
getPrimaryScreen()/getScreens() to observe nil/partial m.screens; compute and
fully initialize the new screens (including running
calculateScreensDipCoordinates) before calling LayoutScreens, and change
LayoutScreens (or its caller contract) to perform an atomic swap of m.screens
only on success (or return an error on failure) so the cache is never replaced
with a partial value; update references to processAndCacheScreens,
LayoutScreens, calculateScreensDipCoordinates, getPrimaryScreen, getScreens and
m.screens accordingly.

In `@v3/pkg/application/webview_window_darwin.go`:
- Around line 1371-1379: The placement code is using options.Screen.WorkArea
directly (when options.Screen != nil) then calling w.setPosition, but on macOS
that WorkArea is in the target NSScreen coordinate space and must be converted
to the window's coordinate space using the darwin-specific helper that
windowSetPosition uses; update the branch that handles options.Screen (and the
WindowCentered branch) to compute the position via the macOS screen-placement
helper (the same conversion windowSetPosition performs) before calling
w.setPosition so scale and Y-flip are calculated from the target screen
(referencing options.Screen.WorkArea, w.setPosition, windowSetPosition and
w.parent.options.InitialPosition).

In `@v3/pkg/application/webview_window.go`:
- Around line 953-971: SetScreen currently performs centering math in the shared
WebviewWindow layer using Screen.WorkArea coordinates and then calls
w.impl.setPosition(x, y); move this platform-specific coordinate translation out
of shared code: remove the centering math from WebviewWindow.SetScreen and
instead call a platform-specific method (e.g., impl.centerOnScreen or
impl.SetScreenWithWorkArea) that accepts the Screen or its WorkArea so each impl
(macOS, Windows, Linux) can convert to its native coordinate system; keep the
nil/impl checks and InvokeSync wrapper in SetScreen but delegate positioning to
the new/updated impl method (referencing WebviewWindow.SetScreen, w.impl,
Screen.WorkArea, and impl.setPosition) so macOS implementation can handle its
coordinate space correctly.

In `@website/src/pages/changelog.mdx`:
- Around line 18-21: The changelog uses incorrect capitalization "MacOS"; update
both occurrences in the diff: change "SetScreen api for MacOS, Windows, Linux"
to "SetScreen api for macOS, Windows, Linux" and change "app.Screen population
on startup in Linux and MacOS" to "app.Screen population on startup in Linux and
macOS" (references: the "SetScreen" API mention and the "app.Screen" startup
fix).

---

Outside diff comments:
In `@v3/pkg/application/linux_purego.go`:
- Around line 771-775: There are two definitions of windowGetPosition which
causes a compile error; remove the earlier duplicate definition that calls
gtkWindowGetPosition (the one at the first occurrence) and keep the later
definition (the one with the TODO) so only the single intended windowGetPosition
implementation remains.

---

Nitpick comments:
In `@v3/pkg/application/webview_window_linux.go`:
- Around line 342-350: Extract the duplicated screen-aware placement logic into
a single helper (e.g., a method on webviewWindow like computeAndSetPosition or
getCenteredPosition) that reads w.parent.options.Screen.WorkArea,
w.parent.options.InitialPosition/WindowCentered, w.parent.options.X/Y and
w.size(), computes the final X/Y, and calls w.setPosition; then replace both
duplicated blocks (the branch before show and the later branch) to call this
helper so the center vs offset math is implemented only once (ensure helper
handles nil Screen by falling back to using options.X/Y).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0b32b7aa-2df4-4eda-8fde-3b5e1a42d764

📥 Commits

Reviewing files that changed from the base of the PR and between bb4fbf9 and 0a288e6.

📒 Files selected for processing (19)
  • v3/internal/runtime/desktop/@wailsio/runtime/src/screens.ts
  • v3/internal/runtime/desktop/@wailsio/runtime/src/window.ts
  • v3/pkg/application/application_darwin.go
  • v3/pkg/application/application_linux.go
  • v3/pkg/application/application_linux_gtk4.go
  • v3/pkg/application/linux_cgo.go
  • v3/pkg/application/linux_purego.go
  • v3/pkg/application/messageprocessor_screens.go
  • v3/pkg/application/messageprocessor_window.go
  • v3/pkg/application/screen_darwin.go
  • v3/pkg/application/screen_linux.go
  • v3/pkg/application/screenmanager.go
  • v3/pkg/application/webview_window.go
  • v3/pkg/application/webview_window_darwin.go
  • v3/pkg/application/webview_window_linux.go
  • v3/pkg/application/webview_window_options.go
  • v3/pkg/application/webview_window_windows.go
  • v3/pkg/application/window.go
  • website/src/pages/changelog.mdx

adds centerOnScreen helper to avoid placement logic in the shared layer
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant