Files
UnrealEngineUWP/Engine/Source/Runtime/Launch/Private/Android/AndroidEventManager.cpp
Matthew Griffin bb70b349ce Merging CL 2804086 from //UE4/Release-4.11 to Dev-Main (//UE4/Dev-Main) to isolate copyright update
#lockdown Nick.Penwarden

[CL 2819020 by Matthew Griffin in Main branch]
2016-01-07 08:17:16 -05:00

410 lines
9.5 KiB
C++

// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#include "LaunchPrivatePCH.h"
#include "AndroidEventManager.h"
#include "AndroidApplication.h"
#include "AudioDevice.h"
#include "CallbackDevice.h"
#include <android/native_window.h>
#include <android/native_window_jni.h>
#include "IHeadMountedDisplay.h"
DEFINE_LOG_CATEGORY(LogAndroidEvents);
FAppEventManager* FAppEventManager::sInstance = NULL;
FAppEventManager* FAppEventManager::GetInstance()
{
if(!sInstance)
{
sInstance = new FAppEventManager();
}
return sInstance;
}
void FAppEventManager::Tick()
{
bool bWindowCreatedThisTick = false;
while (!Queue.IsEmpty())
{
bool bDestroyWindow = false;
FAppEventData Event = DequeueAppEvent();
switch (Event.State)
{
case APP_EVENT_STATE_WINDOW_CREATED:
check(FirstInitialized);
bCreateWindow = true;
PendingWindow = (ANativeWindow*)Event.Data;
break;
case APP_EVENT_STATE_WINDOW_RESIZED:
case APP_EVENT_STATE_WINDOW_CHANGED:
// React on device orientation/windowSize changes only when application has window
// In case window was created this tick it should already has correct size
if (bHaveWindow && !bWindowCreatedThisTick)
{
ExecWindowResized();
}
break;
case APP_EVENT_STATE_SAVE_STATE:
bSaveState = true; //todo android: handle save state.
break;
case APP_EVENT_STATE_WINDOW_DESTROYED:
if (GEngine->HMDDevice.IsValid() && GEngine->HMDDevice->IsHMDConnected())
{
// delay the destruction until after the renderer teardown on GearVR
bDestroyWindow = true;
}
else
{
FAndroidAppEntry::DestroyWindow();
FPlatformMisc::SetHardwareWindow(NULL);
}
bHaveWindow = false;
break;
case APP_EVENT_STATE_ON_START:
//doing nothing here
break;
case APP_EVENT_STATE_ON_DESTROY:
if (FTaskGraphInterface::IsRunning())
{
FGraphEventRef WillTerminateTask = FFunctionGraphTask::CreateAndDispatchWhenReady([&]()
{
FCoreDelegates::ApplicationWillTerminateDelegate.Broadcast();
}, TStatId(), NULL, ENamedThreads::GameThread);
FTaskGraphInterface::Get().WaitUntilTaskCompletes(WillTerminateTask);
}
GIsRequestingExit = true; //destroy immediately. Game will shutdown.
break;
case APP_EVENT_STATE_ON_STOP:
bHaveGame = false;
break;
case APP_EVENT_STATE_ON_PAUSE:
bHaveGame = false;
break;
case APP_EVENT_STATE_ON_RESUME:
bHaveGame = true;
break;
// window focus events that follow their own hierarchy, and might or might not respect App main events hierarchy
case APP_EVENT_STATE_WINDOW_GAINED_FOCUS:
bWindowInFocus = true;
break;
case APP_EVENT_STATE_WINDOW_LOST_FOCUS:
bWindowInFocus = false;
break;
default:
UE_LOG(LogAndroidEvents, Display, TEXT("Application Event : %u not handled. "), Event.State);
}
if (bCreateWindow)
{
// wait until activity is in focus.
if (bWindowInFocus)
{
ExecWindowCreated();
bCreateWindow = false;
bHaveWindow = true;
bWindowCreatedThisTick = true;
}
}
if (!bRunning && bHaveWindow && bHaveGame)
{
ResumeRendering();
ResumeAudio();
// broadcast events after the rendering thread has resumed
if (FTaskGraphInterface::IsRunning())
{
FGraphEventRef EnterForegroundTask = FFunctionGraphTask::CreateAndDispatchWhenReady([&]()
{
FCoreDelegates::ApplicationHasEnteredForegroundDelegate.Broadcast();
}, TStatId(), NULL, ENamedThreads::GameThread);
FGraphEventRef ReactivateTask = FFunctionGraphTask::CreateAndDispatchWhenReady([&]()
{
FCoreDelegates::ApplicationHasReactivatedDelegate.Broadcast();
}, TStatId(), EnterForegroundTask, ENamedThreads::GameThread);
FTaskGraphInterface::Get().WaitUntilTaskCompletes(ReactivateTask);
}
bRunning = true;
}
else if (bRunning && (!bHaveWindow || !bHaveGame))
{
// broadcast events before rendering thread suspends
if (FTaskGraphInterface::IsRunning())
{
FGraphEventRef DeactivateTask = FFunctionGraphTask::CreateAndDispatchWhenReady([&]()
{
FCoreDelegates::ApplicationWillDeactivateDelegate.Broadcast();
}, TStatId(), NULL, ENamedThreads::GameThread);
FGraphEventRef EnterBackgroundTask = FFunctionGraphTask::CreateAndDispatchWhenReady([&]()
{
FCoreDelegates::ApplicationWillEnterBackgroundDelegate.Broadcast();
}, TStatId(), DeactivateTask, ENamedThreads::GameThread);
FTaskGraphInterface::Get().WaitUntilTaskCompletes(EnterBackgroundTask);
}
PauseRendering();
PauseAudio();
bRunning = false;
}
if (bDestroyWindow)
{
FAndroidAppEntry::DestroyWindow();
FPlatformMisc::SetHardwareWindow(NULL);
bDestroyWindow = false;
}
}
if (!bRunning && FirstInitialized)
{
EventHandlerEvent->Wait();
}
}
FAppEventManager::FAppEventManager():
FirstInitialized(false)
,bCreateWindow(false)
,bWindowInFocus(false)
,bSaveState(false)
,bAudioPaused(false)
,PendingWindow(NULL)
,bHaveWindow(false)
,bHaveGame(false)
,bRunning(false)
{
pthread_mutex_init(&MainMutex, NULL);
pthread_mutex_init(&QueueMutex, NULL);
}
void FAppEventManager::HandleWindowCreated(void* InWindow)
{
int rc = pthread_mutex_lock(&MainMutex);
check(rc == 0);
bool AlreadyInited = FirstInitialized;
rc = pthread_mutex_unlock(&MainMutex);
check(rc == 0);
// Make sure window will not be deleted until event is processed
// Window could be deleted by OS while event queue stuck at game start-up phase
FAndroidWindow::AcquireWindowRef((ANativeWindow*)InWindow);
if(AlreadyInited)
{
EnqueueAppEvent(APP_EVENT_STATE_WINDOW_CREATED, InWindow );
}
else
{
//This cannot wait until first tick.
rc = pthread_mutex_lock(&MainMutex);
check(rc == 0);
check(FPlatformMisc::GetHardwareWindow() == NULL);
FPlatformMisc::SetHardwareWindow(InWindow);
FirstInitialized = true;
rc = pthread_mutex_unlock(&MainMutex);
check(rc == 0);
EnqueueAppEvent(APP_EVENT_STATE_WINDOW_CREATED, InWindow );
}
}
void FAppEventManager::SetEventHandlerEvent(FEvent* InEventHandlerEvent)
{
EventHandlerEvent = InEventHandlerEvent;
}
void FAppEventManager::PauseRendering()
{
if(GUseThreadedRendering )
{
if (GIsThreadedRendering)
{
StopRenderingThread();
}
}
else
{
RHIReleaseThreadOwnership();
}
}
void FAppEventManager::ResumeRendering()
{
if( GUseThreadedRendering )
{
if (!GIsThreadedRendering)
{
StartRenderingThread();
}
}
else
{
RHIAcquireThreadOwnership();
}
}
void FAppEventManager::ExecWindowCreated()
{
UE_LOG(LogAndroidEvents, Display, TEXT("ExecWindowCreated"));
check(PendingWindow)
FPlatformMisc::SetHardwareWindow(PendingWindow);
// When application launched while device is in sleep mode SystemResolution could be set to opposite orientation values
// Force to update SystemResolution to current values whenever we create a new window
FPlatformRect ScreenRect = FAndroidWindow::GetScreenRect();
FSystemResolution::RequestResolutionChange(ScreenRect.Right, ScreenRect.Bottom, EWindowMode::Fullscreen);
FAndroidAppEntry::ReInitWindow();
// We hold this reference to ensure that window will not be deleted while game starting up
// release it when window is finally initialized
FAndroidWindow::ReleaseWindowRef(PendingWindow);
PendingWindow = nullptr;
FAndroidApplication::OnWindowSizeChanged();
}
void FAppEventManager::ExecWindowResized()
{
if (bRunning)
{
FlushRenderingCommands();
}
FAndroidWindow::InvalidateCachedScreenRect();
FAndroidAppEntry::ReInitWindow();
FAndroidApplication::OnWindowSizeChanged();
}
void FAppEventManager::PauseAudio()
{
bAudioPaused = true;
if (GEngine->GetMainAudioDevice())
{
GEngine->GetMainAudioDevice()->Suspend(false);
}
}
void FAppEventManager::ResumeAudio()
{
bAudioPaused = false;
if (GEngine->GetMainAudioDevice())
{
GEngine->GetMainAudioDevice()->Suspend(true);
}
}
void FAppEventManager::EnqueueAppEvent(EAppEventState InState, void* InData)
{
FAppEventData Event;
Event.State = InState;
Event.Data = InData;
int rc = pthread_mutex_lock(&QueueMutex);
check(rc == 0);
Queue.Enqueue(Event);
rc = pthread_mutex_unlock(&QueueMutex);
check(rc == 0);
FPlatformMisc::LowLevelOutputDebugStringf(TEXT("LogAndroidEvents: EnqueueAppEvent : %u, %u"), InState, (uintptr_t)InData);
}
FAppEventData FAppEventManager::DequeueAppEvent()
{
int rc = pthread_mutex_lock(&QueueMutex);
check(rc == 0);
FAppEventData OutData;
Queue.Dequeue( OutData );
rc = pthread_mutex_unlock(&QueueMutex);
check(rc == 0);
UE_LOG(LogAndroidEvents, Display, TEXT("DequeueAppEvent : %u, %u"), OutData.State, (uintptr_t)OutData.Data)
return OutData;
}
bool FAppEventManager::IsGamePaused()
{
return !bRunning;
}
bool FAppEventManager::WaitForEventInQueue(EAppEventState InState, double TimeoutSeconds)
{
bool FoundEvent = false;
double StopTime = FPlatformTime::Seconds() + TimeoutSeconds;
TQueue<FAppEventData, EQueueMode::Spsc> HoldingQueue;
while (!FoundEvent)
{
int rc = pthread_mutex_lock(&QueueMutex);
check(rc == 0);
// Copy the existing queue (and check for our event)
while (!Queue.IsEmpty())
{
FAppEventData OutData;
Queue.Dequeue(OutData);
if (OutData.State == InState)
FoundEvent = true;
HoldingQueue.Enqueue(OutData);
}
if (FoundEvent)
break;
// Time expired?
if (FPlatformTime::Seconds() > StopTime)
break;
// Unlock for new events and wait a bit before trying again
rc = pthread_mutex_unlock(&QueueMutex);
check(rc == 0);
FPlatformProcess::Sleep(0.01f);
}
// Add events back to queue from holding
while (!HoldingQueue.IsEmpty())
{
FAppEventData OutData;
HoldingQueue.Dequeue(OutData);
Queue.Enqueue(OutData);
}
int rc = pthread_mutex_unlock(&QueueMutex);
check(rc == 0);
return FoundEvent;
}