Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,12 @@ internal unsafe interface IInputRuntime
/// command sent to the device.</returns>
long DeviceCommand(int deviceId, InputDeviceCommand* commandPtr);

/// <summary>
/// Initialize the focus state based on current conditions of the application.
/// </summary>
/// <seealso cref="focusState"/>
void InitializeFocusState();

/// <summary>
/// Set delegate to be called on input updates.
/// </summary>
Expand Down Expand Up @@ -113,11 +119,15 @@ internal unsafe interface IInputRuntime
/// <summary>
/// Set delegate to call when the application changes focus.
/// </summary>
/// <seealso cref="Application.onFocusChanged"/>
/// <seealso cref="Application.focusChanged"/>
Action<bool> onPlayerFocusChanged { get; set; }
#endif

/// <summary>
/// Flags indicating various focus states for the application and editor.
/// </summary>
FocusFlags focusState { get; set; }

/// <summary>
/// Is true when the player or game view has focus.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,107 +22,120 @@
/// <summary>
/// In editor this means the GameView has focus. In a built player this means the player has focus.
/// </summary>
ApplicationFocus = (1 << 0)
ApplicationFocus = (1 << 0),
Comment thread
VeraMommersteeg marked this conversation as resolved.
};

internal partial class InputManager
{
internal void OnFocusChanged(bool focus)
{
// We set this to temporarily override applicationHasFocus before processing the focus change
// as defaultUpdateType is influenced by it. Before returning from this method, clear the
// bool to stop overriding to indicate this manager has finished processing the focus change.
m_IsHandlingFocusChange = true;
m_ApplicationHadFocus = !focus;
Comment thread
chris-massie marked this conversation as resolved.

#if UNITY_EDITOR
SyncAllDevicesWhenEditorIsActivated();
var shouldClearCurrentUpdateInFinally = false;
#endif

if (!m_Runtime.isInPlayMode)
try
{
focusState = focus ? FocusFlags.ApplicationFocus : FocusFlags.None;
return;
}
#if UNITY_EDITOR
SyncAllDevicesWhenEditorIsActivated();

if (!m_Runtime.isInPlayMode)
return;

Check warning on line 48 in Packages/com.unity.inputsystem/InputSystem/Runtime/InputManager.LegacyFocusHandling.cs

View check run for this annotation

Codecov GitHub.com / codecov/patch

Packages/com.unity.inputsystem/InputSystem/Runtime/InputManager.LegacyFocusHandling.cs#L48

Added line #L48 was not covered by tests

var gameViewFocus = m_Settings.editorInputBehaviorInPlayMode;
var gameViewFocus = m_Settings.editorInputBehaviorInPlayMode;
#endif

var runInBackground =
var runInBackground =
#if UNITY_EDITOR
// In the editor, the player loop will always be run even if the Game View does not have focus. This
// amounts to runInBackground being always true in the editor, regardless of what the setting in
// the Player Settings window is.
//
// If, however, "Game View Focus" is set to "Exactly As In Player", we force code here down the same
// path as in the player.
gameViewFocus != InputSettings.EditorInputBehaviorInPlayMode.AllDeviceInputAlwaysGoesToGameView || m_Runtime.runInBackground;
// In the editor, the player loop will always be run even if the Game View does not have focus. This
// amounts to runInBackground being always true in the editor, regardless of what the setting in
// the Player Settings window is.
//
// If, however, "Game View Focus" is set to "Exactly As In Player", we force code here down the same
// path as in the player.
gameViewFocus != InputSettings.EditorInputBehaviorInPlayMode.AllDeviceInputAlwaysGoesToGameView || m_Runtime.runInBackground;
#else
m_Runtime.runInBackground;
m_Runtime.runInBackground;
#endif

var backgroundBehavior = m_Settings.backgroundBehavior;
if (backgroundBehavior == InputSettings.BackgroundBehavior.IgnoreFocus && runInBackground)
{
// If runInBackground is true, no device changes should happen, even when focus is gained. So early out.
// If runInBackground is false, we still want to sync devices when focus is gained. So we need to continue further.
focusState = focus ? FocusFlags.ApplicationFocus : FocusFlags.None;
return;
}
if (m_Settings.backgroundBehavior == InputSettings.BackgroundBehavior.IgnoreFocus && runInBackground)
{
// If runInBackground is true, no device changes should happen, even when focus is gained. So early out.
// If runInBackground is false, we still want to sync devices when focus is gained. So we need to continue further.
return;
}

#if UNITY_EDITOR
// Set the current update type while we process the focus changes to make sure we
// feed into the right buffer. No need to do this in the player as it doesn't have
// the editor/player confusion.
m_CurrentUpdate = m_UpdateMask.GetUpdateTypeForPlayer();
// Set the current update type while we process the focus changes to make sure we
// feed into the right buffer. No need to do this in the player as it doesn't have
// the editor/player confusion.
m_CurrentUpdate = m_UpdateMask.GetUpdateTypeForPlayer();
shouldClearCurrentUpdateInFinally = true;
#endif

if (!focus)
{
// We only react to loss of focus when we will keep running in the background. If not,
// we'll do nothing and just wait for focus to come back (where we then try to sync all devices).
if (runInBackground)
if (!focus)
{
for (var i = 0; i < m_DevicesCount; ++i)
// We only react to loss of focus when we will keep running in the background. If not,
// we'll do nothing and just wait for focus to come back (where we then try to sync all devices).
if (runInBackground)
{
// Determine whether to run this device in the background.
var device = m_Devices[i];
if (!device.enabled || ShouldRunDeviceInBackground(device))
continue;
for (var i = 0; i < m_DevicesCount; ++i)
{
// Determine whether to run this device in the background.
var device = m_Devices[i];
if (!device.enabled || ShouldRunDeviceInBackground(device))
continue;

// Disable the device. This will also soft-reset it.
EnableOrDisableDevice(device, false, DeviceDisableScope.TemporaryWhilePlayerIsInBackground);
// Disable the device. This will also soft-reset it.
EnableOrDisableDevice(device, false, DeviceDisableScope.TemporaryWhilePlayerIsInBackground);

// In case we invoked a callback that messed with our device array, adjust our index.
var index = m_Devices.IndexOfReference(device, m_DevicesCount);
if (index == -1)
--i;
else
i = index;
// In case we invoked a callback that messed with our device array, adjust our index.
var index = m_Devices.IndexOfReference(device, m_DevicesCount);
if (index == -1)
--i;

Check warning on line 100 in Packages/com.unity.inputsystem/InputSystem/Runtime/InputManager.LegacyFocusHandling.cs

View check run for this annotation

Codecov GitHub.com / codecov/patch

Packages/com.unity.inputsystem/InputSystem/Runtime/InputManager.LegacyFocusHandling.cs#L100

Added line #L100 was not covered by tests
else
i = index;
}
}
}
}
else
{
m_DiscardOutOfFocusEvents = true;
m_FocusRegainedTime = m_Runtime.currentTime;
// On focus gain, reenable and sync devices.
for (var i = 0; i < m_DevicesCount; ++i)
else
{
var device = m_Devices[i];
m_DiscardOutOfFocusEvents = true;
m_FocusRegainedTime = m_Runtime.currentTime;

// On focus gain, reenable and sync devices.
for (var i = 0; i < m_DevicesCount; ++i)
{
var device = m_Devices[i];

// Re-enable the device if we disabled it on focus loss. This will also issue a sync.
if (device.disabledWhileInBackground)
EnableOrDisableDevice(device, true, DeviceDisableScope.TemporaryWhilePlayerIsInBackground);
// Try to sync. If it fails and we didn't run in the background, perform
// a reset instead. This is to cope with backends that are unable to sync but
// may still retain state which now may be outdated because the input device may
// have changed state while we weren't running. So at least make the backend flush
// its state (if any).
else if (device.enabled && !runInBackground && !device.RequestSync() && m_Settings.backgroundBehavior != InputSettings.BackgroundBehavior.IgnoreFocus)
ResetDevice(device);
// Re-enable the device if we disabled it on focus loss. This will also issue a sync.
if (device.disabledWhileInBackground)
EnableOrDisableDevice(device, true, DeviceDisableScope.TemporaryWhilePlayerIsInBackground);

// Try to sync. If it fails and we didn't run in the background, perform
// a reset instead. This is to cope with backends that are unable to sync but
// may still retain state which now may be outdated because the input device may
// have changed state while we weren't running. So at least make the backend flush
// its state (if any).
else if (device.enabled && !runInBackground && !device.RequestSync() && m_Settings.backgroundBehavior != InputSettings.BackgroundBehavior.IgnoreFocus)
ResetDevice(device);
}
}
}

finally
{
#if UNITY_EDITOR
m_CurrentUpdate = InputUpdateType.None;
if (shouldClearCurrentUpdateInFinally)
m_CurrentUpdate = InputUpdateType.None;
#endif

// We set this *after* the block above as defaultUpdateType is influenced by the setting.
focusState = focus ? FocusFlags.ApplicationFocus : FocusFlags.None;
m_IsHandlingFocusChange = false;
}
}

/// <summary>
Expand Down Expand Up @@ -150,8 +163,6 @@
/// <summary>
/// Determines if we should exit early from event processing without handling events.
/// </summary>
/// <param name="eventBuffer">The current event buffer</param>
/// <param name="canFlushBuffer">Whether the buffer can be flushed</param>
/// <param name="updateType">The current update type</param>
/// <returns>True if we should exit early, false otherwise.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
Expand Down
54 changes: 20 additions & 34 deletions Packages/com.unity.inputsystem/InputSystem/Runtime/InputManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@
// The solution here would be to make update calls explicitly specify the update type and no longer use this property.
if (!m_RunPlayerUpdatesInEditMode && (!gameIsPlaying || !gameHasFocus))
return InputUpdateType.Editor;
#endif
#endif

return m_UpdateMask.GetUpdateTypeForPlayer();
}
Expand All @@ -264,27 +264,6 @@
}
}

public FocusFlags focusState
{
get
{
#if UNITY_INPUTSYSTEM_SUPPORTS_FOCUS_EVENTS
return m_Runtime.focusState;
#else
return m_FocusState;
#endif
}
set
{
#if UNITY_INPUTSYSTEM_SUPPORTS_FOCUS_EVENTS
if (m_Runtime != null)
m_Runtime.focusState = value;
#else
m_FocusState = value;
#endif
}
}

public float pollingFrequency
{
get
Expand Down Expand Up @@ -557,7 +536,17 @@
#else
true;
#endif
private bool applicationHasFocus => (focusState & FocusFlags.ApplicationFocus) != FocusFlags.None;
private bool applicationHasFocus
{
get
{
#if !UNITY_INPUTSYSTEM_SUPPORTS_FOCUS_EVENTS
if (m_IsHandlingFocusChange)
return m_ApplicationHadFocus;

Check warning on line 545 in Packages/com.unity.inputsystem/InputSystem/Runtime/InputManager.cs

View check run for this annotation

Codecov GitHub.com / codecov/patch

Packages/com.unity.inputsystem/InputSystem/Runtime/InputManager.cs#L545

Added line #L545 was not covered by tests
#endif
return m_Runtime != null ? m_Runtime.isPlayerFocused : Application.isFocused;
}
}

private bool gameHasFocus =>
#if UNITY_EDITOR
Expand Down Expand Up @@ -2027,11 +2016,6 @@
// we don't know which one the user is going to use. The user
// can manually turn off one of them to optimize operation.
m_UpdateMask = InputUpdateType.Dynamic | InputUpdateType.Fixed;
#if !UNITY_INPUTSYSTEM_SUPPORTS_FOCUS_EVENTS
m_FocusState = Application.isFocused
? m_FocusState | FocusFlags.ApplicationFocus
: m_FocusState & ~FocusFlags.ApplicationFocus;
#endif
#if UNITY_EDITOR
m_EditorIsActive = true;
m_UpdateMask |= InputUpdateType.Editor;
Expand Down Expand Up @@ -2277,9 +2261,7 @@
#endif
m_Runtime.pollingFrequency = pollingFrequency;

focusState = m_Runtime.isPlayerFocused
? focusState | FocusFlags.ApplicationFocus
: focusState & ~FocusFlags.ApplicationFocus;
m_Runtime.InitializeFocusState();

// We only hook NativeInputSystem.onBeforeUpdate if necessary.
if (m_BeforeUpdateListeners.length > 0 || m_HaveDevicesWithStateCallbackReceivers)
Expand Down Expand Up @@ -2451,9 +2433,14 @@
private bool m_NativeBeforeUpdateHooked;
private bool m_HaveDevicesWithStateCallbackReceivers;
#if !UNITY_INPUTSYSTEM_SUPPORTS_FOCUS_EVENTS
private FocusFlags m_FocusState = FocusFlags.ApplicationFocus;
private bool m_DiscardOutOfFocusEvents;
private double m_FocusRegainedTime;
// These two bools are used for overriding applicationHasFocus which affects gameHasFocus
// and thus defaultUpdateType. See comments in defaultUpdateType. While processing the
// focus change in the OnFocusChanged method, these are used to temporarily override
// those properties.
private bool m_IsHandlingFocusChange;
private bool m_ApplicationHadFocus;
#endif
private InputEventStream m_InputEventStream;

Expand Down Expand Up @@ -3938,8 +3925,7 @@
private unsafe void ProcessFocusEvent(InputEvent* currentEventReadPtr)
{
var focusEventPtr = (InputFocusEvent*)currentEventReadPtr;
FocusFlags state = focusEventPtr->focusFlags;
focusState = state;
m_Runtime.focusState = focusEventPtr->focusFlags;

#if UNITY_EDITOR
SyncAllDevicesWhenEditorIsActivated();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -316,11 +316,22 @@ private bool OnWantsToShutdown()
return true;
}

public void InitializeFocusState()
{
m_FocusState = Application.isFocused
? m_FocusState | FocusFlags.ApplicationFocus
: m_FocusState & ~FocusFlags.ApplicationFocus;
}

#if !UNITY_INPUTSYSTEM_SUPPORTS_FOCUS_EVENTS
private Action<bool> m_FocusChangedMethod;

private void OnFocusChanged(bool focus)
{
m_FocusState = focus
? m_FocusState | FocusFlags.ApplicationFocus
: m_FocusState & ~FocusFlags.ApplicationFocus;

m_FocusChangedMethod(focus);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -234,11 +234,19 @@ public unsafe long DeviceCommand(int deviceId, InputDeviceCommand* commandPtr)
}
}

public void InitializeFocusState()
{
// Testing should start in focus and individual tests can change this test runtime to lose focus
// by calling InvokePlayerFocusChanged via InputTestFixture.ScheduleFocusChangedEvent.
m_FocusState = FocusFlags.ApplicationFocus;
}

public void InvokePlayerFocusChanged(bool newFocusState)
{
m_FocusState = newFocusState
? m_FocusState | FocusFlags.ApplicationFocus
: m_FocusState & ~FocusFlags.ApplicationFocus;

onPlayerFocusChanged?.Invoke(newFocusState);
}

Expand Down
Loading