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

Input system breaks if the game window loses focus when there's a mouse button down #33928

Open
guineapenguin opened this issue Nov 27, 2019 · 18 comments

Comments

@guineapenguin
Copy link

Hello. I've already had this happening when I was using 3.1 mono on Windows 7 x64, I've tried 3.1.1 and 3.2 beta2 and it's still happening.

I'll copy/paste the details from a post I made on Reddit to ask if anyone else was experiencing this.

It does happen even with an empty project, so it shouldn't be my code. Also, when I go back to the game, the window's bar doesn't work anymore, can't drag it and its buttons don't work. They start working again if I click somewhere else (outside of the game window). Then they stop working again if I click anything within the game window, and notifications like mouse_entered and mouse_exited don't work anymore if I press a mouse button and release it after moving it.

It only happens with Godot, other programs work fine, and I've tried it on different PCs and it does that on all them. There's a few issues on GitHub about input actions not being released when the window loses focus, and the workaround for that is to check for them and release them via code. But even doing that doesn't fix this problem, the only fix for it is to restart the game.

To reproduce: run any project, even a new empty one, hold mouse button down anywhere on the game window, alt-tab, go back to the game window either by alt-tabbing or by clicking on it. Click anywhere and the window's bar will stop working, if there's any control node in the project they won't be working anymore.

@Zylann
Copy link
Contributor

Zylann commented Nov 27, 2019

Can confirm on Windows 10 64 bits. I held a mouse button anywhere in my game (a spot without anything special), alt-tabbed, went back, could drag the title bar once but then it got stuck afterwards. The whole window decoration doesn't respond.

I had a similar symptom a while back with different conditions: #15833 (comment)

@guineapenguin
Copy link
Author

I had a similar symptom a while back with different conditions: #15833 (comment)

It's the same thing being discussed in that issue, it's just that the OP didn't knew what was causing it. I found out what it was because I was deliberately trying to break my UI and inputs in any possible way that could happen in real-usage cases, but if you're just using the editor you won't notice if some other window got the focus between your button down and up. Could that be the "different conditions" when it was happening to you, were you using any other software that would put its window to foreground at "random" times?

@Zylann
Copy link
Contributor

Zylann commented Nov 27, 2019

@guineapenguin I don't remember tbh, all trace I have is the video in my comment

@capnm
Copy link
Contributor

capnm commented Nov 27, 2019

You may already know that from the joypad issue #16832 (#33656) 😺
As a workaround for that and this issue in my UI, I have to additionally track the window focus …

@Zylann
Copy link
Contributor

Zylann commented Nov 27, 2019

@capnm but in the present case there is no way to handle what happens, isn't it? Window decorations is something pretty OS-related, games usually don't touch/use that

@capnm
Copy link
Contributor

capnm commented Nov 28, 2019

@Zylann In the callback, when the window loses or gains the focus, I can stop the handling of unwanted (or try to fix-up broken) input events. In this case I think the app newer receives the mouse-button-up event. I had similar problems in glfw with modal keys. IIRC they fixed that with some heuristics.

@guineapenguin
Copy link
Author

guineapenguin commented Nov 28, 2019

The issue about the gamepad is not a bug by definition, not at the process level, because (as partially explained there) the game's window isn't being sent the gamepad input events by the OS, it is instead just checking the gamepad's buttons status. Mouse position is polled the same way, even when the window isn't focused the game will know it. And you can still poll mouse and keyboard buttons statuses when the window isn't focused, it's just that the OS is "locking" and sending those input events to the focused window only, for obvious reasons.

It can be considered a bug for your game but it is also something that you may want to happen, so it's more like having locked FPS by default, it only sucks when you can't unlock them, but you may also want them to be locked even if they come unlocked out of the box (and if you can't that may suck, too, because you may want the game not to use 100% of one or more cores/threads when it's not needed).

For example you may want your game to process mouse movement events even when it's not focused, most processes will do so, you may want to open/view a tooltip in your browser or your game while you're doing something else in another window, without the need to click or alt-tab to the other process and back.

but in the present case there is no way to handle what happens, isn't it? Window decorations is something pretty OS-related, games usually don't touch/use that

It's not the OS window decoration that breaks but the game window's child HWND (the game area) that gets the focus when you click on it and somehow it "locks" the mouse button as pressed on it until the focus goes to another process' window. The title bar stops receiving any kind of mouse events after you click once on the game's area (notice that the bar's control buttons just don't get mouse enter/exit events anymore, that's the same thing that happens if the mouse button is actually pressed when you move it), and Godot's control nodes stop working, too.

I've never seen a process do something like this and have no idea about what Windows actually does when it's sending inputs to the HWND which has focus, but it seems to be Godot doing that at the "OS-to-process" level and nothing you can fix by managing the events that the Godot's process "outputs" to the game after that, which are the ones you have access to; you can't even work around that by making your own input system from scratch and bypassing Godot's because of the level at which the process breaks it.

[Edit: I can't see any reason for wanting to process gamepad events when not focused, for the same reason you don't want keyboard events to be processed, too. It's just that this bug here just breaks the app/process and there seems to be no workaround for it. All I wrote in the first part isn't really needed for this issue and I'm aware that you guys already know these things, but I wrote it because someone may end up here from other issues and it could be helpful for them.]

@capnm
Copy link
Contributor

capnm commented Nov 28, 2019

What you describe sounds similar to the linux Godot editor 'frozen' state in #29521 #27669 ...
I can't reproduce this in an app myself. You could try to add some debug output and look what that prints if the 'input event freeze' happens, e.g.:

extends Node

func _notification(what):
	match what:
		NOTIFICATION_WM_FOCUS_IN:
			print("NOTIFICATION_WM_FOCUS_IN")
		NOTIFICATION_WM_FOCUS_OUT:
			print("NOTIFICATION_WM_FOCUS_OUT\n")
		NOTIFICATION_WM_QUIT_REQUEST:
			print("NOTIFICATION_WM_QUIT_REQUEST")

		NOTIFICATION_WM_MOUSE_ENTER:
			print("NOTIFICATION_WM_MOUSE_ENTER")
		NOTIFICATION_WM_MOUSE_EXIT:
			print("NOTIFICATION_WM_MOUSE_EXIT")

@guineapenguin
Copy link
Author

All those notifications work fine, but IsMouseButtonPressed(1) returns true when the game window gains focus again (after alt-tabbing while it was pressed and releasing it before going back to Godot). This is all I've tested today.

I remember doing some more testing when I first met this bug a while ago, and even manually setting the inputs as released when the window loses focus doesn't fix it (nor does it make control nodes work again, so what's broken on the process will keep breaking stuff in the editor/game).

The problem isn't caused by the alt-tabbing itself because I had also tested it by having another process get the focus while the mouse button is down and the same thing happens.

Calinou added a commit to Calinou/godot that referenced this issue Nov 28, 2019
This makes it possible to know whether the window is focused
at a given time, without having to track the focus state manually
using `NOTIFICATION_WM_FOCUS_IN` and `NOTIFICATION_WM_FOCUS_OUT`.

This partially addresses godotengine#33928.
@capnm
Copy link
Contributor

capnm commented Nov 28, 2019

but IsMouseButtonPressed(1) returns true when the game window gains focus again (after alt-tabbing while it was pressed and releasing it before going back to Godot)

That doesn't happen in Linux:

	NOTIFICATION_WM_MOUSE_ENTER:
		g.info("NOTIFICATION_WM_MOUSE_ENTER")
		print("is_mouse_button_pressed(1):", Input.is_mouse_button_pressed(1))

hold LMB

InputEventMouseButton pressed:True, osw focused:True

cmd+tab to another os window

I: NOTIFICATION_WM_FOCUS_OUT
I: NOTIFICATION_WM_FOCUS_OUT
I: NOTIFICATION_WM_MOUSE_EXIT
I: NOTIFICATION_WM_FOCUS_OUT
I: NOTIFICATION_WM_MOUSE_EXIT

release LMB

InputEventMouseButton pressed:False, osw focused:False

cmd-tab back to godot app

I: NOTIFICATION_WM_FOCUS_IN
I: NOTIFICATION_WM_MOUSE_ENTER
is_mouse_button_pressed(1):False
I: NOTIFICATION_WM_FOCUS_IN

click on LMB

InputEventMouseButton pressed: True, osw focused:True
InputEventMouseButton pressed: False, osw focused:True

@Zylann
Copy link
Contributor

Zylann commented Jan 11, 2020

Still happens in Godot 3.2 beta 5, had it during a GDScript debugging session, as the debugger kicked in when I click on something.

@RobertBColton
Copy link
Contributor

I am not too familiar with Godot internals but am working on a fix for this in another engine. Here's some tips about what you guys are experiencing.

  • There's Win32 GetKeyboardState/GetAsyncKeyState to poll the keyboard/mouse directly.
  • There's a similar API in SDL to do the same.
  • There's Win32 SetCapture/ReleaseCapture to capture the mouse when it's clicked on the window (giving it exclusive access to the mouse until the button is released).
  • There's also SDL_CaptureMouse which performs the same way.

@RobertBColton
Copy link
Contributor

RobertBColton commented Jun 24, 2020

Please remember the appropriate time to release the capture is when all mouse buttons are released, you can verify this in other applications. A way to do this is to check the wParam of the UP mouse messages in Win32 or by checking that SDL_GetMouseState(NULL, NULL) is false on SDL mouse release event.

@Calinou Calinou changed the title Input system breaks if the game window loses focus when there's a mouse button down. Input system breaks if the game window loses focus when there's a mouse button down Jun 25, 2020
@z1dev
Copy link

z1dev commented Jul 4, 2021

Just reporting that this is still happening in latest Godot 3.3.2

@Listwon
Copy link
Contributor

Listwon commented Sep 24, 2021

Still reproducible in Godot 3.3.3 on Windows 10

@ChronoDK
Copy link

ChronoDK commented May 5, 2022

Still a problem in 3.4 and 3.5. Try holding down a mouse button and then tab away to another window and release the mouse button there. When you return to the game it still sees the mouse button as being down, even when it's not.

It's a big problem in an html5 game, where just leaving the game div results in the same behavior. So a drag motion outside the div where the button is then released, will mean the game sees the mouse as still down when it returns to the div.

There need to be some new method of polling button status, which report the correct result always.

@RobertBColton
Copy link
Contributor

RobertBColton commented Jun 14, 2022

In the browser you need to use setPointerCapture as the HTML5 equivalent of the Win32 fixes I gave you above.
This is a really easy issue to fix.

Edit: I don't know what this is but it looks like there might be a setting to enable mouse capture mode?

@RobertBColton
Copy link
Contributor

RobertBColton commented Jun 14, 2022

Ok, upon reading the docs it looks like this is maybe a user issue. You are expected to set the mouse to captured on your own. What you can do is handle a button press in your game, and call set mouse mode with captured. Then on button release you should set the mouse mode to not captured. That's how most SDL examples handle it online.

However, Godot could probably handle a little more the mouse capturing transparently under the hood like other engines do. Especially with respect to some UI controls.

https://docs.godotengine.org/en/stable/classes/class_input.html#class-input-method-set-mouse-mode

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

9 participants