Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Setting CurrentMonitor switches display to 800x600 #1663

Open
MV10 opened this issue Oct 15, 2023 · 8 comments
Open

Setting CurrentMonitor switches display to 800x600 #1663

MV10 opened this issue Oct 15, 2023 · 8 comments

Comments

@MV10
Copy link
Contributor

MV10 commented Oct 15, 2023

I was investigating how to save and restore the position and assigned monitor for a window. To my surprise, if I assign a monitor by setting CurrentMonitor, the window does move to that other screen -- but it goes into full-screen mode and changes the resolution to 800 x 600. When this happens, WindowState is still Normal rather than Fullscreen. To complicate matters, when the assignment is to my primary monitor, it appears to make my other monitor the primary (all my desktop icons move to the other screen, my taskbars move, etc.).

I'll work on a simple repro and add another comment in a bit. (This is using OpenTK 4.8.0 since I encountered those full-screen issues in 4.8.1 and had to revert.)

Edit: I suspect it has something to do with NativeWindowSettings.Size defaulting to 800x600, but my window was created and still running with an initial size of 960x540 (odd, I know, but chosen as a testing setup because it's still a 16:9 aspect ratio).

@MV10
Copy link
Contributor Author

MV10 commented Oct 15, 2023

Here's a repro and some output.

Expand to view repro code
class Window : GameWindow
{
    static void Main(string[] args)
    {
        GameWindowSettings gwSettings = new GameWindowSettings();

        NativeWindowSettings nwSettings = new NativeWindowSettings()
        {
            Size = new(960, 540),
            Location = new(50, 150),
            Title = "Issue 1663",
        };

        using Window window = new Window(gwSettings, nwSettings);
        window.Run();
    }

    public Window(GameWindowSettings gws, NativeWindowSettings nws)
        : base(gws, nws)
    {
        Console.WriteLine("Startup:");
        Details();
    }

    float time = 0;
    protected override void OnRenderFrame(FrameEventArgs args)
    {
        base.OnRenderFrame(args);
        const float CycleTime = 8.0f;
        time += (float)args.Time;
        if (time > CycleTime) time = 0;
        Color4 color = Color4.FromHsv(new Vector4(time / CycleTime, 1, 1, 1));
        GL.ClearColor(color);
        GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
        SwapBuffers();
    }

    bool showDetails = false;
    protected override void OnUpdateFrame(FrameEventArgs args)
    {
        base.OnUpdateFrame(args);

        if(showDetails)
        {
            Details();
            showDetails = false;
            return;
        }

        var input = KeyboardState;

        if(input.IsKeyReleased(Keys.D1))
        {
            showDetails = true;
            SetMonitor(1);
            return;
        }

        if (input.IsKeyReleased(Keys.D2))
        {
            showDetails = true;
            SetMonitor(2);
            return;
        }

        if (input.IsKeyReleased(Keys.F))
        {
            showDetails = true;
            Console.WriteLine("Setting fullscreen mode:");
            WindowState = WindowState.Fullscreen;
            return;
        }

        if (input.IsKeyReleased(Keys.N))
        {
            showDetails = true;
            Console.WriteLine("Setting normal mode:");
            WindowState = WindowState.Normal;
            return;
        }

        if (input.IsKeyReleased(Keys.Space))
        {
            Console.WriteLine("Query status:");
            Details();
        }

        if (input.IsKeyReleased(Keys.Escape)) Close();
    }

    private void Details()
    {
        var monitors = Monitors.GetMonitors();
        var current = Monitors.GetMonitorFromWindow(this);
        var n = 1;
        var curr = 0;
        foreach (var m in monitors)
        {
            if (m.Handle.Pointer == current.Handle.Pointer) curr = n;
            Console.WriteLine($"{n++}: {m.HorizontalResolution} x {m.VerticalResolution}, {m.CurrentVideoMode.RedBits}bpp, {m.CurrentVideoMode.RefreshRate}Hz");
        }
        Console.WriteLine($"Window is on {curr} at position ({Location.X},{Location.Y}), overall size ({Size.X},{Size.Y}), in {WindowState} mode.");
        Console.WriteLine("".PadRight(70, '-'));
    }

    private void SetMonitor(int n)
    {
        Console.WriteLine($"Setting monitor {n}:");
        var monitors = Monitors.GetMonitors();
        CurrentMonitor = monitors[n - 1].Handle;
    }
}

You can type Space to dump the details again, type 1 or 2 to switch between monitors, type F or N to set full-screen or normal states, and Esc to quit. Most of the time I only run a single gigantic 4K screen, but I have a little 1280x720 8" that I consider my emergency / portable screen that I plugged in for testing all of this. Here's the sequence of events for this output:

  • Start the program (starts windowed on screen 1)
  • Hit 2 to move to screen 2 (it's full-screen 800x600, but WindowState is incorrect)
  • Hit space to dump the current state
  • Hit F to go "real" full screen (no visible change but state updates, resize does not fire)
  • Hit N to go back to windowed mode (resolution changes back, window is slightly off-screen)

I could then hit 1 and go through similar steps, but I don't like doing that since it screws up my primary monitor settings and it takes a minute of fiddling around to get everything back to normal. Also I checked this stuff in the resize events. The resize doesn't fire when toggling full/normal (I guess because the dimensions don't change?) but it does fire when the monitor is reassigned. The output would be redundant with what's shown below so I left it out of the repro.

The output:

Startup:
1: 3840 x 2160, 8bpp, 60Hz
2: 1280 x 720, 8bpp, 60Hz
Window is on 1 at position (50,150), overall size (984,587), in Normal mode.
----------------------------------------------------------------------
Setting monitor 2:
1: 3840 x 2160, 8bpp, 60Hz
2: 800 x 600, 8bpp, 60Hz
Window is on 2 at position (3840,1428), overall size (800,600), in Normal mode.
----------------------------------------------------------------------
Query status:
1: 3840 x 2160, 8bpp, 60Hz
2: 800 x 600, 8bpp, 60Hz
Window is on 2 at position (3840,1428), overall size (800,600), in Normal mode.
----------------------------------------------------------------------
Setting fullscreen mode:
1: 3840 x 2160, 8bpp, 60Hz
2: 800 x 600, 8bpp, 60Hz
Window is on 2 at position (3840,1428), overall size (800,600), in Fullscreen mode.
----------------------------------------------------------------------
Setting normal mode:
1: 3840 x 2160, 8bpp, 60Hz
2: 1280 x 720, 8bpp, 60Hz
Window is on 2 at position (3828,1393), overall size (824,647), in Normal mode.
----------------------------------------------------------------------

@MV10
Copy link
Contributor Author

MV10 commented Oct 15, 2023

Hmm, maybe these should be separate issues, but two more multi-monitor problems:

When I drag the window from monitor 1 to monitor 2, then hit a key to toggle to full screen, it goes full-screen on monitor 1. I guess it doesn't recognize that it has changed.

When the window is running full-screen on some other monitor, setting focus to a window on a different screen minimizes the full-screen display.

I assume or guess all of this is related to GLFW having trouble with the Windows "extend" desktop mode where monitors exist in some giant virtual space. Probably also why Location has odd values, rather than values relative to whatever monitor it is on.

@NogginBops NogginBops added this to the 4.8.2 milestone Oct 16, 2023
@NogginBops
Copy link
Member

When the window is running full-screen on some other monitor, setting focus to a window on a different screen minimizes the full-screen display.

This is controlled by GLFW_AUTO_ICONIFY which defaults to true. Not sure if we have any options in OpenTK to turn it on and off though...
https://www.glfw.org/docs/3.3/window_guide.html#GLFW_AUTO_ICONIFY_attrib

@NogginBops
Copy link
Member

I assume or guess all of this is related to GLFW having trouble with the Windows "extend" desktop mode where monitors exist in some giant virtual space. Probably also why Location has odd values, rather than values relative to whatever monitor it is on.

Window location is reported in the virtual screen space, ie. relative to the primary monitor. This is intentional.

@MV10
Copy link
Contributor Author

MV10 commented Nov 3, 2023

Window location is reported in the virtual screen space, ie. relative to the primary monitor. This is intentional.

Right and it makes sense, my point is that it doesn't detect the monitor change as a result. (In partial overlap scenarios, I'm not sure what logic makes sense and/or exactly how the OSes handle that, I just thought it was unexpected behavior after years and years of habit from working with multiple monitors.)

@MV10
Copy link
Contributor Author

MV10 commented Nov 26, 2023

Now I'm wondering if the resolution switch is some side-effect of the fullscreen/normal bug ... I found some GLFW docs that says in fullscreen mode assigning a monitor will cause a resolution switch to the nearest supported resolution. My window (not in fullscreen) was 960x540 and I suppose it's arguable that 800x600 would be the nearest.

If I have time this week, I'll pull down PR 1650 that fixes the fullscreen/normal bug and see if that makes a difference. Perhaps the bug here is that monitor assignment in windowed mode is somehow being treated as fullscreen...

@NogginBops
Copy link
Member

Moving this to 4.8.3.

@NogginBops NogginBops modified the milestones: 4.8.2, 4.8.3 Dec 2, 2023
@MV10
Copy link
Contributor Author

MV10 commented Jan 17, 2024

I just remembered that I wanted to re-test this -- same behavior with 4.8.2. In the test below, I started in windowed mode on screen 1, pressed 3 to move that window to screen 3 and what it did was put monitor 3 into 800x600 and showed the content full-screen, while still indicating WindowState.Normal.

1-3:  Set monitor N
  F:  Set WindowState.Fullscreen
  D:  Set WindowState.Normal
----------------------------------------------------------------------
Startup:
1: 3840 x 2160, 8bpp, 60Hz
2: 1280 x 720, 8bpp, 60Hz
3: 3840 x 2160, 8bpp, 60Hz
Window is on 1 at position (50,150), overall size (984,587), in Normal mode.
----------------------------------------------------------------------
Setting monitor 3:
1: 3840 x 2160, 8bpp, 60Hz
2: 1280 x 720, 8bpp, 60Hz
3: 800 x 600, 8bpp, 60Hz
Window is on 3 at position (-800,0), overall size (800,600), in Normal mode.
----------------------------------------------------------------------

@NogginBops NogginBops modified the milestones: 4.8.3, 4.9.0 Mar 5, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants