Skip to content

Commit c335859

Browse files
NEW: Added Focus Events to Input Event Queue (#2365)
This branch changes the way we handle focus in input. focus changes in 6.5 onwards will now be handled through focus events. This will help us process input events more in line with when input sees that it has gained or lost focus
1 parent 734cfc9 commit c335859

19 files changed

+1345
-820
lines changed

Assets/Tests/InputSystem/CoreTests_Actions.cs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -631,7 +631,9 @@ public void Actions_DoNotGetTriggeredByEditorUpdates()
631631

632632
using (var trace = new InputActionTrace(action))
633633
{
634-
runtime.PlayerFocusLost();
634+
ScheduleFocusChangedEvent(applicationHasFocus: false);
635+
InputSystem.Update(InputUpdateType.Dynamic);
636+
635637
Set(gamepad.leftTrigger, 0.123f, queueEventOnly: true);
636638
InputSystem.Update(InputUpdateType.Editor);
637639

@@ -661,13 +663,13 @@ public void Actions_DoNotGetTriggeredByOutOfFocusEventInEditor(InputSettings.Bac
661663
// could just rely on order of event. Which means this test work for a fixed timestamp and it should
662664
// changed accordingly.
663665
currentTime += 1.0f;
664-
runtime.PlayerFocusLost();
666+
ScheduleFocusChangedEvent(applicationHasFocus: false);
665667
currentTime += 1.0f;
666668
// Queuing an event like it would be in the editor when the GameView is out of focus.
667669
Set(mouse.position, new Vector2(0.234f, 0.345f) , queueEventOnly: true);
668670
currentTime += 1.0f;
669671
// Gaining focus like it would happen in the editor when the GameView regains focus.
670-
runtime.PlayerFocusGained();
672+
ScheduleFocusChangedEvent(applicationHasFocus: true);
671673
currentTime += 1.0f;
672674
// This emulates a device sync that happens when the player regains focus through an IOCTL command.
673675
// That's why it also has it's time incremented.
@@ -720,14 +722,15 @@ public void Actions_TimeoutsDoNotGetTriggeredInEditorUpdates()
720722

721723
trace.Clear();
722724

723-
runtime.PlayerFocusLost();
725+
ScheduleFocusChangedEvent(applicationHasFocus: false);
726+
InputSystem.Update(InputUpdateType.Dynamic);
724727
currentTime = 10;
725728

726729
InputSystem.Update(InputUpdateType.Editor);
727730

728731
Assert.That(trace, Is.Empty);
729732

730-
runtime.PlayerFocusGained();
733+
ScheduleFocusChangedEvent(applicationHasFocus: true);
731734
InputSystem.Update(InputUpdateType.Dynamic);
732735

733736
actions = trace.ToArray();

Assets/Tests/InputSystem/CoreTests_Devices.cs

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
using UnityEngine.Scripting;
2020
using UnityEngine.TestTools;
2121
using UnityEngine.TestTools.Utils;
22+
using UnityEngineInternal.Input;
2223
using Gyroscope = UnityEngine.InputSystem.Gyroscope;
2324
using UnityEngine.TestTools.Constraints;
2425
using Is = NUnit.Framework.Is;
@@ -1522,7 +1523,7 @@ public void Devices_CanReconnectDevice_WhenDisconnectedWhileAppIsOutOfFocus()
15221523
Assert.That(device, Is.Not.Null);
15231524

15241525
// Loose focus.
1525-
runtime.PlayerFocusLost();
1526+
ScheduleFocusChangedEvent(applicationHasFocus: false);
15261527
InputSystem.Update();
15271528

15281529
// Disconnect.
@@ -1534,7 +1535,7 @@ public void Devices_CanReconnectDevice_WhenDisconnectedWhileAppIsOutOfFocus()
15341535
Assert.That(InputSystem.devices, Is.Empty);
15351536

15361537
// Regain focus.
1537-
runtime.PlayerFocusGained();
1538+
ScheduleFocusChangedEvent(applicationHasFocus: true);
15381539
InputSystem.Update();
15391540

15401541
var newDeviceId = runtime.ReportNewInputDevice(deviceDesc);
@@ -4604,7 +4605,13 @@ void DeviceChangeCallback(InputDevice device, InputDeviceChange change)
46044605
InputSystem.onDeviceChange += DeviceChangeCallback;
46054606

46064607
var eventCount = 0;
4607-
InputSystem.onEvent += (eventPtr, _) => ++ eventCount;
4608+
InputSystem.onEvent += (eventPtr, _) =>
4609+
{
4610+
// Focus events will always be processed no matter the state
4611+
// Since the test relies on counting events based on state, dont count focus events
4612+
if (eventPtr.data->type != (FourCC)FocusConstants.kEventType)
4613+
++eventCount;
4614+
};
46084615

46094616
Assert.That(trackedDevice.enabled, Is.True);
46104617
Assert.That(mouse.enabled, Is.True);
@@ -4647,7 +4654,8 @@ void DeviceChangeCallback(InputDevice device, InputDeviceChange change)
46474654
}
46484655

46494656
// Lose focus.
4650-
runtime.PlayerFocusLost();
4657+
ScheduleFocusChangedEvent(applicationHasFocus: false);
4658+
InputSystem.Update(InputUpdateType.Dynamic);
46514659

46524660
Assert.That(sensor.enabled, Is.False);
46534661
Assert.That(disabledDevice.enabled, Is.False);
@@ -5068,7 +5076,8 @@ void DeviceChangeCallback(InputDevice device, InputDeviceChange change)
50685076
commands.Clear();
50695077

50705078
// Regain focus.
5071-
runtime.PlayerFocusGained();
5079+
ScheduleFocusChangedEvent(applicationHasFocus: true);
5080+
InputSystem.Update(InputUpdateType.Dynamic);
50725081

50735082
Assert.That(sensor.enabled, Is.False);
50745083
Assert.That(disabledDevice.enabled, Is.False);
@@ -5275,13 +5284,10 @@ void DeviceChangeCallback(InputDevice device, InputDeviceChange change)
52755284
"Sync Gamepad", "Sync Joystick",
52765285
"Sync TrackedDevice", "Sync TrackedDevice2",
52775286
"Sync Mouse", "Sync Mouse2", "Sync Mouse3",
5278-
"Sync Keyboard", "Reset Joystick"
5279-
}));
5280-
// Enabled devices that don't support syncs get reset.
5281-
Assert.That(changes, Is.EquivalentTo(new[]
5282-
{
5283-
"SoftReset Mouse1", "SoftReset Mouse3", "HardReset Joystick", "SoftReset TrackedDevice2"
5287+
"Sync Keyboard"
52845288
}));
5289+
// Enabled devices that don't support syncs dont get reset for Ignore Focus as we do not want to cancel any actions.
5290+
Assert.That(changes, Is.Empty);
52855291
break;
52865292
}
52875293
}
@@ -5318,7 +5324,13 @@ public void Devices_CanSkipProcessingEventsWhileInBackground()
53185324
Assert.That(performedCount, Is.EqualTo(1));
53195325

53205326
// Lose focus
5321-
runtime.PlayerFocusLost();
5327+
ScheduleFocusChangedEvent(applicationHasFocus: false);
5328+
#if UNITY_INPUTSYSTEM_SUPPORTS_FOCUS_EVENTS
5329+
// in the new system, we have to process the focus event to update the state of the devices.
5330+
// In the old system, this wouldn't work and would make the test fal
5331+
InputSystem.Update();
5332+
#endif
5333+
53225334
Assert.That(gamepad.enabled, Is.False);
53235335

53245336
// Queue an event while in the background. We don't want to see this event to be processed once focus
@@ -5329,7 +5341,7 @@ public void Devices_CanSkipProcessingEventsWhileInBackground()
53295341
InputSystem.Update();
53305342

53315343
// Gain focus
5332-
runtime.PlayerFocusGained();
5344+
ScheduleFocusChangedEvent(applicationHasFocus: true);
53335345

53345346
// Run update to try process events accordingly once focus is gained
53355347
InputSystem.Update();

Assets/Tests/InputSystem/CoreTests_Editor.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2720,7 +2720,8 @@ public void Editor_CanForceKeyboardAndMouseInputToGameViewWithoutFocus()
27202720
var keyboard = InputSystem.AddDevice<Keyboard>();
27212721
var mouse = InputSystem.AddDevice<Mouse>();
27222722

2723-
runtime.PlayerFocusLost();
2723+
ScheduleFocusChangedEvent(applicationHasFocus: false);
2724+
InputSystem.Update(InputUpdateType.Dynamic);
27242725

27252726
Assert.That(keyboard.enabled, Is.True);
27262727
Assert.That(mouse.enabled, Is.True);
@@ -3016,7 +3017,8 @@ public void Editor_LeavingPlayMode_ReenablesAllDevicesTemporarilyDisabledDueToFo
30163017
Set(mouse.position, new Vector2(123, 234));
30173018
Press(gamepad.buttonSouth);
30183019

3019-
runtime.PlayerFocusLost();
3020+
ScheduleFocusChangedEvent(applicationHasFocus: false);
3021+
InputSystem.Update(InputUpdateType.Dynamic);
30203022

30213023
Assert.That(gamepad.enabled, Is.False);
30223024

Assets/Tests/InputSystem/CoreTests_State.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -704,7 +704,9 @@ public void State_CanSetUpMonitorsForStateChanges_InEditor()
704704
InputState.AddChangeMonitor(gamepad.leftStick,
705705
(control, time, eventPtr, monitorIndex) => monitorFired = true);
706706

707-
runtime.PlayerFocusLost();
707+
ScheduleFocusChangedEvent(applicationHasFocus: false);
708+
InputSystem.Update(InputUpdateType.Dynamic);
709+
708710
Set(gamepad.leftStick, new Vector2(0.123f, 0.234f), queueEventOnly: true);
709711
InputSystem.Update(InputUpdateType.Editor);
710712

@@ -1676,7 +1678,9 @@ public void State_RecordingHistory_ExcludesEditorInputByDefault()
16761678
{
16771679
history.StartRecording();
16781680

1679-
runtime.PlayerFocusLost();
1681+
ScheduleFocusChangedEvent(applicationHasFocus: false);
1682+
InputSystem.Update(InputUpdateType.Dynamic);
1683+
16801684
Set(gamepad.leftTrigger, 0.123f, queueEventOnly: true);
16811685
InputSystem.Update(InputUpdateType.Editor);
16821686

@@ -1696,7 +1700,9 @@ public void State_RecordingHistory_CanCaptureEditorInput()
16961700
history.updateMask = InputUpdateType.Editor;
16971701
history.StartRecording();
16981702

1699-
runtime.PlayerFocusLost();
1703+
ScheduleFocusChangedEvent(applicationHasFocus: false);
1704+
InputSystem.Update(InputUpdateType.Dynamic);
1705+
17001706
Set(gamepad.leftTrigger, 0.123f, queueEventOnly: true);
17011707
InputSystem.Update(InputUpdateType.Editor);
17021708

Assets/Tests/InputSystem/Plugins/EnhancedTouchTests.cs

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -158,11 +158,10 @@ public void EnhancedTouch_SupportsEditorUpdates(InputSettings.UpdateMode updateM
158158
Assert.That(Touch.activeTouches, Has.Count.EqualTo(1));
159159

160160
// And make sure we're not seeing the data in the editor.
161-
runtime.PlayerFocusLost();
161+
ScheduleFocusChangedEvent(applicationHasFocus: false);
162162
InputSystem.Update(InputUpdateType.Editor);
163163

164164
Assert.That(Touch.activeTouches, Is.Empty);
165-
166165
// Feed some data into editor state.
167166
BeginTouch(2, new Vector2(0.234f, 0.345f), queueEventOnly: true);
168167
InputSystem.Update(InputUpdateType.Editor);
@@ -171,8 +170,25 @@ public void EnhancedTouch_SupportsEditorUpdates(InputSettings.UpdateMode updateM
171170
Assert.That(Touch.activeTouches[0].touchId, Is.EqualTo(2));
172171

173172
// Switch back to player.
174-
runtime.PlayerFocusGained();
175-
InputSystem.Update();
173+
ScheduleFocusChangedEvent(applicationHasFocus: true);
174+
175+
// Explicitly schedule the player's configured update type rather than relying on the default.
176+
// Without explicit scheduling, defaultUpdateType would be Editor (since focus has not yet been
177+
// gained during update), causing the editor buffer to be used instead of the player buffer,
178+
// which would retrieve the wrong active touch. A proper fix would require removing defaultUpdateType
179+
// and splitting player/editor update loops into separate methods.
180+
switch (updateMode)
181+
{
182+
case InputSettings.UpdateMode.ProcessEventsInDynamicUpdate:
183+
InputSystem.Update(InputUpdateType.Dynamic);
184+
break;
185+
case InputSettings.UpdateMode.ProcessEventsInFixedUpdate:
186+
InputSystem.Update(InputUpdateType.Fixed);
187+
break;
188+
case InputSettings.UpdateMode.ProcessEventsManually:
189+
InputSystem.Update(InputUpdateType.Manual);
190+
break;
191+
}
176192

177193
Assert.That(Touch.activeTouches, Has.Count.EqualTo(1));
178194
Assert.That(Touch.activeTouches[0].touchId, Is.EqualTo(1));
@@ -1160,7 +1176,7 @@ public void EnhancedTouch_ActiveTouchesGetCanceledOnFocusLoss_WithRunInBackgroun
11601176
Assert.That(Touch.activeTouches, Has.Count.EqualTo(1));
11611177
Assert.That(Touch.activeTouches[0].phase, Is.EqualTo(TouchPhase.Began));
11621178

1163-
runtime.PlayerFocusLost();
1179+
ScheduleFocusChangedEvent(applicationHasFocus: false);
11641180

11651181
if (runInBackground)
11661182
{
@@ -1171,7 +1187,7 @@ public void EnhancedTouch_ActiveTouchesGetCanceledOnFocusLoss_WithRunInBackgroun
11711187
else
11721188
{
11731189
// When not running in the background, the same thing happens but only on focus gain.
1174-
runtime.PlayerFocusGained();
1190+
ScheduleFocusChangedEvent(applicationHasFocus: true);
11751191
InputSystem.Update();
11761192
}
11771193

Assets/Tests/InputSystem/Plugins/InputForUITests.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -715,19 +715,19 @@ public void UIActions_DoNotGetTriggeredByOutOfFocusEventInEditor(InputSettings.B
715715
currentTime += 1.0f;
716716
Update();
717717
currentTime += 1.0f;
718-
runtime.PlayerFocusLost();
718+
ScheduleFocusChangedEvent(applicationHasFocus: false);
719719
currentTime += 1.0f;
720720
Set(mouse.position, outOfFocusPosition , queueEventOnly: true);
721721
currentTime += 1.0f;
722-
runtime.PlayerFocusGained();
722+
ScheduleFocusChangedEvent(applicationHasFocus: true);
723723
currentTime += 1.0f;
724724
Set(mouse.position, focusPosition, queueEventOnly: true);
725725
currentTime += 1.0f;
726726

727727
// We call specific updates to simulate editor behavior when regaining focus.
728728
InputSystem.Update(InputUpdateType.Editor);
729729
Assert.AreEqual(0, m_InputForUIEvents.Count);
730-
InputSystem.Update();
730+
InputSystem.Update(InputUpdateType.Dynamic);
731731
// Calling the event provider update after we call InputSystem updates so that we trigger InputForUI events
732732
EventProvider.NotifyUpdate();
733733

Assets/Tests/InputSystem/Plugins/UITests.cs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4128,7 +4128,9 @@ public IEnumerator UI_WhenAppLosesAndRegainsFocus_WhileUIButtonIsPressed_UIButto
41284128

41294129
scene.leftChildReceiver.events.Clear();
41304130

4131-
runtime.PlayerFocusLost();
4131+
ScheduleFocusChangedEvent(applicationHasFocus: false);
4132+
InputSystem.Update(InputUpdateType.Dynamic);
4133+
41324134
if (canRunInBackground)
41334135
Assert.That(clickCanceled, Is.EqualTo(0));
41344136
else
@@ -4139,7 +4141,9 @@ public IEnumerator UI_WhenAppLosesAndRegainsFocus_WhileUIButtonIsPressed_UIButto
41394141
Assert.That(scene.eventSystem.hasFocus, Is.False);
41404142
Assert.That(clicked, Is.False);
41414143

4142-
runtime.PlayerFocusGained();
4144+
ScheduleFocusChangedEvent(applicationHasFocus: true);
4145+
InputSystem.Update(InputUpdateType.Dynamic);
4146+
41434147
scene.eventSystem.SendMessage("OnApplicationFocus", true);
41444148

41454149
yield return null;
@@ -4168,11 +4172,13 @@ public IEnumerator UI_WhenAppLosesAndRegainsFocus_WhileUIButtonIsPressed_UIButto
41684172

41694173
// Ensure that losing and regaining focus doesn't cause the next click to be ignored
41704174
clicked = false;
4171-
runtime.PlayerFocusLost();
4175+
ScheduleFocusChangedEvent(applicationHasFocus: false);
4176+
InputSystem.Update(InputUpdateType.Dynamic);
41724177
scene.eventSystem.SendMessage("OnApplicationFocus", false);
41734178
yield return null;
41744179

4175-
runtime.PlayerFocusGained();
4180+
ScheduleFocusChangedEvent(applicationHasFocus: true);
4181+
InputSystem.Update(InputUpdateType.Dynamic);
41764182
scene.eventSystem.SendMessage("OnApplicationFocus", true);
41774183
yield return null;
41784184

Assets/Tests/InputSystem/Plugins/UserTests.cs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1268,8 +1268,21 @@ public void Users_DoNotReactToEditorInput()
12681268
++InputUser.listenForUnpairedDeviceActivity;
12691269
InputUser.onUnpairedDeviceUsed += (control, eventPtr) => Assert.Fail("Should not react!");
12701270

1271-
runtime.PlayerFocusLost();
1272-
1271+
// Process the focus event before pressing the button to ensure correct update type selection.
1272+
//
1273+
// Issue: When Update() is called without an update type, it uses defaultUpdateType which checks
1274+
// focus state. However, scheduled focus events aren't processed until an update runs, so the
1275+
// focus check sees stale state and selects the wrong update type.
1276+
//
1277+
// Workaround: Run a dynamic update first to process the focus event, ensuring the subsequent
1278+
// button press correctly uses editor update type.
1279+
//
1280+
// Alternative: Queue the button press and explicitly call an editor update to process both events.
1281+
//
1282+
// Proper fix: Remove defaultUpdateType and split editor/player loops, or always specify the
1283+
// update type explicitly when calling Update().
1284+
ScheduleFocusChangedEvent(applicationHasFocus: false);
1285+
InputSystem.Update(InputUpdateType.Dynamic);
12731286
Press(gamepad.buttonSouth);
12741287

12751288
Assert.That(gamepad.buttonSouth.isPressed, Is.True);

Assets/Tests/InputSystem/Unity.InputSystem.Tests.asmdef

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,11 @@
7070
"name": "Unity",
7171
"expression": "6000.3.0a6",
7272
"define": "UNITY_INPUT_SYSTEM_PLATFORM_POLLING_FREQUENCY"
73+
},
74+
{
75+
"name": "Unity",
76+
"expression": "6000.5.0a8",
77+
"define": "UNITY_INPUTSYSTEM_SUPPORTS_FOCUS_EVENTS"
7378
}
7479
],
7580
"noEngineReferences": false

0 commit comments

Comments
 (0)