microsoft / malmo

Project Malmo is a platform for Artificial Intelligence experimentation and research built on top of Minecraft. We aim to inspire a new generation of research into challenging new problems presented by this unique environment. --- For installation instructions, scroll down to *Getting Started* below, or visit the project page for more information:
https://www.microsoft.com/en-us/research/project/project-malmo/
MIT License
4.07k stars 600 forks source link

Mac only: Mission window glides across screen when moved #590

Open Brigadirk opened 7 years ago

Brigadirk commented 7 years ago

When I start up the Minecraft server, I'm able to move and adjust the screen as I please.

However, after starting a mission, any attempt to move the Minecraft window will send it spiralling out of control across the screen, often sliding out of vision, necessitating a restart of the server.

Additionally, adding a videoproducer to the XML centers the Minecraft window. Combined with the screen playing ice hockey with the Minecraft window, this can make it very annoying to run tests on a single laptop screen.

According to David Bignell, this only happens on Mac, hence the title.

DaveyBiggers commented 7 years ago

Yes, after some digging it seems it's due to to the following code in EntityRenderer:

        if (flag && Minecraft.IS_RUNNING_ON_MAC && this.mc.inGameHasFocus && !Mouse.isInsideWindow())
        {
            Mouse.setGrabbed(false);
            Mouse.setCursorPosition(Display.getWidth() / 2, Display.getHeight() / 2 - 20);
            Mouse.setGrabbed(true);
        }

This is also the reason why the OSX Minecraft window is so reluctant to relinquish the mouse capture (Malmo contains code to ensure Minecraft doesn't keep hold of the mouse, which works fine on Windows and Linux). Any time the window gains focus and the mouse is outside of the window, the mouse is automatically grabbed, and moved to within the window... Unfortunately, if you are dragging the window, the window gets moved with the mouse (leaving the mouse outside the window client area again, thus re-triggering the move, shifting the window again, etc.etc.)

DaveyBiggers commented 7 years ago

As for the automatic centring of the window after resize... In theory this should be an easy fix - just grab the x/y coords of the window before resizing, and reset them afterwards:

            int oldX = Display.getX();
            int oldY = Display.getY();
            DisplayMode dm = new DisplayMode(this.renderWidth, this.renderHeight); // new dimensions
            Display.setDisplayMode(dm); // resizes window
            Display.setLocation(oldX,  oldY); // reset position

Unfortunately there are a few problems with this: Firstly, lwjgl (which Minecraft uses) has mismatched position getters/setters - the getters seem to take into account the window furniture (title bar etc), whereas the setters don't - so calling

Display.setLocation(Display.getX(), Display.getY())

will result in the window gradually creeping up and to the left.

Secondly, this is lwjgl's helpful OSX implementation of the reshape method (which is called by setLocation):

    public void reshape(int x, int y, int width, int height) {
        //if (native_mode) {
        //    nResizeWindow(window, x, y, width, height);
        //}
    }

So calling setLocation on OSX doesn't actually move the window at all... though it does cache the values for x and y, which will then be used the next time the window is recreated (eg when the dimensions change), so, somewhat counter-intuitively, this almost works:

            int oldX = Display.getX();
            int oldY = Display.getY();
            Display.setLocation(oldX,  oldY); // Do this *before* changing dimension
            DisplayMode dm = new DisplayMode(this.renderWidth, this.renderHeight); // new dimensions
            Display.setDisplayMode(dm); // resizes window

Why is the setLocation necessary at all, in this case? Because Display's cached x and y values (which are used when recreating the window) have nothing to do with the window's actual position. They are not updated if the window is moved by the user. (This is what causes the centring in the first place - by default they are set to -1, which is a magic value used to indicate that the window should be centred, and moving the window manually doesn't change them, so each resize re-centres the window.)

Fortunately Display.getX() and getY() get the actual window positions (hence the mismatch between getters and setters - the getWindowX() and getWindowY() methods which return the cached values are private), and then these values can be written back to the cached values using setLocation, so that the window will appear in roughly the right place after recreation.

Ugh.

DaveyBiggers commented 7 years ago

To make matters worse, according to the lwjgl documentation for setLocation, "The window is clamped to remain entirely on the screen." As far as I can tell, this is just a lie. There doesn't appear to be any code anywhere that actually implements this. So the result of creeping up and to the left, eventually, is that the Minecraft window disappears off screen entirely.

Admittedly, it takes a while for this to happen, and under normal working conditions the Minecraft window won't be changing size often, so it shouldn't present a problem in most cases.

DaveyBiggers commented 6 years ago

This has been "fixed" (allowing for the above ugliness) in 0.31.0.

DaveyBiggers commented 6 years ago

Am reopening this because the fix (making sure this.mc.inGameHasFocus is false for the check in EntityRenderer) breaks the continuous attack command.