Behavior When Unfocused

I use UE4 for non-game purposes at work, often alongside other applications on Windows.

A common pattern is creating the display part with UE4 and a controller-like part with WPF, then using them together.

In this pattern, the focus inevitably goes to the WPF application, leaving the UE4 window unfocused.

This caused the following problems:

  • Sound cannot be heard
  • GamePad input cannot be captured

I couldn’t find solutions for these for a while, but after checking the Engine source, I found countermeasures for both, so I’m writing them down as a memo.

Note that the environment where I applied these measures was 4.24, but since the implementation in that part seems unchanged in 4.25, I believe the same measures should work (though I haven’t confirmed the operation in 4.25).

Sound Playback When Unfocused

I found the following description in WindowsPlatformApplicationMisc::PumpMessages:

// if its our window, allow sound, otherwise apply multiplier
FApp::SetVolumeMultiplier( HasFocus ? 1.0f : FApp::GetUnfocusedVolumeMultiplier() );

It seems SetVolumeMultiplier is a function to set the sound volume. Here, it uses 1.0 if focused, and the value of GetUnfocusedVolumeMultiplier otherwise.

Checking the value returned by GetUnfocusedVolumeMultiplier, it was 0. That’s why I couldn’t hear anything.

However, I found that the initial value of this is taken from the Config.

static bool GUnfocusedVolumeMultiplierInitialised = false;
float FApp::GetUnfocusedVolumeMultiplier()
{
    if (!GUnfocusedVolumeMultiplierInitialised)
    {
        GUnfocusedVolumeMultiplierInitialised = true;
        GConfig->GetFloat(TEXT("Audio"), TEXT("UnfocusedVolumeMultiplier"), UnfocusedVolumeMultiplier, GEngineIni);
    }
    return UnfocusedVolumeMultiplier;

So, I wrote the following setting in Config/DefaultEngine.ini to make the value of UnfocusedVolumeMultiplier also 1.0 and confirmed the operation.

[Audio]
UnfocusedVolumeMultiplier=1.0

Success! Sound can now be heard even when unfocused.

GamePad Input When Unfocused

In UE4, GamePad input on Windows uses an API called XInput.

Searching from there, I found a class called XInputInterface. Checking its contents, it calls the XInput API, so it’s highly likely that this is where GamePad input is received.

Next, searching for where XInputInterface is used, I found it’s used in FWindowsApplication.

Then, it seems FWindowsApplication::PollGameDeviceState fires the XInput input events. Furthermore, this function is called from FSlateApplication::PollGameDeviceState().

Looking inside FSlateApplication::PollGameDeviceState(), it’s guarded by an if statement.

if( ActiveModalWindows.Num() == 0 && !GIntraFrameDebuggingGameThread && (!bRequireFocusForGamepadInput || IsActive()))
{
    // Don't poll when a modal window open or intra frame debugging is happening
    PlatformApplication->PollGameDeviceState( GetDeltaTime() );
}

There are three conditions here, and the last one, IsActive(), feels like it means having focus. However, there’s a variable here, bRequireFocusForGamepadInput, which seems directly related.

Checking its definition, it looked like the value could be changed via a console command.

static bool bRequireFocusForGamepadInput = false;
FAutoConsoleVariableRef CVarRequireFocusForGamepadInput(
    TEXT("Slate.RequireFocusForGamepadInput"),
    bRequireFocusForGamepadInput,
    TEXT("")
);

For my work purposes, I use a development build, so I tried entering the above command using Ctrl+@, and I was able to get GamePad input even when unfocused.

However, typing it every time is tedious (calling it from BluePrint is an option, but I’d rather not if possible).

Doing a bit more research, it seems console commands can also be defined in an ini file, so I decided to go with that approach.

[UE4] Special Nature of ConsoleVariables.ini - Qiita

Summary

When a UE4 application loses focus, various things become impossible, but I managed to adjust it to behave as desired.

Being able to see the engine source makes this kind of investigation easier, which is really great!