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

Make pause hotkey work while game is focused #103411

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

GustJc
Copy link
Contributor

@GustJc GustJc commented Feb 28, 2025

Closes godotengine/godot-proposals#11875

This makes the Pause Running Project (F7) hotkey work while the game is being played.
(When the pause hotkey is pressed, the game is paused and minimized. But you can enable always on top to prevent this behavior)

working.mp4

Notes:
This does not address the pause button from the embeded window as its pause works in a different way.
It can pause the embed window, but that's because the hotkey is calling the pause button from the EditorRunBar.
I think this inconsistency is a bug and should be handled in a different PR

@GustJc GustJc requested a review from a team as a code owner February 28, 2025 18:43
@Calinou Calinou added this to the 4.x milestone Feb 28, 2025
Comment on lines 812 to 853
EditorRunBar::get_singleton()->get_pause_button()->set_pressed(true);
EditorRunBar::get_singleton()->get_pause_button()->emit_signal(SceneStringNames::get_singleton()->pressed);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

set_pressed() emits the signal automatically (unlike set_pressed_no_signal()), to the emit_signal() line is redudant as far as I can tell.

void BaseButton::set_pressed(bool p_pressed) {
bool prev_pressed = status.pressed;
set_pressed_no_signal(p_pressed);
if (status.pressed == prev_pressed) {
return;
}
if (p_pressed) {
_unpress_group();
if (button_group.is_valid()) {
button_group->emit_signal(SceneStringName(pressed), this);
}
}
_toggled(status.pressed);
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's what I thought too. But the signal is not emited when calling it inside the script_editor_debugger.cpp.
If fails at line 345 because button_group is null. So I had to call the signal manually.
(It does emit the toggled signal, just not the pressed one)

I think its because the EditorRunBar sets the pause button as set_toggle_mode(true) but the button itself doesn't belong to any ButtonGroup.

And the code that actually pauses the game is connected to the pressed signal.

EditorRunBar::get_singleton()->get_pause_button()->connect(SceneStringName(pressed), callable_mp(this, &DebugAdapterProtocol::on_debug_paused));

EditorRunBar::get_singleton()->get_pause_button()->connect(SceneStringName(pressed), callable_mp(this, &EditorDebuggerNode::_paused));

Which the hotkey can emit the pressed signal without the ButtonGroup check if called from inside the _on_action_event()

void BaseButton::on_action_event(Ref<InputEvent> p_event) {

(Also, it seems a lot of code called inside these classes calls set_pressed(true/false) directly, which just so happens it not emitting the set_pressed signal and causing a cyclic call loop, so adding the pause to a button group might break a few unexpected things)

Not sure if this ButtonGroup check inconsistency is a bug or intended, but it seems there is a lot of code that works based on this behavior and making changes to it now might not be ideal.

Anyways, I think emit_signal is necessary in this case for a clean solution.

@Calinou
Copy link
Member

Calinou commented Feb 28, 2025

This does not address the pause button from the embeded window as its pause works in a different way. It can pause the embed window, but that's becaus

These are different kinds of pausing. Depending on what you want to achieve, one is more suited than the other.

The embedded window's pause button toggles SceneTree pause, so it keeps rendering and processing nodes that have the Always process mode (and shaders using TIME). It's the same as in-game pausing as exposed by Godot.

F7 pause on the other hand is a non-conditional debugger break, so you can use the expression evaluator, but the game will stop rendering new frames and will stop processing everything. This means alt-tabbing back to it might display nothing to look at. This kind of pausing is only available in debug builds.

@GustJc
Copy link
Contributor Author

GustJc commented Feb 28, 2025

These are different kinds of pausing. Depending on what you want to achieve, one is more suited than the other.

The embedded window's pause button toggles SceneTree pause, so it keeps rendering and processing nodes that have the Always process mode (and shaders using TIME). It's the same as in-game pausing as exposed by Godot.

F7 pause on the other hand is a non-conditional debugger break, so you can use the expression evaluator, but the game will stop rendering new frames and will stop processing everything. This means alt-tabbing back to it might display nothing to look at. This kind of pausing is only available in debug builds.

Makes sense, I thought it would be something along those lines.
This PR will only deal with the classic pause/break button.
But since they don't need to be synced, I can try and take a look how to make the embeded buttons work with new hotkeys in a separate PR.

Thanks.

Copy link
Member

@Calinou Calinou left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested locally on Linux, it mostly works as expected.

I noticed two issues that hamper usability:

  • After pausing, you can't resume by pressing F7 while the game window (or embedded game) is focused. You need to click the editor's pause button or press F7 while the editor is focused.
  • If you press F7 again while the game is paused, attempting to unpause it in the editor will pause the game one frame later, likely because the in-game F7 attempt is queued while the game is paused.
    • This is printed when you attempt to pause a second time:
ERROR: Got break when already broke!
   at: debug (./core/debugger/remote_debugger.cpp:471)

@GustJc
Copy link
Contributor Author

GustJc commented Mar 5, 2025

I noticed two issues that hamper usability:

  • If you press F7 again while the game is paused, attempting to unpause it in the editor will pause the game one frame later, likely because the in-game F7 attempt is queued while the game is paused.

    • This is printed when you attempt to pause a second time:
ERROR: Got break when already broke!
   at: debug (./core/debugger/remote_debugger.cpp:471)

My mistake, I think I fixed this problem.
Maybe the paused input handling here is different but I didn't get this on windows.
If you could test it again it'd be very helpful.


  • After pausing, you can't resume by pressing F7 while the game window (or embedded game) is focused. You need to click the editor's pause button or press F7 while the editor is focused.

That's an issue I don't know how to solve. Even the F8 to quit doesn't work.
When the debug break is active the godot window is paused so window_input is never called.
And the Godot editor window can't capture events when not focused.
I have no idea how to solve this. I can only think of hacky OS-depend ways to make this work, but that's not feasible.

Maybe this can be fixed in another issue by someone with more experience on the issue?


Pushed rebased to the new master and hopefully fixed the the "break already broke" issue.

@GustJc GustJc force-pushed the pause_hotkey_unfocused branch from 70e3118 to 0a8e882 Compare March 16, 2025 12:56
@GustJc
Copy link
Contributor Author

GustJc commented Mar 16, 2025

Rebased to remove conflicts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Pause Running Project shortcut hotkey should work when the playing game is focused
2 participants