godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
88.94k stars 20.17k forks source link

Window resized on secondary monitor resets to default window size if moved to main display #95987

Open lostminds opened 3 weeks ago

lostminds commented 3 weeks ago

Tested versions

Reproducible on 4.3.stable, and as far back as I can remember (at least 4.2) so it's not a recent regression I think.

System information

macOS 14.6.1

Issue description

If you move a window to a secondary display by setting the window.position to a position on that display and then changing the window.size this works as expected. However, if you then move this window back to the main display the window resized back to the project default window size (set small in the example to see the effect).

https://github.com/user-attachments/assets/dc80d385-5ac8-48e9-b05f-9dcc57a28e0d

If you do the resize on the main monitor you can move the window back and forth to the secondary monitor with no issue. Same if you manually resize the window on the secondary display before moving it to the main display.

Steps to reproduce

All that is required is to position and resize a window like so:

func _ready() -> void:
    get_window().position = Vector2(200,200) #position on secondary display
    get_window().size = Vector2(1000,1000) #Something different from project default window size

And the drag the window back to the main monitor.

However, if you set the size first and then move the window, the issue does not occur. So it's as if setting the window size when it's on a secondary monitor it puts it in some invalid size state that is revealed when ending a move of the window to a different display?

Minimal reproduction project (MRP)

As you can see in the video above only a couple of lines of code are required, apart from having two monitors to test with. I've seen this on macOS only, since it's the only multi-monitor system I have available to test on, so I'm not sure if it's macOS specific or not.

In my case both monitors are hiDPI monitors, but with the same display scale, so it shouldn't be an issue related to trying to switch between monitors with different dpi (which is often a cause for issues like this).

bruvzg commented 2 weeks ago

Seems like it's macOS bug (and likely new one, I do not remember anything like this in older versions), auto resize is triggered for windows resized from code (but not manually resized) by the move OS move event handler.

....
0x000000019c8ef120 -[NSWindow _setFrameCommon:display:fromServer:] + 2708
0x000000019d35ece8 -[NSWindow(NSScreenLayout) _setFrame:fromAdjustmentToScreen:anchorIfNeeded:animate:] + 1572
0x000000019d35e6b8 -[NSWindow(NSScreenLayout) _setFrame:fromAdjustmentToScreen:anchor:animate:] + 32
0x000000019c9d8174 -[NSWindow _setFrameAfterMove:] + 1088
0x000000019d367ab4 -[NSWindow _endWindowMoveWithEvent:] + 60
....

We probably can override _setFrame:fromAdjustmentToScreen private method in our NSWindow subclass to prevent it.

lostminds commented 2 weeks ago

We probably can override _setFrame:fromAdjustmentToScreen private method in our NSWindow subclass to prevent it.

Yes, however, this might also disable adjustments we do want in case of moving between monitors with different resolution scaling (which I guess is the intention of this), or (maybe?) resizing down windows that are too large to fit on the new monitor. If it is indeed a macOS bug (I've seen it in Godot at least since macOS 14.5) I guess we could also report it to them and hope it'll be fixed as it's kind of specific and not likely to happen that often in Godot. I've done some searching but haven't found any other mentions of it though. But since other window resizing issues related to multiple-monitor setups are so common it's a hard issue to search for.

The main use case where you'd encounter this (and how I discovered it) is I guess restoring a window size and position at startup based on saved info from last session. And if you then first move the window and then resize it on the secondary monitor you run into this issue. But if you resize is first and move it second you can avoid it, so there's sort of a workaround for it if you know about it.

bruvzg commented 2 weeks ago

however, this might also disable adjustments we do want in case of moving between monitors with different resolution scaling (which I guess is the intention of this),

Seems to have no effect on moving between different DPI screens.

or (maybe?) resizing down windows that are too large to fit on the new monitor.

It is disabling this, but I'm not sure if OS fitting window to screen is desired behavior (it's adjusting only the size and only when moving manually, so mostly useless).

I've done some searching but haven't found any other mentions of it though

The only related thing I have found is this https://github.com/iPlug2/iPlug2/commit/dfce8c6344b32fa8789926594e46a63ce31ef99b (overriding _setFrame for exactly same reason).

lostminds commented 2 weeks ago

It is disabling this, but I'm not sure if OS fitting window to screen is desired behavior (it's adjusting only the size and only when moving manually, so mostly useless).

I think it's supposed to ensure that parts of the window aren't stuck outside the monitor so you can't reach the edges and resize it down. Which could be a nice service to the user, but perhaps less valuable than avoiding this weird resize issue considering you can resize the windows from the other edges and move it.

For cases like this perhaps a Window.screen_changed signal could be a useful addition to allow users to get a notification when the window has moved to a new monitor so they have a chance then to deal with resizing/scaling issues like this themselves.