X Tutup
Skip to content

NEW: Added Focus Events to Input Event Queue#2365

Open
VeraMommersteeg wants to merge 18 commits intodevelopfrom
input/add-focus-events-to-queue
Open

NEW: Added Focus Events to Input Event Queue#2365
VeraMommersteeg wants to merge 18 commits intodevelopfrom
input/add-focus-events-to-queue

Conversation

@VeraMommersteeg
Copy link
Collaborator

@VeraMommersteeg VeraMommersteeg commented Mar 3, 2026

Description

These are the managed changes for the package to introduce a focus event into the event buffer. It no longer listens to the Application.OnFocusChanged and instead changes focus based on the queued event from native. This will help us deal with desync focus state issues to make sure events are being processed in the right state.

Jira tickets:
ISX-2426
ISX-2427

Testing status & QA

Testing done:

  • Ran editor and playmode tests locally
  • Manually tested several Editor Playmode Input Behaviours
  • Built samples as a player and tried several background behaviour settings compared to develop

Testing by QA:

  • General pass against other samples

Overall Product Risks

  • Complexity: Medium - Focus event changes the way events are processed
  • Halo Effect: Low - Most changes are encapsulated to InputManager.cs

Comments to reviewers

I've spend some time cleaning up the OnUpdate a bit, didn't go too crazy and mostly just split up the code a bit in a couple of functions. But since the git diff makes it a bit difficult to see the changes, it might be easier to pull the branch and do a diff that way.

Checklist

Before review:

  • Changelog entry added.
    • Explains the change in Changed, Fixed, Added sections.
    • For API change contains an example snippet and/or migration example.
    • JIRA ticket linked, example (case %%). If it is a private issue, just add the case ID without a link.
    • Jira port for the next release set as "Resolved".
  • Tests added/changed, if applicable.
    • Functional tests Area_CanDoX, Area_CanDoX_EvenIfYIsTheCase, Area_WhenIDoX_AndYHappens_ThisIsTheResult.
    • Performance tests.
    • Integration tests.
  • Docs for new/changed API's.
    • Xmldoc cross references are set correctly.
    • Added explanation how the API works.
    • Usage code examples added.
    • The manual is updated, if needed.

During merge:

  • Commit message for squash-merge is prefixed with one of the list:
    • NEW: ___.
    • FIX: ___.
    • DOCS: ___.
    • CHANGE: ___.
    • RELEASE: 1.1.0-preview.3.

@VeraMommersteeg VeraMommersteeg self-assigned this Mar 3, 2026
@cla-assistant-unity
Copy link

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@VeraMommersteeg VeraMommersteeg changed the title Input/add focus events to queue Added focus events to queue Mar 3, 2026
@VeraMommersteeg VeraMommersteeg changed the title Added focus events to queue NEW: Added Focus Events to Input Event Queue Mar 4, 2026
@ekcoh ekcoh self-requested a review March 4, 2026 20:06
@VeraMommersteeg VeraMommersteeg marked this pull request as ready for review March 10, 2026 09:32
Copy link
Contributor

@u-pr u-pr bot left a comment

Choose a reason for hiding this comment

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

May require changes

This review identified several issues ranging from critical bugs in event handling to minor code cleanup items. The most significant concerns involve potential stale state during event processing and improper event discarding logic during focus changes in InputManager.cs, which could lead to stuck input states. Additionally, there are some minor performance improvements, concerns regarding profiler marker reliability, and several typos/commented-out code blocks in the test suite.

🤖 Helpful? 👍/👎

Comment on lines +3176 to 3188
var currentEventType = currentEventReadPtr->type;

Debug.Assert(!currentEventReadPtr->handled, "Event in buffer is already marked as handled");

// In before render updates, we only take state events and only those for devices
// that have before render updates enabled.
if (updateType == InputUpdateType.BeforeRender)
{
while (m_InputEventStream.remainingEventCount > 0)
{
Debug.Assert(!currentEventReadPtr->handled,
"Iterated to event in buffer that is already marked as handled");

device = TryGetDeviceById(currentEventReadPtr->deviceId);
if (device != null && device.updateBeforeRender &&
(currentEventReadPtr->type == StateEvent.Type ||
currentEventReadPtr->type == DeltaStateEvent.Type))
break;

currentEventReadPtr = m_InputEventStream.Advance(leaveEventInBuffer: true);
}
}
ProcessBeforeRenderStateEvents(out device, out currentEventReadPtr);

if (m_InputEventStream.remainingEventCount == 0)
break;

var currentEventTimeInternal = currentEventReadPtr->internalTime;
Copy link
Contributor

Choose a reason for hiding this comment

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

high
If ProcessBeforeRenderStateEvents skips over events (advancing currentEventReadPtr), currentEventType becomes stale because it still holds the type of the first evaluated event (e.g., FocusEvent or DeviceRemoveEvent) rather than the newly found StateEvent.

This stale currentEventType will be incorrectly passed into subsequent checks like SkipEventDueToEditorBehaviour and ShouldDeferEventBetweenEditorAndPlayerUpdates, potentially causing the StateEvent to be erroneously dropped or deferred based on the rules meant for the skipped event.

You should update currentEventType after calling ProcessBeforeRenderStateEvents, or declare it after the BeforeRender block:

if (updateType == InputUpdateType.BeforeRender)
    ProcessBeforeRenderStateEvents(out device, out currentEventReadPtr);

if (m_InputEventStream.remainingEventCount == 0)
    break;

var currentEventTimeInternal = currentEventReadPtr->internalTime;
var currentEventType = currentEventReadPtr->type; // Grab type here!

🤖 Helpful? 👍/👎

Comment on lines +3374 to +3387
var focusEventType = new FourCC(FocusConstants.kEventType);
var possibleFocusEvent = m_InputEventStream.Peek();

case IMECompositionEvent.Type:
{
var imeEventPtr = (IMECompositionEvent*)currentEventReadPtr;
var textInputReceiver = device as ITextInputReceiver;
textInputReceiver?.OnIMECompositionChanged(imeEventPtr->compositionString);
break;
}
if (possibleFocusEvent != null)
{
if (possibleFocusEvent->type == focusEventType && !gameShouldGetInputRegardlessOfFocus)
{
// If the next event is a focus event and we're not supposed to get input of the current update type in the next one, drop current event.
// This ensures that we don't end up with a half processed events due to swapping buffers between editor and player,
// such as InputActionPhase.Started not being finished by a InputActionPhase.Performed and ending up in a pressed state in the previous update type
m_InputEventStream.Advance(false);
return true;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

high
This unconditionally drops the current event if the next event in the buffer is a FocusEvent and !gameShouldGetInputRegardlessOfFocus.

This causes the exact "half processed" states the comment mentions it wants to prevent, because it only drops the single event immediately preceding the focus change. For example, if a queue contains [StateEvent(Button Down), StateEvent(Button Up), FocusEvent(Lost)], only the Button Up event will be dropped, leaving the button permanently stuck down!

Furthermore, this will recklessly discard critical system events if they happen to precede a focus change (e.g. dropping a DeviceRemoveEvent or even another FocusEvent if multiple focus changes occur in one frame).

Consider handling cleanup explicitly via device reset/sync upon focus changes instead of arbitrarily dropping the event directly before the focus event.

🤖 Helpful? 👍/👎

return m_CurrentUpdate;

#if UNITY_EDITOR
#if UNITY_EDITOR
// We can no longer rely on checking the curent focus state, due to this check being used pre-update
Copy link
Contributor

Choose a reason for hiding this comment

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

low
Typo in comment: "curent" should be "current".

🤖 Helpful? 👍/👎

Comment on lines +2142 to +2143
focusState = Application.isFocused ? focusState |= FocusFlags.ApplicationFocus
: focusState &= ~FocusFlags.ApplicationFocus;
Copy link
Contributor

Choose a reason for hiding this comment

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

medium
This ternary assignment is redundant and potentially inefficient. Since focusState is a property whose setter performs logic (updating the runtime state), using |= or &= inside the ternary branches causes the setter to be invoked twice—once for the compound assignment and once for the ternary's final assignment.

Consider simplifying to:

focusState = Application.isFocused 
    ? focusState | FocusFlags.ApplicationFocus 
    : focusState & ~FocusFlags.ApplicationFocus;

🤖 Helpful? 👍/👎

k_InputUpdateProfilerMarker.Begin();

if (m_InputEventStream.isOpen)
using (k_InputUpdateProfilerMarker.Auto())
Copy link
Contributor

Choose a reason for hiding this comment

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

medium
The previous implementation included a cautionary comment specifically warning against using try/finally with this profiler marker due to unreliability in the EndSample execution. The new code uses marker.Auto(), which uses try/finally under the hood. Have you verified that this unreliability is no longer a concern for the Unity versions targeted by this package?

🤖 Helpful? 👍/👎

InputSystem.Update();
ScheduleFocusEvent(false);
InputSystem.Update(InputUpdateType.Dynamic);
//InputSystem.Update();
Copy link
Contributor

Choose a reason for hiding this comment

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

low
This commented-out call to InputSystem.Update() appears to be leftover debugging code. Consider removing it.

🤖 Helpful? 👍/👎

InputSystem.Update();
ScheduleFocusEvent(true);
InputSystem.Update(InputUpdateType.Dynamic);
//InputSystem.Update();
Copy link
Contributor

Choose a reason for hiding this comment

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

low
This commented-out call to InputSystem.Update() appears to be leftover debugging code. Consider removing it.

🤖 Helpful? 👍/👎

"SoftReset Mouse1", "SoftReset Mouse3", "HardReset Joystick", "SoftReset TrackedDevice2"
"Sync Keyboard"
}));
// Enabled devices that don't support syncs dont get reset for Ignore Forcus as we do not want to cancel any actions.
Copy link
Contributor

Choose a reason for hiding this comment

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

low
Typo in comment: "Forcus" should be "Focus".

🤖 Helpful? 👍/👎

InputSystem.Update();
ScheduleFocusEvent(true);

// We have to schedule the specificic update type that the player is configured to process events in,
Copy link
Contributor

Choose a reason for hiding this comment

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

low
Typo in comment: "specificic" should be "specific".

🤖 Helpful? 👍/👎

{
// When not running in the background, the same thing happens but only on focus gain.
runtime.PlayerFocusGained();
//runtime.PlayerFocusGained();
Copy link
Contributor

Choose a reason for hiding this comment

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

low
Commented-out code //runtime.PlayerFocusGained(); should be removed to keep the test source clean.

🤖 Helpful? 👍/👎

@codecov-github-com
Copy link

codecov-github-com bot commented Mar 10, 2026

Codecov Report

Attention: Patch coverage is 74.87179% with 49 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...putsystem/InputSystem/Utilities/DelegateHelpers.cs 50.00% 14 Missing ⚠️
...ts/Tests/InputSystem/Plugins/EnhancedTouchTests.cs 18.18% 9 Missing ⚠️
...nity.inputsystem/InputSystem/LegacyInputManager.cs 90.62% 9 Missing ⚠️
Assets/Tests/InputSystem/Plugins/UITests.cs 0.00% 8 Missing ⚠️
Assets/Tests/InputSystem/CoreTests_Actions.cs 57.14% 3 Missing ⚠️
Assets/Tests/InputSystem/CoreTests_Devices.cs 82.35% 3 Missing ⚠️
...nity.inputsystem/InputSystem/NativeInputRuntime.cs 50.00% 2 Missing ⚠️
....inputsystem/Tests/TestFixture/InputTestRuntime.cs 66.66% 1 Missing ⚠️
@@             Coverage Diff             @@
##           develop    #2365      +/-   ##
===========================================
+ Coverage    77.90%   78.05%   +0.14%     
===========================================
  Files          476      477       +1     
  Lines        97613    98388     +775     
===========================================
+ Hits         76048    76792     +744     
- Misses       21565    21596      +31     
Flag Coverage Δ
inputsystem_MacOS_2022.3_project 61.79% <73.40%> (-13.61%) ⬇️
inputsystem_MacOS_6000.0_project 63.72% <73.82%> (-13.58%) ⬇️
inputsystem_MacOS_6000.3_project 63.71% <73.82%> (-13.59%) ⬇️
inputsystem_MacOS_6000.4_project 63.72% <73.82%> (-13.58%) ⬇️
inputsystem_MacOS_6000.5_project 63.73% <58.16%> (-13.58%) ⬇️
inputsystem_MacOS_6000.6_project 63.73% <58.16%> (-13.58%) ⬇️
inputsystem_Ubuntu_2022.3_project 61.14% <73.40%> (-14.05%) ⬇️
inputsystem_Ubuntu_6000.0_project 63.02% <73.82%> (-14.08%) ⬇️
inputsystem_Ubuntu_6000.3_project 63.00% <73.82%> (-14.10%) ⬇️
inputsystem_Ubuntu_6000.4_project 63.02% <73.82%> (-14.09%) ⬇️
inputsystem_Ubuntu_6000.5_project 63.02% <58.16%> (-14.09%) ⬇️
inputsystem_Ubuntu_6000.6_project 63.02% <58.16%> (-14.09%) ⬇️
inputsystem_Windows_2022.3_project 62.04% <73.40%> (-13.49%) ⬇️
inputsystem_Windows_6000.0_project 63.96% <73.82%> (-13.46%) ⬇️
inputsystem_Windows_6000.3_project 63.95% <73.82%> (-13.47%) ⬇️
inputsystem_Windows_6000.4_project 63.96% <73.82%> (-13.47%) ⬇️
inputsystem_Windows_6000.5_project 63.96% <58.16%> (-13.46%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
Assets/Tests/InputSystem/CoreTests_Editor.cs 97.95% <100.00%> (+<0.01%) ⬆️
Assets/Tests/InputSystem/CoreTests_State.cs 96.43% <100.00%> (+0.01%) ⬆️
...ssets/Tests/InputSystem/Plugins/InputForUITests.cs 96.70% <100.00%> (ø)
Assets/Tests/InputSystem/Plugins/UserTests.cs 98.20% <100.00%> (+<0.01%) ⬆️
...com.unity.inputsystem/InputSystem/IInputRuntime.cs 80.00% <ø> (ø)
.../com.unity.inputsystem/InputSystem/InputManager.cs 92.96% <ø> (+1.18%) ⬆️
....inputsystem/Tests/TestFixture/InputTestFixture.cs 76.73% <100.00%> (+0.40%) ⬆️
....inputsystem/Tests/TestFixture/InputTestRuntime.cs 90.80% <66.66%> (-0.56%) ⬇️
...nity.inputsystem/InputSystem/NativeInputRuntime.cs 52.76% <50.00%> (-0.30%) ⬇️
Assets/Tests/InputSystem/CoreTests_Actions.cs 98.12% <57.14%> (-0.03%) ⬇️
... and 5 more

... and 10 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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

Successfully merging this pull request may close these issues.

1 participant

X Tutup