2014-12-07 19:09:38 -05:00
|
|
|
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
|
2014-03-14 14:13:41 -04:00
|
|
|
|
|
|
|
|
#include "LaunchPrivatePCH.h"
|
|
|
|
|
#include "AndroidEventManager.h"
|
|
|
|
|
#include "AndroidApplication.h"
|
|
|
|
|
#include "AudioDevice.h"
|
2014-12-05 14:08:07 -05:00
|
|
|
#include "CallbackDevice.h"
|
2014-03-14 14:13:41 -04:00
|
|
|
#include <android/native_window.h>
|
|
|
|
|
#include <android/native_window_jni.h>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
DEFINE_LOG_CATEGORY(LogAndroidEvents);
|
|
|
|
|
|
|
|
|
|
FAppEventManager* FAppEventManager::sInstance = NULL;
|
|
|
|
|
|
2014-09-21 20:35:48 -04:00
|
|
|
|
2014-03-14 14:13:41 -04:00
|
|
|
FAppEventManager* FAppEventManager::GetInstance()
|
|
|
|
|
{
|
|
|
|
|
if(!sInstance)
|
|
|
|
|
{
|
|
|
|
|
sInstance = new FAppEventManager();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return sInstance;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void FAppEventManager::Tick()
|
|
|
|
|
{
|
|
|
|
|
while (!Queue.IsEmpty())
|
|
|
|
|
{
|
2015-01-09 19:49:36 -05:00
|
|
|
bool bDestroyWindow = false;
|
|
|
|
|
|
2014-03-14 14:13:41 -04:00
|
|
|
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:
|
|
|
|
|
bWindowChanged = true;
|
|
|
|
|
break;
|
|
|
|
|
case APP_EVENT_STATE_WINDOW_CHANGED:
|
|
|
|
|
bWindowChanged = true;
|
|
|
|
|
break;
|
|
|
|
|
case APP_EVENT_STATE_SAVE_STATE:
|
|
|
|
|
bSaveState = true; //todo android: handle save state.
|
|
|
|
|
break;
|
|
|
|
|
case APP_EVENT_STATE_WINDOW_DESTROYED:
|
2015-01-09 19:49:36 -05:00
|
|
|
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);
|
|
|
|
|
}
|
2014-03-14 14:13:41 -04:00
|
|
|
bHaveWindow = false;
|
|
|
|
|
break;
|
|
|
|
|
case APP_EVENT_STATE_ON_START:
|
|
|
|
|
//doing nothing here
|
|
|
|
|
break;
|
|
|
|
|
case APP_EVENT_STATE_ON_DESTROY:
|
2015-04-10 18:20:14 -04:00
|
|
|
if (FTaskGraphInterface::IsRunning())
|
|
|
|
|
{
|
|
|
|
|
FGraphEventRef WillTerminateTask = FFunctionGraphTask::CreateAndDispatchWhenReady([&]()
|
|
|
|
|
{
|
|
|
|
|
FCoreDelegates::ApplicationWillTerminateDelegate.Broadcast();
|
|
|
|
|
}, TStatId(), NULL, ENamedThreads::GameThread);
|
|
|
|
|
FTaskGraphInterface::Get().WaitUntilTaskCompletes(WillTerminateTask);
|
|
|
|
|
}
|
2014-03-14 14:13:41 -04:00
|
|
|
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 heirarchy, and might or might not respect App main events heirarchy
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (bWindowChanged)
|
|
|
|
|
{
|
|
|
|
|
// wait until window is valid and created.
|
|
|
|
|
if(FPlatformMisc::GetHardwareWindow() != NULL)
|
|
|
|
|
{
|
|
|
|
|
if(GEngine && GEngine->GameViewport && GEngine->GameViewport->ViewportFrame)
|
|
|
|
|
{
|
|
|
|
|
ExecWindowChanged();
|
|
|
|
|
bWindowChanged = false;
|
|
|
|
|
bHaveWindow = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!bRunning && bHaveWindow && bHaveGame)
|
|
|
|
|
{
|
|
|
|
|
ResumeRendering();
|
|
|
|
|
ResumeAudio();
|
2014-12-16 20:04:46 -05:00
|
|
|
|
|
|
|
|
// broadcast events after the rendering thread has resumed
|
2015-04-10 18:20:14 -04:00
|
|
|
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);
|
|
|
|
|
}
|
2014-12-16 20:04:46 -05:00
|
|
|
|
2014-03-14 14:13:41 -04:00
|
|
|
bRunning = true;
|
|
|
|
|
}
|
|
|
|
|
else if (bRunning && (!bHaveWindow || !bHaveGame))
|
|
|
|
|
{
|
2015-04-10 18:20:14 -04:00
|
|
|
// 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);
|
|
|
|
|
}
|
2014-12-16 20:04:46 -05:00
|
|
|
|
2014-03-14 14:13:41 -04:00
|
|
|
PauseRendering();
|
|
|
|
|
PauseAudio();
|
2014-12-16 20:04:46 -05:00
|
|
|
|
2014-03-14 14:13:41 -04:00
|
|
|
bRunning = false;
|
|
|
|
|
}
|
2015-01-09 19:49:36 -05:00
|
|
|
|
|
|
|
|
if (bDestroyWindow)
|
|
|
|
|
{
|
|
|
|
|
FAndroidAppEntry::DestroyWindow();
|
|
|
|
|
FPlatformMisc::SetHardwareWindow(NULL);
|
|
|
|
|
bDestroyWindow = false;
|
|
|
|
|
}
|
2014-03-14 14:13:41 -04:00
|
|
|
}
|
2014-06-27 15:48:23 -04:00
|
|
|
|
|
|
|
|
if (!bRunning && FirstInitialized)
|
|
|
|
|
{
|
|
|
|
|
EventHandlerEvent->Wait();
|
|
|
|
|
}
|
2014-03-14 14:13:41 -04:00
|
|
|
}
|
|
|
|
|
|
2014-09-21 20:35:48 -04:00
|
|
|
|
2014-03-14 14:13:41 -04:00
|
|
|
FAppEventManager::FAppEventManager():
|
|
|
|
|
FirstInitialized(false)
|
|
|
|
|
,bCreateWindow(false)
|
|
|
|
|
,bWindowChanged(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);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-21 20:35:48 -04:00
|
|
|
|
2014-03-14 14:13:41 -04:00
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
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);
|
2014-05-22 09:08:32 -04:00
|
|
|
|
|
|
|
|
EnqueueAppEvent(APP_EVENT_STATE_WINDOW_CREATED, InWindow );
|
2014-03-14 14:13:41 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-21 20:35:48 -04:00
|
|
|
|
2014-06-27 15:48:23 -04:00
|
|
|
void FAppEventManager::SetEventHandlerEvent(FEvent* InEventHandlerEvent)
|
|
|
|
|
{
|
|
|
|
|
EventHandlerEvent = InEventHandlerEvent;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-21 20:35:48 -04:00
|
|
|
|
2014-03-14 14:13:41 -04:00
|
|
|
void FAppEventManager::PauseRendering()
|
|
|
|
|
{
|
|
|
|
|
if(GUseThreadedRendering )
|
|
|
|
|
{
|
|
|
|
|
if (GIsThreadedRendering)
|
|
|
|
|
{
|
|
|
|
|
StopRenderingThread();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
RHIReleaseThreadOwnership();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-21 20:35:48 -04:00
|
|
|
|
2014-03-14 14:13:41 -04:00
|
|
|
void FAppEventManager::ResumeRendering()
|
|
|
|
|
{
|
|
|
|
|
if( GUseThreadedRendering )
|
|
|
|
|
{
|
|
|
|
|
if (!GIsThreadedRendering)
|
|
|
|
|
{
|
|
|
|
|
StartRenderingThread();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
RHIAcquireThreadOwnership();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-21 20:35:48 -04:00
|
|
|
|
2014-03-14 14:13:41 -04:00
|
|
|
void FAppEventManager::ExecWindowCreated()
|
|
|
|
|
{
|
|
|
|
|
UE_LOG(LogAndroidEvents, Display, TEXT("ExecWindowCreated"));
|
|
|
|
|
check(PendingWindow)
|
|
|
|
|
|
|
|
|
|
FPlatformMisc::SetHardwareWindow(PendingWindow);
|
|
|
|
|
PendingWindow = NULL;
|
|
|
|
|
|
|
|
|
|
FAndroidAppEntry::ReInitWindow();
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-21 20:35:48 -04:00
|
|
|
|
2014-03-14 14:13:41 -04:00
|
|
|
void FAppEventManager::ExecWindowChanged()
|
|
|
|
|
{
|
|
|
|
|
FPlatformRect ScreenRect = FAndroidWindow::GetScreenRect();
|
|
|
|
|
UE_LOG(LogAndroidEvents, Display, TEXT("ExecWindowChanged : width: %u, height: %u"), ScreenRect.Right, ScreenRect.Bottom);
|
|
|
|
|
|
|
|
|
|
if(GEngine && GEngine->GameViewport && GEngine->GameViewport->ViewportFrame)
|
|
|
|
|
{
|
2014-04-23 19:29:53 -04:00
|
|
|
GEngine->GameViewport->ViewportFrame->ResizeFrame(ScreenRect.Right, ScreenRect.Bottom, EWindowMode::Fullscreen);
|
2014-03-14 14:13:41 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-21 20:35:48 -04:00
|
|
|
|
2014-03-14 14:13:41 -04:00
|
|
|
void FAppEventManager::PauseAudio()
|
|
|
|
|
{
|
|
|
|
|
bAudioPaused = true;
|
|
|
|
|
|
2015-03-24 17:06:02 -04:00
|
|
|
if (GEngine->GetMainAudioDevice())
|
2014-03-14 14:13:41 -04:00
|
|
|
{
|
2015-03-24 17:06:02 -04:00
|
|
|
GEngine->GetMainAudioDevice()->Suspend(false);
|
2014-03-14 14:13:41 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-21 20:35:48 -04:00
|
|
|
|
2014-03-14 14:13:41 -04:00
|
|
|
void FAppEventManager::ResumeAudio()
|
|
|
|
|
{
|
|
|
|
|
bAudioPaused = false;
|
2014-04-23 17:38:52 -04:00
|
|
|
|
2015-03-24 17:06:02 -04:00
|
|
|
if (GEngine->GetMainAudioDevice())
|
2014-04-23 17:38:52 -04:00
|
|
|
{
|
2015-03-24 17:06:02 -04:00
|
|
|
GEngine->GetMainAudioDevice()->Suspend(true);
|
2014-04-23 17:38:52 -04:00
|
|
|
}
|
2014-03-14 14:13:41 -04:00
|
|
|
}
|
|
|
|
|
|
2014-09-21 20:35:48 -04:00
|
|
|
|
2014-03-14 14:13:41 -04:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-21 20:35:48 -04:00
|
|
|
|
2014-03-14 14:13:41 -04:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-21 20:35:48 -04:00
|
|
|
|
2014-03-14 14:13:41 -04:00
|
|
|
bool FAppEventManager::IsGamePaused()
|
|
|
|
|
{
|
|
|
|
|
return !bRunning;
|
|
|
|
|
}
|
2015-01-19 18:26:22 -05:00
|
|
|
|
2015-01-27 18:30:20 -05:00
|
|
|
bool FAppEventManager::WaitForEventInQueue(EAppEventState InState, double TimeoutSeconds)
|
2015-01-19 18:26:22 -05:00
|
|
|
{
|
|
|
|
|
bool FoundEvent = false;
|
2015-01-27 18:30:20 -05:00
|
|
|
double StopTime = FPlatformTime::Seconds() + TimeoutSeconds;
|
2015-01-19 18:26:22 -05:00
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
2015-01-27 18:30:20 -05:00
|
|
|
// Time expired?
|
|
|
|
|
if (FPlatformTime::Seconds() > StopTime)
|
|
|
|
|
break;
|
|
|
|
|
|
2015-01-19 18:26:22 -05:00
|
|
|
// 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);
|
2015-01-27 18:30:20 -05:00
|
|
|
|
|
|
|
|
return FoundEvent;
|
2015-01-19 18:26:22 -05:00
|
|
|
}
|