Weird Mouse behavior..

Started by Notch, April 14, 2005, 21:24:29

Previous topic - Next topic

Notch

I want to grab the mouse when the left mouse button is pressed, and release the mouse when the button is released.. This seems to cause all sorts of weird problems.


Try 1:

   boolean wasDown = false;
    boolean down = false;

    private void updateMouse()
    {
        Mouse.poll();

        while (Mouse.next())
        {
            if (Mouse.getEventButton()==0)
            {
                down = Mouse.getEventButtonState(); // (a)
                System.out.println("Mouse was "+(down?"pressed":"released")+"!");
            }
            if (Mouse.isGrabbed())
            {
                game.moveMouse(Mouse.getEventDX(), Mouse.getEventDY());
            }
        }
        
        if (wasDown != down) // Only grab/release mouse pointer if the button state has changed
        {
            System.out.println((down?"Grabbing":"Releasing")+" mouse"); // (b)
            Mouse.setGrabbed(down);
            wasDown = down;
        }

        System.out.println("Mouse is now "+(Mouse.isButtonDown(0)?"pressed":"released")+"!"); // (c)
    }


Behavior:
When the mouse button is pressed, down changed to true twice in a row (a), and "Grabbing mouse" (b) gets printed once.
Immediately after that, down gets set to false (a) a couple of dozen times in a row, and "Releasing mouse" (b) gets printed once.
The console then spams with "Mouse is now pressed" (c) until the mouse button is released, when a single mouse release event is triggered (a), and the mouse is released (b).
The console then correctly spams that the "Mouse is now released" (c)

Notch

Try 2:

   boolean wasDown = false;

    private void updateMouse()
    {
        Mouse.poll();

        while (Mouse.next())
        {
            if (Mouse.isGrabbed())
            {
                game.moveMouse(Mouse.getEventDX(), Mouse.getEventDY());
            }
        }
        
        boolean down = Mouse.isButtonDown(0);
        if (wasDown != down) // Only grab/release mouse pointer if the button state has changed
        {
            System.out.println((down?"Grabbing":"Releasing")+" mouse");
            Mouse.setGrabbed(down);
            wasDown = down;
        }

        System.out.println("Mouse is now "+(Mouse.isButtonDown(0)?"pressed":"released")+"!"); // (b)
    }


Behavior:
The game starts out by spamming that "Mouse is now released" (b).
When the mouse button is pressed, it correctly prints "Grabbing mouse" (a) once, then spams "Mouse is now pressed" (b).
Upon releasing the mouse button, "Releasing mouse" (a) is printed, then a single "Mouse is now released" (b), then "Grabbing mouse" (a) followed by endlessly many "Mouse is now pressed" (b).
Pressing the mouse button in this weird state does nothing, but releasing it again after that results in the same thing as before (immediately triggers a mouse press event)
The game (and LWJGL) now considers the mouse button to be pressed even though it isn't. Some investigation indicated that other programs agree about that.

Notch

Control test: (This works as expected)

   boolean wasDown = false;

    private void updateMouse()
    {
        Mouse.poll();

        while (Mouse.next())
        {
            if (Mouse.isGrabbed())
            {
                game.moveMouse(Mouse.getEventDX(), Mouse.getEventDY());
            }
        }
        
        boolean down = Keyboard.isKeyDown(Keyboard.KEY_M);
        if (wasDown != down) // Only grab/release mouse pointer if the button state has changed
        {
            System.out.println((down?"Grabbing":"Releasing")+" mouse"); // (a)
            Mouse.setGrabbed(down);
            wasDown = down;
        }

        System.out.println("Mouse is now "+(Mouse.isButtonDown(0)?"pressed":"released")+"!"); // (b)
    }


Behavior:
The console spams the correct state (b) at all times, regardless of how often the mouse is pressed. (Correct state of the mouse pointer, that is, not of the M button)
Upon pressing the M key, "Grabbing mouse" (a) is printed once, and the game does indeed grab the mouse pointer.
Releasing M prints "Releasing mouse" (a), and releases the mouse pointer properly.

Notch

Further testing:

Using the control test as following results in some predictable and interesting results:

1) Press and hold mouse button ("Mouse is now pressed" starts spamming)
2) Press and hold M (The mouse gets grabbed)
3) Release the mouse button ("Mouse is now released" starts spamming)
4) Release M (The mouse gets released)

When releasing M, the console output immediately changes to "Mouse is now pressed".

My guesstimate of what's happening is that the mouse pointer state returns to the old state when Mouse.setGrabbed(false) is called. Since the mouse pointer release event was consumed while the application had grabbed the mouse, the OS never realises that the mouse button is no longer pressed.


How can I solve this?

elias

Yes, that pretty much is a consequence of using the DISCL_EXCLUSIVE flag in DirectInput's SetCooperativeLevel:

Quote
Windows itself requires exclusive access to the system mouse. The reason is that system mouse events, such as a click on an inactive window, could force an application to unacquire the device, with potentially harmful results such as a loss of data from the input buffer. So when an application has exclusive access to the system mouse, created by passing GUID_SysMouse to IDirectInput8::CreateDevice, Windows is not allowed any access and no mouse messages are generated. A further side effect is that the cursor disappears.

(From the MSDN docs on cooperative levels.)

I'm not sure what to do about it though - using the NONEXCLUSIVE flag will not make the cursor disappear nor will the cursor be confined to the window area.

- elias

Notch

(bumpy)

This is a bit of a show-stopper for me..

elias

As I said, I'm not sure there is a good fix for this. Will you be content with a setCursorPos(x, y) that sets the native cursor position? It's implemented in CVS.

- elias

Notch

I fail to see how that would help..

elias

What exactly do you need then? Why do you want to capture the cursor on clicks, as opposed to, say, always having the cursor grabbed?

- elias

Orangy Tang

If you had a setCursor(x,y) and could set a custom, empty mouse image then you could emulate the same behaviour.

However I can't help but think that you've got a broken UI design there - it looks wrong and confusing for a user.

Notch

orangy: Broken GUI design? There are heaps of games that do this, mostly strategy games that let you drag the game area around with the right mouse button without moving the mouse cursor. (Transport Tycoon comes to mind)

The difference is that I don't want to lock the cursor to the game window when the "drag view" button isn't pressed, so I toggle mouse grabbed on and off.

And, as I said, if setGrabbed(true) is called while the mouse button is pressed, the os will never get the mouse button release event, and will keep thinking the mouse is pressed for all future. (or at least until it gets a release call)

Orangy Tang

Quote from: "Notch"orangy: Broken GUI design? There are heaps of games that do this, mostly strategy games that let you drag the game area around with the right mouse button without moving the mouse cursor. (Transport Tycoon comes to mind)
Yes, and as far as I've seen none of them grab or hide the cursor. I personally love that style of dragging around in 2d editors and have done it in a couple of them without problems (both LWJGL in a window).

Orangy Tang

I must apologise to Notch, I've only just switched to the latest version and seem to have hit the same snag as he has.

Primarily I want the good old right-click-and-drag to move the background around. Now I had this working fine (via Spaghetti) with 0.92 and all was good. However in .96 it seems like I can only use Mouse.getDX() or Mouse.getEventX(), depending on whether the mouse is grabbed or not.

I could keep track of previous position and get my own dx/dy values, which works when not grabbed. But now my code doesn't work with a grabbed mouse. All of a sudden I need to add lots of checks to see which mouse method i need to call, and potentially maintain extra state - all for something that worked fine in a previous release!

(Yes, I could probably add a fairly basic wrapper which chooses the right one and maintains delta values when not normally avalible, but thats not the point - it worked before! And if its so simple it would perhaps be better done by the Mouse class itself).

elias

Could you  be a little more specific as to what the problem is, Orangy? We always use getEventX/Y(), regardless of whether the mouse is grabbed or not, and it seems to work fine.

- elias

elias

Ok, I think I know what you're complaining about. I'm now allowing delta queries when grabbed. Be careful though, if you're using the deltas to track the absolute position of the cursor you can easily get lost.

- elias