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

Recreating SDL window during mouse down event can lead to next mouse down event getting lost #984

Closed
SDLBugzilla opened this issue Feb 10, 2021 · 1 comment

Comments

@SDLBugzilla
Copy link
Collaborator

SDLBugzilla commented Feb 10, 2021

This bug report was migrated from our old Bugzilla tracker.

These attachments are available in the static archive:

Reported in version: HG 2.0
Reported for operating system, platform: Linux, x86_64

Comments on the original bug report:

On 2013-07-26 11:43:44 +0000, Ellie wrote:

Recreating SDL window during mouse down event sometimes leads to the next mouse down event getting lost (only mouse release arrives). Let me explain with more obvious step by step instructions:

Test case:

#include "SDL.h"

int main(int argc, const char* argv) {
    SDL_Window* w = SDL_CreateWindow("blubb", 0, 0, 320, 240, 0);
    int firstmousedown = 1;
    SDL_Event e;
    while (1) {
        while (SDL_PollEvent(&e) == 1) {
            if (e.type == SDL_MOUSEBUTTONDOWN) {
                printf("SDL_MOUSEBUTTONDOWN\n");
                if (firstmousedown) {
                    firstmousedown = 0;
                    SDL_DestroyWindow(w);
                    SDL_Delay(1000);
                    w = SDL_CreateWindow("blabb", 0, 0, 320, 240, 0);
                }
            }
            if (e.type == SDL_MOUSEBUTTONUP) {
                printf("SDL_MOUSEBUTTONUP\n");
            }
            if (e.type == SDL_QUIT) {
                exit(0);
            }
        }
    }
}

Do this:

  1. Run the program. A window should appear.
  2. Do a fast regular click on the window. It should disappear.
  3. Wait a second for the window to reappear (the mouse button should be released by now!)
  4. The window reappears. Press mouse button down and see how there is no SDL_MOUSEBUTTONDOWN, release mouse button and see how there is now a SDL_MOUSEBUTTONRELEASE again (but the down event was missed!).
  5. Additional clicks work just fine again.

This breaks the first mouse click event in my engine in some screen resolution change situations (actually for pretty much all of them). A fix would be appreciated!

On 2014-05-10 15:41:51 +0000, Leonardo wrote:

I can confirm this bug.

On 2014-08-20 02:50:58 +0000, skaller wrote:

First, a more general problem: it is possible to get messages
for a window that doesn't exist. On OSX 10.6.8 after I destroy
a window, I get a HIDDEN event. I am sure hoping when SDL
creates a window it does NOT reuse the window ID integer,
because if it does, and I have created a new window that gets
that ID, then when I read the HIDDEN message it will be associated
with the wrong window.

Now, considering you code, you have a mouse down, destroy
the window .. what happens to the mouse up? It is possible
this depends on just how fast you release the mouse button
compared to how fast the window gets destroyed. Once the window
is gone, there is no window for the mouse release to be associated
with, and probably the OS/WM/SDL combo will not see the release.

But if you release really fast it might see the release.
But you will only get that release after you have destroyed
the window, created another one, and then polled the event queue.

Ok, so which window ID will that release be associated with?
Hopefully the old one, and hopefully the new window will have
a distinct ID number.

OK so now, the surprise: SDL is definitely and consistently
giving completely inconsistent results on OSX.

Here's what I did: press the mouse button in the window
and move out of the window. I get a LEAVE message.

Now I release the mouse button.
WOOPS! I get a mouse up on window 0 (the full screen).
But then I get a ENTER message for the window!
If I move the mouse again, and not until I move the
mouse again, I get a LEAVE message.

And then a FOCUSLOST.

Given how confused SDL is about mouse captures
(the existing function is totally wrong for windows)
I'm not surprised.

So: the bug in the bug report is not the real bug.
SDL isn't just losing a mouse click. It has no
real idea how to handle the mouse outside a window
at all (on OSX anyhow).

Consider this: my window has no focus. I press the left mouse
on it. I get a focus gained message. Now I release the mouse
button .. no message (the "whole click" sets the focus, right??)

But with the right mouse button, I get enter/leave and mouse up/down
messages when I click in the window: there's no focus change.
But I do get enter/leave.

With the middle mouse button I get mouse up and down, even when
the release is outside the window, but no enter or leave from
the click.

However if the window has focus .. then you get enter/leave
on the mouse continuously after just one press of the middle
button in the window, even if the button is released outside
the window.

It's not clear ANY of this weird behaviour is a bug.
Some code will be platform dependent and some platform
independent. Fix it in the generic part for one platform
and you'll break another. Ultimately the real problem is that
the WM is trying to implement an ill-define protocol:
user actions (mouse, KB) are asynchronous with respect to
video display (windows). And they all do it differently
(I hate the OSX "click to gain focus" because if I'm using
Vim with multiple panes I have to click twice, and the second
click moves the caret/edit position .. grrr .. give me back
autoraise!)

On 2017-07-08 07:20:08 +0000, Ben Kurtovic wrote:

Created attachment 2791
Patch to process missing button events

I was digging into this today as it's been causing me considerable frustration for such a seemly minor bug. There does not seem to be an easy fix in client code, but fortunately there is a very easy (though slightly questionable) fix in SDL itself.

Basically: SDL has a global mouse state that tracks what buttons it currently thinks are pressed. If it receives a button press event that would not change this global state, it discards the event. The issue here appears to be that SDL receives the mouse-down event like normal, but the mouse-up event is lost because it is not associated with the right window. Thus, SDL's global mouse state remains on "pressed", even after the mouse is released and the new window is created. It does get a proper mouse-down event from the WM, but it ignores it because it thinks the mouse button is already pressed.

The patch just removes this check and always processes mouse button press events. I am not convinced that the check is even necessary in general, because why would the WM send us a press event if the mouse wasn't being pressed? If someone has critical application code that relies on never receiving two presses in a row without a release, then maybe they should have their own boolean flag--it does not seem safe to rely on this behavior to begin with.

Anyway, I think the possible design flaw here is the use of a global mouse state rather than a per-window one. While there is indeed only one mouse in existence, SDL doesn't have enough information to keep track of its state all the time--as we see here, relevant mouse events can happen when we don't have a way of receiving them. If the mouse state was per-window, then the mouse would properly be marked as unpressed when the new window is created, regardless of whatever happened to the real mouse in the mean time.

On 2018-08-23 18:48:11 +0000, Ellie wrote:

Yeah it would be nice if this was fixed. I assumed it was SDL2's mouse state being confused, and it'd be really useful if that didn't happen.

(In reply to skaller from comment # 2)

It's not clear ANY of this weird behaviour is a bug.

I am 99% sure it is an SDL bug. The mouse down/up lost happens when the window has long been re-created (read my reproduction instructions carefully), so none of the "get events while window is possibly not present" issues you mentioned in your lengthy text should apply. Unless of course I misunderstood what you wrote

On 2018-08-28 20:40:42 +0000, Sam Lantinga wrote:

The contract in the SDL API is that you don't get events unless they actually change state. However, clearly if a window goes away, the mouse won't be pressed in it. Let me think on this a bit.

@slouken slouken removed the bug label May 11, 2022
@slouken
Copy link
Collaborator

slouken commented Nov 4, 2023

This is fixed in the latest SDL code.

The SDL3 version of the test program is:

#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>

int main(int argc, const char *argv)
{
    SDL_Window *w = SDL_CreateWindow("blubb", 320, 240, 0);
    int firstmousedown = 1;
    SDL_Event e;
    while (1) {
        while (SDL_PollEvent(&e) == 1) {
            if (e.type == SDL_EVENT_MOUSE_BUTTON_DOWN) {
                SDL_Log("SDL_MOUSEBUTTONDOWN\n");
                if (firstmousedown) {
                    firstmousedown = 0;
                    SDL_DestroyWindow(w);
                    SDL_Delay(1000);
                    w = SDL_CreateWindow("blabb", 320, 240, 0);
                }
            }
            if (e.type == SDL_EVENT_MOUSE_BUTTON_UP) {
                SDL_Log("SDL_MOUSEBUTTONUP\n");
            }
            if (e.type == SDL_EVENT_QUIT) {
                exit(0);
            }
        }
    }
}

@slouken slouken closed this as completed Nov 4, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants