Skip to content

NEW: Added Focus Events to Input Event Queue#2365

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

NEW: Added Focus Events to Input Event Queue#2365
VeraMommersteeg wants to merge 21 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 +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? 👍/👎

@codecov-github-com
Copy link

codecov-github-com bot commented Mar 10, 2026

Codecov Report

Attention: Patch coverage is 74.61140% 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 80.00% 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.01%   +0.11%     
===========================================
  Files          476      482       +6     
  Lines        97613    98385     +772     
===========================================
+ Hits         76048    76758     +710     
- Misses       21565    21627      +62     
Flag Coverage Δ
inputsystem_MacOS_2022.3_project 61.85% <73.11%> (-13.55%) ⬇️
inputsystem_MacOS_6000.0_project 63.78% <73.54%> (-13.51%) ⬇️
inputsystem_MacOS_6000.3_project 63.77% <73.54%> (-13.52%) ⬇️
inputsystem_MacOS_6000.4_project 63.79% <73.54%> (-13.52%) ⬇️
inputsystem_MacOS_6000.5_project 63.80% <57.29%> (-13.51%) ⬇️
inputsystem_MacOS_6000.6_project 63.80% <57.29%> (-13.51%) ⬇️
inputsystem_Ubuntu_2022.3_project 61.20% <73.11%> (-14.00%) ⬇️
inputsystem_Ubuntu_6000.0_project 63.08% <73.54%> (-14.02%) ⬇️
inputsystem_Ubuntu_6000.3_project 63.07% <73.54%> (-14.04%) ⬇️
inputsystem_Ubuntu_6000.4_project 63.09% <73.54%> (-14.03%) ⬇️
inputsystem_Ubuntu_6000.5_project 63.09% <57.29%> (-14.02%) ⬇️
inputsystem_Ubuntu_6000.6_project 63.09% <57.29%> (-14.02%) ⬇️
inputsystem_Windows_2022.3_project 62.09% <73.11%> (-13.43%) ⬇️
inputsystem_Windows_6000.0_project 64.02% <73.54%> (-13.40%) ⬇️
inputsystem_Windows_6000.3_project 64.01% <73.54%> (-13.41%) ⬇️
inputsystem_Windows_6000.4_project 64.03% <73.54%> (-13.40%) ⬇️
inputsystem_Windows_6000.5_project 64.04% <57.29%> (-13.39%) ⬇️
inputsystem_Windows_6000.6_project 64.04% <57.29%> (-13.39%) ⬇️

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.53% <100.00%> (+0.21%) ⬆️
....inputsystem/Tests/TestFixture/InputTestRuntime.cs 90.80% <66.66%> (-0.56%) ⬇️
...nity.inputsystem/InputSystem/NativeInputRuntime.cs 53.69% <50.00%> (+0.63%) ⬆️
Assets/Tests/InputSystem/CoreTests_Actions.cs 98.22% <57.14%> (+0.07%) ⬆️
... and 5 more

... and 15 files with indirect coverage changes

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

@Pauliusd01 Pauliusd01 self-requested a review March 10, 2026 13:04
@Pauliusd01

This comment was marked as off-topic.

# Conflicts:
#	Packages/com.unity.inputsystem/InputSystem/InputManager.cs
#	Packages/com.unity.inputsystem/Tests/TestFixture/InputTestFixture.cs
@ekcoh
Copy link
Collaborator

ekcoh commented Mar 11, 2026

@VeraMommersteeg Looks like the PR currently has conflicts that are in need of being resolved:

  • InputManager.cs
  • InputTestFixture.cs

Likely due to CEPM/FEPM branch being merged.

@ekcoh
Copy link
Collaborator

ekcoh commented Mar 11, 2026

@VeraMommersteeg Noticed there are multiple test failures, I suspect you have already noticed and is looking into it?

@VeraMommersteeg
Copy link
Collaborator Author

Yes Im aware I have changes to fix it locally

Copy link
Collaborator

@ekcoh ekcoh left a comment

Choose a reason for hiding this comment

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

Just commenting for now as a half-way review. Need to clone this PR in order to have a realistic chance to provide relevant feedback on InputManager.cs diff. Looking at the diff alone is very complex.


// And make sure we're not seeing the data in the editor.
runtime.PlayerFocusLost();
ScheduleFocusEvent(false);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Minor: It is only semantics, by I personally think a test fixture helper like is should be named what it does instead of reflecting implementation details (that changing focus leads to scheduling an event). E.g. ChangeFocus(applicationHasFocus: false) is a lot clearer. The fact that this leads to a deferred change event is a detail. But the actual change happens when that line is executed.

Nitpick: When using trivial arguments like this, I think it's helpful to pass as named argument since false could be mean many things. It makes it a lot easier to read the code, especially in web-interface where there are no IDE smartness that highlights the name of the argument.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I only write this comment once even though it applies to all tests here, e.g. row 174 ScheduleFocusEvent(true) etc.

// will be Editor, which means that we will end up swapping buffers to the editor buffer, and retreiving the wrong active touch
// The only way to properly fix this, is to remove defaultUpdateType, and split the player/editor update loops into separate methods, which would be a breaking change.
// Until then, we have to make sure to schedule the correct update type here.
switch (updateMode)
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think it would be cleaner, and separate concerns, if the type transform from InputSettings.UpdateMode to InputUpdateType would happen in its own single-purpose (internal) extension method. This whole switch statement would then effectively be part of that static method and this would instead become:
InputSystem.Update(updateMode.ToInputUpdateType());

I suspect that conversion happens in multiple places so such a conversion is likely reusable to reduce code-bloat as well.


runtime.PlayerFocusLost();

// We now have to run a dynamic update before the press, to make sure the focus state is up to date.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Thanks for adding this comment, very helpful. A slight improvement could be to reference what you actually refer to by "pre-update". It is not currently clear exactly what this means. It might help developers circling back to this in the future.


var eventCount = 0;
InputSystem.onEvent += (eventPtr, _) => ++ eventCount;
InputSystem.onEvent += (eventPtr, _) =>
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nitpick: I have no problem with this approach or filtering at all. The first thing that strikes me though is that it sounds like the test is based on observing an exact number of events. This means that the test is very brittle and would brake down when any new (or user plugin injected) event is added. To avoid that, maybe it would make sense to instead only count the events relevant to the test to make it future proof? The system should be resilient to new events being added IMO. What do you think?

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

Choose a reason for hiding this comment

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

Makes a lot of sense IMO.

@@ -398,12 +431,13 @@ public bool runPlayerUpdatesInEditMode
#else
true;
#endif
private bool applicationHasFocus => (focusState & FocusFlags.ApplicationFocus) != 0;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nitpick: This is fine, but the C# standard way of such a check is typically (focusState & FocusFlags.ApplicationFocus) != FocusFlags.None. Happy to see you do not use flags extension since I learned it applies boxing under the hood.

k_InputAddDeviceMarker.End();
return null;
var device = AddDevice(layout, deviceId, deviceName, description, deviceFlags);
device.m_Description = description;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Not introduced by your PR but refactored, but isn't this device.m_Description = description; redundant? It seems to be passed in AddDevice on the line above? (I would assume it is the same)

None = 0,

/// <summary>
/// The application has focus.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Maybe we should make this comment more crisp (potentially both here and in module), to clarify that ApplicationFocus means "GameView focus" when running in the editor, see https://docs.unity3d.com/6000.3/Documentation/ScriptReference/MonoBehaviour.OnApplicationFocus.html. This could avoid confusion and strengthen the definition.

@@ -0,0 +1,267 @@
using System.Runtime.CompilerServices;
Copy link
Collaborator

Choose a reason for hiding this comment

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

The name of this class can be very confusing since it is similar to what Input Manager in Unity engine is often called. Since this is a partial class containing a selected set of functionality I would suggest renaming it to InputManager.LegacyFocusHandling.cs or similar instead.

Copy link
Collaborator

Choose a reason for hiding this comment

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

(But it make a lot of sense to extract this functionality into its own file)

get => m_FocusState;
set => m_FocusState = value;
}
public bool isPlayerFocused => (m_FocusState & FocusFlags.ApplicationFocus) != 0;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nitpick: Compare FocusFlags.None instead of 0?

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.

3 participants