Skip to content

[v3, macOS] SetPosition() cannot move windows across screens — Y conversion uses current screen instead of target #5117

@flofreud

Description

@flofreud

Description

SetPosition() on macOS cannot reliably move a window from one screen to another. The Y-coordinate conversion in windowSetPosition uses [window screen] (the screen the window is currently on) rather than determining which screen the target coordinates belong to.

This means if a window is on Screen A and you call SetPosition with coordinates intended for Screen B, the Y-axis conversion uses Screen A's height, placing the window at the wrong location.

Root Cause

In pkg/application/webview_window_darwin.go (lines 627-641):

void windowSetPosition(void* nsWindow, int x, int y) {
    WebviewWindow* window = (WebviewWindow*)nsWindow;
    NSScreen* screen = [window screen];  // ← always the CURRENT screen
    if (screen == NULL) {
        screen = [NSScreen mainScreen];
    }
    CGFloat scale = [screen backingScaleFactor];
    NSRect frame = [window frame];
    frame.origin.x = x / scale;
    frame.origin.y = (screen.frame.size.height - frame.size.height) - (y / scale);
    //                 ^^^^^^^^^^^^^^^^^^^^^^^ wrong screen height for cross-screen moves
    [window setFrame:frame display:YES];
}

The same issue exists in windowGetPosition (lines 612-625) — both use [window screen] for conversion, so coordinates are always relative to the current screen rather than absolute.

Steps to Reproduce

  1. Connect an external monitor above or below the built-in display
  2. Create a window on the primary screen
  3. Call SetPosition(x, y) with coordinates that should place the window on the external monitor
  4. Window appears at the wrong position (still on the primary screen, or offset)

Expected Behavior

SetPosition should move the window to the correct absolute position regardless of which screen it's currently on.

Suggested Fix

windowSetPosition should determine which screen the target (x, y) belongs to, rather than using [window screen]. For example:

void windowSetPosition(void* nsWindow, int x, int y) {
    WebviewWindow* window = (WebviewWindow*)nsWindow;
    NSScreen* screen = [window screen];
    if (screen == NULL) {
        screen = [NSScreen mainScreen];
    }
    CGFloat scale = [screen backingScaleFactor];

    // Convert target point to NSScreen coordinates to find the target screen
    CGFloat targetX = x / scale;
    CGFloat targetY = (screen.frame.size.height - 1) - (y / scale); // approximate
    NSPoint target = NSMakePoint(targetX, targetY);

    // Find which screen the target point belongs to
    for (NSScreen *s in [NSScreen screens]) {
        if (NSMouseInRect(target, [s frame], NO)) {
            screen = s;
            break;
        }
    }

    NSRect frame = [window frame];
    frame.origin.x = x / scale;
    frame.origin.y = (screen.frame.size.height - frame.size.height) - (y / scale);
    [window setFrame:frame display:YES];
}

(This is a rough sketch — the exact conversion depends on how Wails defines its coordinate system for multi-screen setups.)

Workaround

I bypassed Wails' SetPosition entirely by using NativeWindow() to get the NSWindow* pointer and calling [window setFrameOrigin:] directly with native macOS coordinates:

// Go side
nw := w.NativeWindow()
winW, winH := w.Size()
centerWindowOnActiveScreen(nw, winW, winH)
// Objective-C via cgo
int centerWindowOnMouseScreen(void* nsWindow, int winWidth, int winHeight) {
    NSPoint mouse = [NSEvent mouseLocation];
    for (NSScreen *s in [NSScreen screens]) {
        if (NSMouseInRect(mouse, [s frame], NO)) {
            NSRect wa = [s visibleFrame];
            CGFloat x = wa.origin.x + (wa.size.width - winWidth) / 2.0;
            CGFloat y = wa.origin.y + (wa.size.height - winHeight) / 2.0;
            dispatch_async(dispatch_get_main_queue(), ^{
                [(NSWindow *)nsWindow setFrameOrigin:NSMakePoint(x, y)];
            });
            return 1;
        }
    }
    return 0;
}

Environment

  • Wails v3.0.0-alpha.74
  • macOS 26.4 (arm64)
  • Go 1.25.4
  • Setup: MacBook built-in display + external monitor stacked vertically

Related Issues

Metadata

Metadata

Assignees

No one assigned

    Labels

    BugSomething isn't workingDocumentationImprovements or additions to documentationMacOSinvestigatingThe bug is being investigatedv3

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions