Refactor GDK platform to be base for Xbox consoles
This commit is contained in:
@@ -117,9 +117,6 @@ bool GraphicsService::Init()
|
|||||||
// Platform default
|
// Platform default
|
||||||
if (!device)
|
if (!device)
|
||||||
{
|
{
|
||||||
#if PLATFORM_WINDOWS
|
|
||||||
|
|
||||||
// Windows
|
|
||||||
#if GRAPHICS_API_DIRECTX11
|
#if GRAPHICS_API_DIRECTX11
|
||||||
if (!device)
|
if (!device)
|
||||||
device = CreateGPUDeviceDX11();
|
device = CreateGPUDeviceDX11();
|
||||||
@@ -132,65 +129,9 @@ bool GraphicsService::Init()
|
|||||||
if (!device)
|
if (!device)
|
||||||
device = CreateGPUDeviceVulkan();
|
device = CreateGPUDeviceVulkan();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#elif PLATFORM_UWP
|
|
||||||
|
|
||||||
// UWP
|
|
||||||
if (!device)
|
|
||||||
device = CreateGPUDeviceDX11();
|
|
||||||
|
|
||||||
#elif PLATFORM_LINUX
|
|
||||||
|
|
||||||
// Linux
|
|
||||||
#if GRAPHICS_API_VULKAN
|
|
||||||
if (!device)
|
|
||||||
device = CreateGPUDeviceVulkan();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#elif PLATFORM_PS4
|
|
||||||
|
|
||||||
// PS4
|
|
||||||
#if GRAPHICS_API_PS4
|
#if GRAPHICS_API_PS4
|
||||||
if (!device)
|
if (!device)
|
||||||
device = CreateGPUDevicePS4();
|
device = CreateGPUDevicePS4();
|
||||||
#endif
|
|
||||||
|
|
||||||
#elif PLATFORM_XBOX_ONE
|
|
||||||
|
|
||||||
// Xbox One
|
|
||||||
#if GRAPHICS_API_DIRECTX12
|
|
||||||
if (!device)
|
|
||||||
device = CreateGPUDeviceDX12();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#elif PLATFORM_XBOX_SCARLETT
|
|
||||||
|
|
||||||
// Xbox Scarlett
|
|
||||||
#if GRAPHICS_API_DIRECTX12
|
|
||||||
if (!device)
|
|
||||||
device = CreateGPUDeviceDX12();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#elif PLATFORM_ANDROID
|
|
||||||
|
|
||||||
// Android
|
|
||||||
#if GRAPHICS_API_VULKAN
|
|
||||||
if (!device)
|
|
||||||
device = CreateGPUDeviceVulkan();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#elif PLATFORM_SWITCH
|
|
||||||
|
|
||||||
// Switch
|
|
||||||
#if GRAPHICS_API_VULKAN
|
|
||||||
if (!device)
|
|
||||||
device = CreateGPUDeviceVulkan();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#elif !defined(GRAPHICS_API_NULL)
|
|
||||||
|
|
||||||
#error "Platform does not support GPU devices."
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -198,20 +198,6 @@ GPUDeviceDX12::GPUDeviceDX12(IDXGIFactory4* dxgiFactory, GPUAdapterDX* adapter)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
#if PLATFORM_XBOX_SCARLETT
|
|
||||||
namespace XboxScarlett
|
|
||||||
{
|
|
||||||
extern Action OnSuspend;
|
|
||||||
extern Action OnResume;
|
|
||||||
}
|
|
||||||
#elif PLATFORM_XBOX_ONE
|
|
||||||
namespace XboxOne
|
|
||||||
{
|
|
||||||
extern Action OnSuspend;
|
|
||||||
extern Action OnResume;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
bool GPUDeviceDX12::Init()
|
bool GPUDeviceDX12::Init()
|
||||||
{
|
{
|
||||||
#if PLATFORM_XBOX_SCARLETT || PLATFORM_XBOX_ONE
|
#if PLATFORM_XBOX_SCARLETT || PLATFORM_XBOX_ONE
|
||||||
@@ -264,12 +250,9 @@ bool GPUDeviceDX12::Init()
|
|||||||
LOG(Info, "Hardware Version: {0}", hwVer);
|
LOG(Info, "Hardware Version: {0}", hwVer);
|
||||||
updateFrameEvents();
|
updateFrameEvents();
|
||||||
|
|
||||||
#if PLATFORM_XBOX_SCARLETT
|
#if PLATFORM_GDK
|
||||||
XboxScarlett::OnSuspend.Bind<GPUDeviceDX12, &GPUDeviceDX12::OnSuspend>(this);
|
GDKPlatform::OnSuspend.Bind<GPUDeviceDX12, &GPUDeviceDX12::OnSuspend>(this);
|
||||||
XboxScarlett::OnResume.Bind<GPUDeviceDX12, &GPUDeviceDX12::OnResume>(this);
|
GDKPlatform::OnResume.Bind<GPUDeviceDX12, &GPUDeviceDX12::OnResume>(this);
|
||||||
#elif PLATFORM_XBOX_ONE
|
|
||||||
XboxOne::OnSuspend.Bind<GPUDeviceDX12, &GPUDeviceDX12::OnSuspend>(this);
|
|
||||||
XboxOne::OnResume.Bind<GPUDeviceDX12, &GPUDeviceDX12::OnResume>(this);
|
|
||||||
#endif
|
#endif
|
||||||
#else
|
#else
|
||||||
// Get DXGI adapter
|
// Get DXGI adapter
|
||||||
|
|||||||
253
Source/Engine/Platform/GDK/GDKInput.cpp
Normal file
253
Source/Engine/Platform/GDK/GDKInput.cpp
Normal file
@@ -0,0 +1,253 @@
|
|||||||
|
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
#if PLATFORM_XBOX_ONE || PLATFORM_XBOX_SCARLETT
|
||||||
|
|
||||||
|
#include "GDKInput.h"
|
||||||
|
#include "Engine/Input/Input.h"
|
||||||
|
#include "Engine/Input/Gamepad.h"
|
||||||
|
#include "Engine/Platform/Win32/IncludeWindowsHeaders.h"
|
||||||
|
#include <GameInput.h>
|
||||||
|
|
||||||
|
static_assert(sizeof(Guid) <= sizeof(APP_LOCAL_DEVICE_ID), "Invalid Game Input deviceId size.");
|
||||||
|
|
||||||
|
String ToString(GameInputString const* name)
|
||||||
|
{
|
||||||
|
return name && name->data ? String(name->data) : String::Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Implementation of the gamepad device for GDK platform.
|
||||||
|
/// </summary>
|
||||||
|
/// <seealso cref="Gamepad" />
|
||||||
|
class GDKGamepad : public Gamepad
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="GDKGamepad"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="device">The input device.</param>
|
||||||
|
explicit GDKGamepad(IGameInputDevice* device)
|
||||||
|
: Gamepad(*(Guid*)&device->GetDeviceInfo()->deviceId, ::ToString(device->GetDeviceInfo()->displayName))
|
||||||
|
, Device(device)
|
||||||
|
{
|
||||||
|
Device->AddRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finalizes an instance of the <see cref="GDKGamepad"/> class.
|
||||||
|
/// </summary>
|
||||||
|
~GDKGamepad()
|
||||||
|
{
|
||||||
|
Device->Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The device.
|
||||||
|
/// </summary>
|
||||||
|
IGameInputDevice* Device;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
// [Gamepad]
|
||||||
|
void SetVibration(const GamepadVibrationState& state) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
// [Gamepad]
|
||||||
|
bool UpdateState() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace GDKInputImpl
|
||||||
|
{
|
||||||
|
IGameInput* GameInput = nullptr;
|
||||||
|
IGameInputReading* PrevReading = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
using namespace GDKInputImpl;
|
||||||
|
|
||||||
|
bool IsSameDevice(const APP_LOCAL_DEVICE_ID& first, const APP_LOCAL_DEVICE_ID& second)
|
||||||
|
{
|
||||||
|
return memcmp(&first, &second, APP_LOCAL_DEVICE_ID_SIZE) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsSameDevice(IGameInputDevice* first, IGameInputDevice* second)
|
||||||
|
{
|
||||||
|
return IsSameDevice(first->GetDeviceInfo()->deviceId, second->GetDeviceInfo()->deviceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TryAddGamepad(IGameInputDevice* device)
|
||||||
|
{
|
||||||
|
for (auto& gamepad : Input::Gamepads)
|
||||||
|
{
|
||||||
|
if (IsSameDevice(((GDKGamepad*)gamepad)->Device, device))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Input::Gamepads.Add(New<GDKGamepad>(device));
|
||||||
|
Input::OnGamepadsChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CALLBACK OnDeviceEnumerated(
|
||||||
|
_In_ GameInputCallbackToken callbackToken,
|
||||||
|
_In_ void* context,
|
||||||
|
_In_ IGameInputDevice* device,
|
||||||
|
_In_ uint64_t timestamp,
|
||||||
|
_In_ GameInputDeviceStatus currentStatus,
|
||||||
|
_In_ GameInputDeviceStatus previousStatus)
|
||||||
|
{
|
||||||
|
TryAddGamepad(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GDKInput::Init()
|
||||||
|
{
|
||||||
|
GameInputCreate(&GameInput);
|
||||||
|
|
||||||
|
// Find connected devices
|
||||||
|
GameInputCallbackToken token;
|
||||||
|
if (SUCCEEDED(GameInput->RegisterDeviceCallback(
|
||||||
|
nullptr,
|
||||||
|
GameInputKindGamepad,
|
||||||
|
GameInputDeviceAnyStatus,
|
||||||
|
GameInputBlockingEnumeration,
|
||||||
|
nullptr,
|
||||||
|
OnDeviceEnumerated,
|
||||||
|
&token)))
|
||||||
|
{
|
||||||
|
GameInput->UnregisterCallback(token, 5000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GDKInput::Exit()
|
||||||
|
{
|
||||||
|
if (PrevReading)
|
||||||
|
{
|
||||||
|
PrevReading->Release();
|
||||||
|
PrevReading = nullptr;
|
||||||
|
}
|
||||||
|
if (GameInput)
|
||||||
|
{
|
||||||
|
GameInput->Release();
|
||||||
|
GameInput = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GDKInput::Update()
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
// Read input
|
||||||
|
IGameInputReading* reading = nullptr;
|
||||||
|
if (!PrevReading)
|
||||||
|
{
|
||||||
|
if (FAILED(GDKInputImpl::GameInput->GetCurrentReading(GameInputKindGamepad, nullptr, &reading)))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
PrevReading = reading;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const HRESULT hr = GameInput->GetNextReading(PrevReading, GameInputKindGamepad, nullptr, &reading);
|
||||||
|
if (SUCCEEDED(hr))
|
||||||
|
{
|
||||||
|
PrevReading->Release();
|
||||||
|
PrevReading = reading;
|
||||||
|
}
|
||||||
|
else if (hr != GAMEINPUT_E_READING_NOT_FOUND)
|
||||||
|
{
|
||||||
|
PrevReading->Release();
|
||||||
|
PrevReading = nullptr;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!reading)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Check if new device was connected
|
||||||
|
IGameInputDevice* device;
|
||||||
|
reading->GetDevice(&device);
|
||||||
|
TryAddGamepad(device);
|
||||||
|
device->Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GDKGamepad::SetVibration(const GamepadVibrationState& state)
|
||||||
|
{
|
||||||
|
GameInputRumbleParams vibration;
|
||||||
|
vibration.lowFrequency = state.LeftSmall;
|
||||||
|
vibration.highFrequency = state.RightSmall;
|
||||||
|
vibration.leftTrigger = state.LeftLarge;
|
||||||
|
vibration.rightTrigger = state.RightLarge;
|
||||||
|
Device->SetRumbleState(&vibration);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GDKGamepad::UpdateState()
|
||||||
|
{
|
||||||
|
// Gather device state
|
||||||
|
IGameInputReading* reading = nullptr;
|
||||||
|
if (FAILED(GDKInputImpl::GameInput->GetCurrentReading(GameInputKindGamepad,Device, &reading)))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
GameInputGamepadState state;
|
||||||
|
if (!reading->GetGamepadState(&state))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const float deadZone = 0.2f;
|
||||||
|
|
||||||
|
// Process buttons state
|
||||||
|
_state.Buttons[(int32)GamepadButton::A] = state.buttons & GameInputGamepadA;
|
||||||
|
_state.Buttons[(int32)GamepadButton::B] = state.buttons & GameInputGamepadB;
|
||||||
|
_state.Buttons[(int32)GamepadButton::X] = state.buttons & GameInputGamepadX;
|
||||||
|
_state.Buttons[(int32)GamepadButton::Y] = state.buttons & GameInputGamepadY;
|
||||||
|
_state.Buttons[(int32)GamepadButton::LeftShoulder] = state.buttons & GameInputGamepadLeftShoulder;
|
||||||
|
_state.Buttons[(int32)GamepadButton::RightShoulder] = state.buttons & GameInputGamepadRightShoulder;
|
||||||
|
_state.Buttons[(int32)GamepadButton::Back] = state.buttons & GameInputGamepadMenu;
|
||||||
|
_state.Buttons[(int32)GamepadButton::Start] = state.buttons & GameInputGamepadView;
|
||||||
|
_state.Buttons[(int32)GamepadButton::LeftThumb] = state.buttons & GameInputGamepadLeftThumbstick;
|
||||||
|
_state.Buttons[(int32)GamepadButton::RightThumb] = state.buttons & GameInputGamepadRightThumbstick;
|
||||||
|
_state.Buttons[(int32)GamepadButton::LeftTrigger] = state.leftTrigger > deadZone;
|
||||||
|
_state.Buttons[(int32)GamepadButton::RightTrigger] = state.rightTrigger > deadZone;
|
||||||
|
_state.Buttons[(int32)GamepadButton::DPadUp] = state.buttons & GameInputGamepadDPadUp;
|
||||||
|
_state.Buttons[(int32)GamepadButton::DPadDown] = state.buttons & GameInputGamepadDPadDown;
|
||||||
|
_state.Buttons[(int32)GamepadButton::DPadLeft] = state.buttons & GameInputGamepadDPadLeft;
|
||||||
|
_state.Buttons[(int32)GamepadButton::DPadRight] = state.buttons & GameInputGamepadDPadRight;
|
||||||
|
_state.Buttons[(int32)GamepadButton::LeftStickUp] = state.leftThumbstickY > deadZone;
|
||||||
|
_state.Buttons[(int32)GamepadButton::LeftStickDown] = state.leftThumbstickY < -deadZone;
|
||||||
|
_state.Buttons[(int32)GamepadButton::LeftStickLeft] = state.leftThumbstickX < -deadZone;
|
||||||
|
_state.Buttons[(int32)GamepadButton::LeftStickRight] = state.leftThumbstickX > deadZone;
|
||||||
|
_state.Buttons[(int32)GamepadButton::RightStickUp] = state.rightThumbstickY > deadZone;
|
||||||
|
_state.Buttons[(int32)GamepadButton::RightStickDown] = state.rightThumbstickY < -deadZone;
|
||||||
|
_state.Buttons[(int32)GamepadButton::RightStickLeft] = state.rightThumbstickX < -deadZone;
|
||||||
|
_state.Buttons[(int32)GamepadButton::RightStickRight] = state.rightThumbstickX > deadZone;
|
||||||
|
|
||||||
|
// Process axes state
|
||||||
|
_state.Axis[(int32)GamepadAxis::LeftStickX] = state.leftThumbstickX;
|
||||||
|
_state.Axis[(int32)GamepadAxis::LeftStickY] = state.leftThumbstickY;
|
||||||
|
_state.Axis[(int32)GamepadAxis::RightStickX] = state.rightThumbstickX;
|
||||||
|
_state.Axis[(int32)GamepadAxis::RightStickY] = state.rightThumbstickY;
|
||||||
|
_state.Axis[(int32)GamepadAxis::LeftTrigger] = state.leftTrigger;
|
||||||
|
_state.Axis[(int32)GamepadAxis::RightTrigger] = state.rightTrigger;
|
||||||
|
|
||||||
|
reading->Release();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
void GDKInput::Init()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void GDKInput::Exit()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void GDKInput::Update()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
19
Source/Engine/Platform/GDK/GDKInput.h
Normal file
19
Source/Engine/Platform/GDK/GDKInput.h
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if PLATFORM_GDK
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// GDK platform specific implementation of the input system parts.
|
||||||
|
/// </summary>
|
||||||
|
class GDKInput
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
static void Init();
|
||||||
|
static void Exit();
|
||||||
|
static void Update();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
631
Source/Engine/Platform/GDK/GDKPlatform.cpp
Normal file
631
Source/Engine/Platform/GDK/GDKPlatform.cpp
Normal file
@@ -0,0 +1,631 @@
|
|||||||
|
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
#if PLATFORM_GDK
|
||||||
|
|
||||||
|
#include "Engine/Platform/Platform.h"
|
||||||
|
#include "Engine/Platform/Window.h"
|
||||||
|
#include "Engine/Platform/CreateWindowSettings.h"
|
||||||
|
#include "Engine/Platform/WindowsManager.h"
|
||||||
|
#include "Engine/Platform/MemoryStats.h"
|
||||||
|
#include "Engine/Platform/BatteryInfo.h"
|
||||||
|
#include "Engine/Profiler/ProfilerCPU.h"
|
||||||
|
#include "Engine/Core/Log.h"
|
||||||
|
#include "Engine/Core/Collections/Array.h"
|
||||||
|
#include "Engine/Core/Collections/Dictionary.h"
|
||||||
|
#include "Engine/Platform/MessageBox.h"
|
||||||
|
#include "Engine/Engine/Engine.h"
|
||||||
|
#include "Engine/Utilities/StringConverter.h"
|
||||||
|
#include "Engine/Platform/Win32/IncludeWindowsHeaders.h"
|
||||||
|
#include "GDKInput.h"
|
||||||
|
#include <XGameRuntime.h>
|
||||||
|
#include <appnotify.h>
|
||||||
|
|
||||||
|
inline bool operator==(const APP_LOCAL_DEVICE_ID& l, const APP_LOCAL_DEVICE_ID& r)
|
||||||
|
{
|
||||||
|
return Platform::MemoryCompare(&l, &r, sizeof(APP_LOCAL_DEVICE_ID)) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Char* GDKPlatform::ApplicationWindowClass = TEXT("FlaxWindow");
|
||||||
|
void* GDKPlatform::Instance = nullptr;
|
||||||
|
Delegate<> GDKPlatform::OnSuspend;
|
||||||
|
Delegate<> GDKPlatform::OnResume;
|
||||||
|
|
||||||
|
struct User
|
||||||
|
{
|
||||||
|
XUserHandle UserHandle;
|
||||||
|
XUserLocalId LocalId;
|
||||||
|
Array<APP_LOCAL_DEVICE_ID, FixedAllocation<32>> AssociatedDevices;
|
||||||
|
|
||||||
|
void Set(XUserHandle userHandle, XUserLocalId localId)
|
||||||
|
{
|
||||||
|
UserHandle = userHandle;
|
||||||
|
LocalId = localId;
|
||||||
|
AssociatedDevices.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Unset()
|
||||||
|
{
|
||||||
|
XUserCloseHandle(UserHandle);
|
||||||
|
AssociatedDevices.Clear();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
bool IsSuspended = false;
|
||||||
|
HANDLE PlmSuspendComplete = nullptr;
|
||||||
|
HANDLE PlmSignalResume = nullptr;
|
||||||
|
PAPPSTATE_REGISTRATION Plm = {};
|
||||||
|
String UserLocale, ComputerName;
|
||||||
|
XTaskQueueHandle TaskQueue = nullptr;
|
||||||
|
Array<User, FixedAllocation<8>> Users;
|
||||||
|
XTaskQueueRegistrationToken UserChangeEventCallbackToken;
|
||||||
|
XTaskQueueRegistrationToken UserDeviceAssociationChangedCallbackToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||||||
|
{
|
||||||
|
switch (msg)
|
||||||
|
{
|
||||||
|
case WM_USER:
|
||||||
|
{
|
||||||
|
LOG(Info, "Suspending application");
|
||||||
|
IsSuspended = true;
|
||||||
|
GDKPlatform::OnSuspend();
|
||||||
|
|
||||||
|
// Complete deferral
|
||||||
|
SetEvent(PlmSuspendComplete);
|
||||||
|
|
||||||
|
(void)WaitForSingleObject(PlmSignalResume, INFINITE);
|
||||||
|
|
||||||
|
IsSuspended = false;
|
||||||
|
LOG(Info, "Resuming application");
|
||||||
|
GDKPlatform::OnResume();
|
||||||
|
return DefWindowProc(hWnd, msg, wParam, lParam);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find window to process that message
|
||||||
|
if (hWnd != nullptr)
|
||||||
|
{
|
||||||
|
// Find window by handle
|
||||||
|
const auto win = WindowsManager::GetByNativePtr(hWnd);
|
||||||
|
if (win)
|
||||||
|
{
|
||||||
|
return static_cast<GDKWindow*>(win)->WndProc(msg, wParam, lParam);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default
|
||||||
|
return DefWindowProc(hWnd, msg, wParam, lParam);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CALLBACK UserChangeEventCallback(_In_opt_ void* context,_In_ XUserLocalId userLocalId, _In_ XUserChangeEvent event)
|
||||||
|
{
|
||||||
|
LOG(Info, "User event (userLocalId: {0}, event: {1})", userLocalId.value, (int32)event);
|
||||||
|
|
||||||
|
switch (event)
|
||||||
|
{
|
||||||
|
case XUserChangeEvent::SignedInAgain:
|
||||||
|
break;
|
||||||
|
case XUserChangeEvent::SignedOut:
|
||||||
|
for (int32 i = 0; i < Users.Count(); i++)
|
||||||
|
{
|
||||||
|
if (Users[i].LocalId.value == userLocalId.value)
|
||||||
|
{
|
||||||
|
Users[i].Unset();
|
||||||
|
Users.RemoveAt(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default: ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String ToString(const APP_LOCAL_DEVICE_ID& deviceId)
|
||||||
|
{
|
||||||
|
return String::Format(TEXT("{}-{}-{}-{}-{}-{}-{}-{}"),
|
||||||
|
*reinterpret_cast<const unsigned int*>(&deviceId.value[0]),
|
||||||
|
*reinterpret_cast<const unsigned int*>(&deviceId.value[4]),
|
||||||
|
*reinterpret_cast<const unsigned int*>(&deviceId.value[8]),
|
||||||
|
*reinterpret_cast<const unsigned int*>(&deviceId.value[12]),
|
||||||
|
*reinterpret_cast<const unsigned int*>(&deviceId.value[16]),
|
||||||
|
*reinterpret_cast<const unsigned int*>(&deviceId.value[20]),
|
||||||
|
*reinterpret_cast<const unsigned int*>(&deviceId.value[24]),
|
||||||
|
*reinterpret_cast<const unsigned int*>(&deviceId.value[28]));
|
||||||
|
}
|
||||||
|
|
||||||
|
User* FindUser(const XUserLocalId& id)
|
||||||
|
{
|
||||||
|
User* result = nullptr;
|
||||||
|
for (auto& user : Users)
|
||||||
|
{
|
||||||
|
if (user.LocalId.value == id.value)
|
||||||
|
{
|
||||||
|
result = &user;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CALLBACK UserDeviceAssociationChangedCallback(_In_opt_ void* context,_In_ const XUserDeviceAssociationChange* change)
|
||||||
|
{
|
||||||
|
LOG(Info, "User device association event (deviceId: {0}, oldUser: {1}, newUser: {2})", ToString(change->deviceId), change->oldUser.value, change->newUser.value);
|
||||||
|
|
||||||
|
User* oldGameUser = FindUser(change->oldUser);
|
||||||
|
if (oldGameUser)
|
||||||
|
{
|
||||||
|
oldGameUser->AssociatedDevices.Remove(change->deviceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
User* newGameUser = FindUser(change->newUser);
|
||||||
|
if (newGameUser)
|
||||||
|
{
|
||||||
|
newGameUser->AssociatedDevices.Add(change->deviceId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnMainWindowCreated(HWND hWnd)
|
||||||
|
{
|
||||||
|
// Register for app suspending/resuming events
|
||||||
|
PlmSuspendComplete = CreateEventEx(nullptr, nullptr, 0, EVENT_MODIFY_STATE | SYNCHRONIZE);
|
||||||
|
PlmSignalResume = CreateEventEx(nullptr, nullptr, 0, EVENT_MODIFY_STATE | SYNCHRONIZE);
|
||||||
|
if (!PlmSuspendComplete || !PlmSignalResume)
|
||||||
|
return;
|
||||||
|
if (RegisterAppStateChangeNotification([](BOOLEAN quiesced, PVOID context)
|
||||||
|
{
|
||||||
|
if (quiesced)
|
||||||
|
{
|
||||||
|
ResetEvent(PlmSuspendComplete);
|
||||||
|
ResetEvent(PlmSignalResume);
|
||||||
|
|
||||||
|
// To ensure we use the main UI thread to process the notification, we self-post a message
|
||||||
|
PostMessage(reinterpret_cast<HWND>(context), WM_USER, 0, 0);
|
||||||
|
|
||||||
|
// To defer suspend, you must wait to exit this callback
|
||||||
|
(void)WaitForSingleObject(PlmSuspendComplete, INFINITE);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SetEvent(PlmSignalResume);
|
||||||
|
}
|
||||||
|
}, hWnd, &Plm))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CALLBACK AddUserComplete(_In_ XAsyncBlock* ab)
|
||||||
|
{
|
||||||
|
XUserHandle userHandle;
|
||||||
|
HRESULT hr = XUserAddResult(ab, &userHandle);
|
||||||
|
if (SUCCEEDED(hr))
|
||||||
|
{
|
||||||
|
XUserLocalId userLocalId;
|
||||||
|
XUserGetLocalId(userHandle, &userLocalId);
|
||||||
|
|
||||||
|
XUserLocalId localId;
|
||||||
|
XUserGetLocalId(userHandle, &localId);
|
||||||
|
|
||||||
|
if (FindUser(localId) == nullptr)
|
||||||
|
{
|
||||||
|
// Add user
|
||||||
|
auto& user = Users.AddOne();
|
||||||
|
user.Set(userHandle, userLocalId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete ab;
|
||||||
|
}
|
||||||
|
|
||||||
|
DialogResult MessageBox::Show(Window* parent, const StringView& text, const StringView& caption, MessageBoxButtons buttons, MessageBoxIcon icon)
|
||||||
|
{
|
||||||
|
const char *firstButtonText, *secondButtonText, *thirdButtonText;
|
||||||
|
XGameUiMessageDialogButton defaultButton, cancelButton = XGameUiMessageDialogButton::First;
|
||||||
|
switch (buttons)
|
||||||
|
{
|
||||||
|
case MessageBoxButtons::AbortRetryIgnore:
|
||||||
|
firstButtonText = "Abort";
|
||||||
|
secondButtonText = "Retry";
|
||||||
|
thirdButtonText = "Ignore";
|
||||||
|
defaultButton = XGameUiMessageDialogButton::Second;
|
||||||
|
cancelButton = XGameUiMessageDialogButton::Third;
|
||||||
|
break;
|
||||||
|
case MessageBoxButtons::OK:
|
||||||
|
firstButtonText = "OK";
|
||||||
|
secondButtonText = nullptr;
|
||||||
|
thirdButtonText = nullptr;
|
||||||
|
defaultButton = XGameUiMessageDialogButton::First;
|
||||||
|
cancelButton = XGameUiMessageDialogButton::First;
|
||||||
|
break;
|
||||||
|
case MessageBoxButtons::OKCancel:
|
||||||
|
firstButtonText = "OK";
|
||||||
|
secondButtonText = "Cancel";
|
||||||
|
thirdButtonText = nullptr;
|
||||||
|
defaultButton = XGameUiMessageDialogButton::First;
|
||||||
|
cancelButton = XGameUiMessageDialogButton::Second;
|
||||||
|
break;
|
||||||
|
case MessageBoxButtons::RetryCancel:
|
||||||
|
firstButtonText = "Retry";
|
||||||
|
secondButtonText = "Cancel";
|
||||||
|
thirdButtonText = nullptr;
|
||||||
|
defaultButton = XGameUiMessageDialogButton::First;
|
||||||
|
cancelButton = XGameUiMessageDialogButton::Second;
|
||||||
|
break;
|
||||||
|
case MessageBoxButtons::YesNo:
|
||||||
|
firstButtonText = "Yes";
|
||||||
|
secondButtonText = "No";
|
||||||
|
thirdButtonText = nullptr;
|
||||||
|
defaultButton = XGameUiMessageDialogButton::First;
|
||||||
|
cancelButton = XGameUiMessageDialogButton::Second;
|
||||||
|
break;
|
||||||
|
case MessageBoxButtons::YesNoCancel:
|
||||||
|
firstButtonText = "Yes";
|
||||||
|
secondButtonText = "No";
|
||||||
|
thirdButtonText = "Cancel";
|
||||||
|
defaultButton = XGameUiMessageDialogButton::First;
|
||||||
|
cancelButton = XGameUiMessageDialogButton::Third;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return DialogResult::None;
|
||||||
|
}
|
||||||
|
const StringAsANSI<> textAnsi(text.Get(), text.Length());
|
||||||
|
const StringAsANSI<> captionAnsi(caption.Get(), caption.Length());
|
||||||
|
|
||||||
|
// Show dialog and wait for the result
|
||||||
|
DialogResult result = DialogResult::None;
|
||||||
|
XTaskQueueHandle queue;
|
||||||
|
if (FAILED(XTaskQueueCreate(XTaskQueueDispatchMode::ThreadPool, XTaskQueueDispatchMode::Immediate, &queue)))
|
||||||
|
{
|
||||||
|
return DialogResult::None;
|
||||||
|
}
|
||||||
|
XAsyncBlock* ab = new XAsyncBlock();
|
||||||
|
Platform::MemoryClear(ab, sizeof(XAsyncBlock));
|
||||||
|
ab->queue = queue;
|
||||||
|
XGameUiMessageDialogButton button;
|
||||||
|
if (SUCCEEDED(XGameUiShowMessageDialogAsync(ab, captionAnsi.Get(), textAnsi.Get(), firstButtonText, secondButtonText, thirdButtonText, defaultButton, cancelButton)) &&
|
||||||
|
SUCCEEDED(XAsyncGetStatus(ab, true)) &&
|
||||||
|
SUCCEEDED(XGameUiShowMessageDialogResult(ab, &button)))
|
||||||
|
{
|
||||||
|
switch (buttons)
|
||||||
|
{
|
||||||
|
case MessageBoxButtons::AbortRetryIgnore:
|
||||||
|
result = button == XGameUiMessageDialogButton::First ? DialogResult::Abort : button == XGameUiMessageDialogButton::Second ? DialogResult::Retry : DialogResult::Ignore;
|
||||||
|
break;
|
||||||
|
case MessageBoxButtons::OK:
|
||||||
|
result = DialogResult::OK;
|
||||||
|
break;
|
||||||
|
case MessageBoxButtons::OKCancel:
|
||||||
|
result = button == XGameUiMessageDialogButton::First ? DialogResult::OK : DialogResult::Cancel;
|
||||||
|
break;
|
||||||
|
case MessageBoxButtons::RetryCancel:
|
||||||
|
result = button == XGameUiMessageDialogButton::First ? DialogResult::Retry : DialogResult::Cancel;
|
||||||
|
break;
|
||||||
|
case MessageBoxButtons::YesNo:
|
||||||
|
result = button == XGameUiMessageDialogButton::First ? DialogResult::Yes : DialogResult::No;
|
||||||
|
break;
|
||||||
|
case MessageBoxButtons::YesNoCancel:
|
||||||
|
result = button == XGameUiMessageDialogButton::First ? DialogResult::Yes : button == XGameUiMessageDialogButton::Second ? DialogResult::No : DialogResult::Cancel;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
XTaskQueueTerminate(queue, true, nullptr, nullptr);
|
||||||
|
delete ab;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GDKPlatform::PreInit(void* hInstance)
|
||||||
|
{
|
||||||
|
ASSERT(hInstance);
|
||||||
|
Instance = hInstance;
|
||||||
|
|
||||||
|
// Initialize the Game Runtime APIs
|
||||||
|
if (FAILED(XGameRuntimeInitialize()))
|
||||||
|
{
|
||||||
|
Error(TEXT("Game runtime initialization failed!"));
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register window class
|
||||||
|
WNDCLASSW windowsClass;
|
||||||
|
Platform::MemoryClear(&windowsClass, sizeof(WNDCLASS));
|
||||||
|
windowsClass.style = CS_HREDRAW | CS_VREDRAW;
|
||||||
|
windowsClass.lpfnWndProc = WndProc;
|
||||||
|
windowsClass.hInstance = (HINSTANCE)Instance;
|
||||||
|
windowsClass.lpszClassName = ApplicationWindowClass;
|
||||||
|
if (!RegisterClassW(&windowsClass))
|
||||||
|
{
|
||||||
|
Error(TEXT("Window class registration failed!"));
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GDKPlatform::IsRunningOnDevKit()
|
||||||
|
{
|
||||||
|
const XSystemDeviceType deviceType = XSystemGetDeviceType();
|
||||||
|
return deviceType == XSystemDeviceType::XboxOneXDevkit || deviceType == XSystemDeviceType::XboxScarlettDevkit;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GDKPlatform::Init()
|
||||||
|
{
|
||||||
|
if (Win32Platform::Init())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
DWORD tmp;
|
||||||
|
Char buffer[256];
|
||||||
|
|
||||||
|
// Get user locale string
|
||||||
|
if (GetUserDefaultLocaleName(buffer, LOCALE_NAME_MAX_LENGTH))
|
||||||
|
{
|
||||||
|
UserLocale = String(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get computer name string
|
||||||
|
if (GetComputerNameW(buffer, &tmp))
|
||||||
|
{
|
||||||
|
ComputerName = String(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a task queue that will process in the background on system threads and fire callbacks on a thread we choose in a serialized order
|
||||||
|
if (FAILED(XTaskQueueCreate(XTaskQueueDispatchMode::ThreadPool, XTaskQueueDispatchMode::Manual, &TaskQueue)))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Register for any change events for user
|
||||||
|
XUserRegisterForChangeEvent(
|
||||||
|
TaskQueue,
|
||||||
|
nullptr,
|
||||||
|
&UserChangeEventCallback,
|
||||||
|
&UserChangeEventCallbackToken
|
||||||
|
);
|
||||||
|
|
||||||
|
// Registers for any change to device association so that the application can keep up-to-date information about users and their associated devices
|
||||||
|
XUserRegisterForDeviceAssociationChanged(
|
||||||
|
TaskQueue,
|
||||||
|
nullptr,
|
||||||
|
&UserDeviceAssociationChangedCallback,
|
||||||
|
&UserDeviceAssociationChangedCallbackToken
|
||||||
|
);
|
||||||
|
|
||||||
|
// Login the default user
|
||||||
|
{
|
||||||
|
auto asyncBlock = new XAsyncBlock();
|
||||||
|
asyncBlock->queue = TaskQueue;
|
||||||
|
asyncBlock->callback = AddUserComplete;
|
||||||
|
HRESULT hr = XUserAddAsync(XUserAddOptions::AddDefaultUserAllowingUI, asyncBlock);
|
||||||
|
if (FAILED(hr))
|
||||||
|
delete asyncBlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
GDKInput::Init();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GDKPlatform::BeforeRun()
|
||||||
|
{
|
||||||
|
// Log system info
|
||||||
|
const XSystemAnalyticsInfo analyticsInfo = XSystemGetAnalyticsInfo();
|
||||||
|
LOG(Info, "{0}, {1}", StringAsUTF16<64>(analyticsInfo.family).Get(), StringAsUTF16<64>(analyticsInfo.form).Get());
|
||||||
|
LOG(Info, "OS Version {0}.{1}.{2}.{3}", analyticsInfo.osVersion.major, analyticsInfo.osVersion.minor, analyticsInfo.osVersion.build, analyticsInfo.osVersion.revision);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GDKPlatform::Tick()
|
||||||
|
{
|
||||||
|
PROFILE_CPU_NAMED("Application.Tick");
|
||||||
|
|
||||||
|
GDKInput::Update();
|
||||||
|
|
||||||
|
// Handle callbacks in the main thread to ensure thread safety
|
||||||
|
while (XTaskQueueDispatch(TaskQueue, XTaskQueuePort::Completion, 0))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check to see if any messages are waiting in the queue
|
||||||
|
MSG msg;
|
||||||
|
while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
|
||||||
|
{
|
||||||
|
// Translate the message and dispatch it to WindowProc()
|
||||||
|
TranslateMessage(&msg);
|
||||||
|
DispatchMessage(&msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GDKPlatform::BeforeExit()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void GDKPlatform::Exit()
|
||||||
|
{
|
||||||
|
GDKInput::Exit();
|
||||||
|
|
||||||
|
XUserUnregisterForDeviceAssociationChanged(UserDeviceAssociationChangedCallbackToken, false);
|
||||||
|
XUserUnregisterForChangeEvent(UserChangeEventCallbackToken, false);
|
||||||
|
if (TaskQueue)
|
||||||
|
{
|
||||||
|
XTaskQueueCloseHandle(TaskQueue);
|
||||||
|
}
|
||||||
|
|
||||||
|
UnregisterAppStateChangeNotification(Plm);
|
||||||
|
|
||||||
|
CloseHandle(PlmSuspendComplete);
|
||||||
|
CloseHandle(PlmSignalResume);
|
||||||
|
|
||||||
|
UnregisterClassW(ApplicationWindowClass, nullptr);
|
||||||
|
|
||||||
|
XGameRuntimeUninitialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !BUILD_RELEASE
|
||||||
|
|
||||||
|
void GDKPlatform::Log(const StringView& msg)
|
||||||
|
{
|
||||||
|
OutputDebugStringW(msg.Get());
|
||||||
|
OutputDebugStringW(TEXT(PLATFORM_LINE_TERMINATOR));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GDKPlatform::IsDebuggerPresent()
|
||||||
|
{
|
||||||
|
return !!::IsDebuggerPresent();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
BatteryInfo GDKPlatform::GetBatteryInfo()
|
||||||
|
{
|
||||||
|
BatteryInfo info;
|
||||||
|
info.State = BatteryInfo::States::Connected;
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 GDKPlatform::GetDpi()
|
||||||
|
{
|
||||||
|
return 96;
|
||||||
|
}
|
||||||
|
|
||||||
|
String GDKPlatform::GetUserLocaleName()
|
||||||
|
{
|
||||||
|
return UserLocale;
|
||||||
|
}
|
||||||
|
|
||||||
|
String GDKPlatform::GetComputerName()
|
||||||
|
{
|
||||||
|
return ComputerName;
|
||||||
|
}
|
||||||
|
|
||||||
|
String GDKPlatform::GetUserName()
|
||||||
|
{
|
||||||
|
return String::Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GDKPlatform::GetHasFocus()
|
||||||
|
{
|
||||||
|
return !IsSuspended;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GDKPlatform::CanOpenUrl(const StringView& url)
|
||||||
|
{
|
||||||
|
return Users.HasItems();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GDKPlatform::OpenUrl(const StringView& url)
|
||||||
|
{
|
||||||
|
const StringAsANSI<> urlANSI(url.Get(), url.Length());
|
||||||
|
XLaunchUri(Users[0].UserHandle, urlANSI.Get());
|
||||||
|
}
|
||||||
|
|
||||||
|
struct GetMonitorBoundsData
|
||||||
|
{
|
||||||
|
Vector2 Pos;
|
||||||
|
Rectangle Result;
|
||||||
|
|
||||||
|
GetMonitorBoundsData(const Vector2& pos)
|
||||||
|
: Pos(pos)
|
||||||
|
, Result(Vector2::Zero, GDKPlatform::GetDesktopSize())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Rectangle GDKPlatform::GetMonitorBounds(const Vector2& screenPos)
|
||||||
|
{
|
||||||
|
return Rectangle(Vector2::Zero, GetDesktopSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector2 GDKPlatform::GetDesktopSize()
|
||||||
|
{
|
||||||
|
return Vector2(1920, 1080);
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle GDKPlatform::GetVirtualDesktopBounds()
|
||||||
|
{
|
||||||
|
return Rectangle(Vector2::Zero, GetDesktopSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
void GDKPlatform::GetEnvironmentVariables(Dictionary<String, String>& result)
|
||||||
|
{
|
||||||
|
const LPWCH environmentStr = GetEnvironmentStringsW();
|
||||||
|
if (environmentStr)
|
||||||
|
{
|
||||||
|
LPWCH env = environmentStr;
|
||||||
|
while (*env != '\0')
|
||||||
|
{
|
||||||
|
if (*env != '=')
|
||||||
|
{
|
||||||
|
WCHAR* str = wcschr(env, '=');
|
||||||
|
ASSERT(str);
|
||||||
|
result[String(env, (int32)(str - env))] = str + 1;
|
||||||
|
}
|
||||||
|
while (*env != '\0')
|
||||||
|
env++;
|
||||||
|
env++;
|
||||||
|
}
|
||||||
|
FreeEnvironmentStringsW(environmentStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GDKPlatform::GetEnvironmentVariable(const String& name, String& value)
|
||||||
|
{
|
||||||
|
const int32 bufferSize = 512;
|
||||||
|
Char buffer[bufferSize];
|
||||||
|
DWORD result = GetEnvironmentVariableW(*name, buffer, bufferSize);
|
||||||
|
if (result == 0)
|
||||||
|
{
|
||||||
|
LOG_WIN32_LAST_ERROR;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (bufferSize < result)
|
||||||
|
{
|
||||||
|
value.ReserveSpace(result);
|
||||||
|
result = GetEnvironmentVariableW(*name, *value, result);
|
||||||
|
if (!result)
|
||||||
|
{
|
||||||
|
LOG_WIN32_LAST_ERROR;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
value.Set(buffer, result);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GDKPlatform::SetEnvironmentVariable(const String& name, const String& value)
|
||||||
|
{
|
||||||
|
if (!SetEnvironmentVariableW(*name, *value))
|
||||||
|
{
|
||||||
|
LOG_WIN32_LAST_ERROR;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Window* GDKPlatform::CreateWindow(const CreateWindowSettings& settings)
|
||||||
|
{
|
||||||
|
return New<GDKWindow>(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
void* GDKPlatform::LoadLibrary(const Char* filename)
|
||||||
|
{
|
||||||
|
ASSERT(filename);
|
||||||
|
void* handle = ::LoadLibraryW(filename);
|
||||||
|
if (!handle)
|
||||||
|
{
|
||||||
|
LOG(Warning, "Failed to load '{0}' (GetLastError={1})", filename, GetLastError());
|
||||||
|
}
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GDKPlatform::FreeLibrary(void* handle)
|
||||||
|
{
|
||||||
|
::FreeLibrary((HMODULE)handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
void* GDKPlatform::GetProcAddress(void* handle, const char* symbol)
|
||||||
|
{
|
||||||
|
return (void*)::GetProcAddress((HMODULE)handle, symbol);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
79
Source/Engine/Platform/GDK/GDKPlatform.h
Normal file
79
Source/Engine/Platform/GDK/GDKPlatform.h
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if PLATFORM_GDK
|
||||||
|
|
||||||
|
#include "Engine/Platform/Win32/Win32Platform.h"
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The GDK platform implementation and application management utilities.
|
||||||
|
/// </summary>
|
||||||
|
class GDKPlatform : public Win32Platform
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Win32 application windows class name.
|
||||||
|
/// </summary>
|
||||||
|
static const Char* ApplicationWindowClass;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handle to Win32 application instance.
|
||||||
|
/// </summary>
|
||||||
|
static void* Instance;
|
||||||
|
|
||||||
|
static Delegate<> OnSuspend;
|
||||||
|
static Delegate<> OnResume;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns true if current OS version is Windows 10.
|
||||||
|
/// </summary>
|
||||||
|
static bool IsWindows10()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Pre initialize platform.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="hInstance">The Win32 application instance.</param>
|
||||||
|
static void PreInit(void* hInstance);
|
||||||
|
|
||||||
|
static bool IsRunningOnDevKit();
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
// [Win32Platform]
|
||||||
|
static bool Init();
|
||||||
|
static void BeforeRun();
|
||||||
|
static void Tick();
|
||||||
|
static void BeforeExit();
|
||||||
|
static void Exit();
|
||||||
|
#if !BUILD_RELEASE
|
||||||
|
static void Log(const StringView& msg);
|
||||||
|
static bool IsDebuggerPresent();
|
||||||
|
#endif
|
||||||
|
static BatteryInfo GetBatteryInfo();
|
||||||
|
static int32 GetDpi();
|
||||||
|
static String GetUserLocaleName();
|
||||||
|
static String GetComputerName();
|
||||||
|
static String GetUserName();
|
||||||
|
static bool GetHasFocus();
|
||||||
|
static bool CanOpenUrl(const StringView& url);
|
||||||
|
static void OpenUrl(const StringView& url);
|
||||||
|
static Rectangle GetMonitorBounds(const Vector2& screenPos);
|
||||||
|
static Vector2 GetDesktopSize();
|
||||||
|
static Rectangle GetVirtualDesktopBounds();
|
||||||
|
static void GetEnvironmentVariables(Dictionary<String, String, HeapAllocation>& result);
|
||||||
|
static bool GetEnvironmentVariable(const String& name, String& value);
|
||||||
|
static bool SetEnvironmentVariable(const String& name, const String& value);
|
||||||
|
static Window* CreateWindow(const CreateWindowSettings& settings);
|
||||||
|
static void* LoadLibrary(const Char* filename);
|
||||||
|
static void FreeLibrary(void* handle);
|
||||||
|
static void* GetProcAddress(void* handle, const char* symbol);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
394
Source/Engine/Platform/GDK/GDKWindow.cpp
Normal file
394
Source/Engine/Platform/GDK/GDKWindow.cpp
Normal file
@@ -0,0 +1,394 @@
|
|||||||
|
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
#if PLATFORM_GDK
|
||||||
|
|
||||||
|
#undef _GAMING_XBOX
|
||||||
|
#include "GDKWindow.h"
|
||||||
|
#include "GDKPlatform.h"
|
||||||
|
#include "Engine/Core/Log.h"
|
||||||
|
#include "Engine/Core/Math/Math.h"
|
||||||
|
#include "Engine/Graphics/GPUSwapChain.h"
|
||||||
|
#include "Engine/Graphics/RenderTask.h"
|
||||||
|
#include "Engine/Graphics/GPUDevice.h"
|
||||||
|
#include "Engine/Platform/Win32/IncludeWindowsHeaders.h"
|
||||||
|
|
||||||
|
extern void OnMainWindowCreated(HWND hWnd);
|
||||||
|
|
||||||
|
GDKWindow::GDKWindow(const CreateWindowSettings& settings)
|
||||||
|
: WindowBase(settings)
|
||||||
|
{
|
||||||
|
int32 x = Math::TruncToInt(settings.Position.X);
|
||||||
|
int32 y = Math::TruncToInt(settings.Position.Y);
|
||||||
|
int32 clientWidth = Math::TruncToInt(settings.Size.X);
|
||||||
|
int32 clientHeight = Math::TruncToInt(settings.Size.Y);
|
||||||
|
int32 windowWidth = clientWidth;
|
||||||
|
int32 windowHeight = clientHeight;
|
||||||
|
_clientSize = Vector2((float)clientWidth, (float)clientHeight);
|
||||||
|
|
||||||
|
// Setup window style
|
||||||
|
uint32 style = WS_POPUP, exStyle = 0;
|
||||||
|
if (settings.SupportsTransparency)
|
||||||
|
exStyle |= WS_EX_LAYERED;
|
||||||
|
if (!settings.ActivateWhenFirstShown)
|
||||||
|
exStyle |= WS_EX_NOACTIVATE;
|
||||||
|
if (settings.ShowInTaskbar)
|
||||||
|
exStyle |= WS_EX_APPWINDOW;
|
||||||
|
else
|
||||||
|
exStyle |= WS_EX_TOOLWINDOW;
|
||||||
|
if (settings.IsTopmost)
|
||||||
|
exStyle |= WS_EX_TOPMOST;
|
||||||
|
if (!settings.AllowInput)
|
||||||
|
exStyle |= WS_EX_TRANSPARENT;
|
||||||
|
if (settings.AllowMaximize)
|
||||||
|
style |= WS_MAXIMIZEBOX;
|
||||||
|
if (settings.AllowMinimize)
|
||||||
|
style |= WS_MINIMIZEBOX;
|
||||||
|
if (settings.HasSizingFrame)
|
||||||
|
style |= WS_THICKFRAME;
|
||||||
|
|
||||||
|
// Check if window should have a border
|
||||||
|
if (settings.HasBorder)
|
||||||
|
{
|
||||||
|
// Create window style flags
|
||||||
|
style |= WS_OVERLAPPED | WS_SYSMENU | WS_BORDER | WS_CAPTION;
|
||||||
|
exStyle |= 0;
|
||||||
|
|
||||||
|
// Adjust window size and positions to take into account window border
|
||||||
|
RECT winRect = { 0, 0, clientWidth, clientHeight };
|
||||||
|
AdjustWindowRectEx(&winRect, style, FALSE, exStyle);
|
||||||
|
x += winRect.left;
|
||||||
|
y += winRect.top;
|
||||||
|
windowWidth = winRect.right - winRect.left;
|
||||||
|
windowHeight = winRect.bottom - winRect.top;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Create window style flags
|
||||||
|
style |= WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
|
||||||
|
exStyle |= WS_EX_WINDOWEDGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creating the window
|
||||||
|
_handle = CreateWindowExW(
|
||||||
|
exStyle,
|
||||||
|
Platform::ApplicationWindowClass,
|
||||||
|
settings.Title.GetText(),
|
||||||
|
style,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
windowWidth,
|
||||||
|
windowHeight,
|
||||||
|
settings.Parent ? static_cast<HWND>(settings.Parent->GetNativePtr()) : nullptr,
|
||||||
|
nullptr,
|
||||||
|
(HINSTANCE)Platform::Instance,
|
||||||
|
nullptr);
|
||||||
|
|
||||||
|
// Validate result
|
||||||
|
if (!HasHWND())
|
||||||
|
{
|
||||||
|
LOG_WIN32_LAST_ERROR;
|
||||||
|
Platform::Fatal(TEXT("Cannot create window."));
|
||||||
|
}
|
||||||
|
|
||||||
|
OnMainWindowCreated(_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
GDKWindow::~GDKWindow()
|
||||||
|
{
|
||||||
|
if (HasHWND())
|
||||||
|
{
|
||||||
|
// Destroy window
|
||||||
|
if (DestroyWindow(_handle) == 0)
|
||||||
|
{
|
||||||
|
LOG(Warning, "DestroyWindow failed! Error: {0:#x}", GetLastError());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear
|
||||||
|
_handle = nullptr;
|
||||||
|
_visible = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void* GDKWindow::GetNativePtr() const
|
||||||
|
{
|
||||||
|
return _handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GDKWindow::Show()
|
||||||
|
{
|
||||||
|
if (!_visible)
|
||||||
|
{
|
||||||
|
InitSwapChain();
|
||||||
|
if (_showAfterFirstPaint)
|
||||||
|
{
|
||||||
|
if (RenderTask)
|
||||||
|
RenderTask->Enabled = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT(HasHWND());
|
||||||
|
|
||||||
|
// Show
|
||||||
|
ShowWindow(_handle, (_settings.AllowInput && _settings.ActivateWhenFirstShown) ? SW_SHOW : SW_SHOWNA);
|
||||||
|
|
||||||
|
// Base
|
||||||
|
WindowBase::Show();
|
||||||
|
|
||||||
|
_focused = true; // TODO: remove it and check if the initial focus works when game starts with rendering, do we get WM_ACTIVATEAPP?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GDKWindow::Hide()
|
||||||
|
{
|
||||||
|
if (_visible)
|
||||||
|
{
|
||||||
|
ASSERT(HasHWND());
|
||||||
|
|
||||||
|
// Hide
|
||||||
|
ShowWindow(_handle, SW_HIDE);
|
||||||
|
|
||||||
|
// Base
|
||||||
|
WindowBase::Hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GDKWindow::Minimize()
|
||||||
|
{
|
||||||
|
ASSERT(HasHWND());
|
||||||
|
ShowWindow(_handle, SW_MINIMIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GDKWindow::Maximize()
|
||||||
|
{
|
||||||
|
ASSERT(HasHWND());
|
||||||
|
ShowWindow(_handle, SW_MAXIMIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GDKWindow::Restore()
|
||||||
|
{
|
||||||
|
ASSERT(HasHWND());
|
||||||
|
ShowWindow(_handle, SW_RESTORE);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GDKWindow::IsClosed() const
|
||||||
|
{
|
||||||
|
return !HasHWND();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GDKWindow::IsForegroundWindow() const
|
||||||
|
{
|
||||||
|
return Platform::GetHasFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GDKWindow::SetIsFullscreen(bool isFullscreen)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void GDKWindow::GetScreenInfo(int32& x, int32& y, int32& width, int32& height) const
|
||||||
|
{
|
||||||
|
x = 0;
|
||||||
|
y = 0;
|
||||||
|
width = (int32)_clientSize.X;
|
||||||
|
height = (int32)_clientSize.Y;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GDKWindow::SetCursor(CursorType type)
|
||||||
|
{
|
||||||
|
// Base
|
||||||
|
WindowBase::SetCursor(type);
|
||||||
|
|
||||||
|
UpdateCursor();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GDKWindow::CheckForWindowResize()
|
||||||
|
{
|
||||||
|
// Skip for minimized window (GetClientRect for minimized window returns 0)
|
||||||
|
if (_minimized)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ASSERT(HasHWND());
|
||||||
|
|
||||||
|
// Cache client size
|
||||||
|
RECT rect;
|
||||||
|
GetClientRect(_handle, &rect);
|
||||||
|
const int32 width = Math::Max(rect.right - rect.left, 0L);
|
||||||
|
const int32 height = Math::Max(rect.bottom - rect.top, 0L);
|
||||||
|
_clientSize = Vector2(static_cast<float>(width), static_cast<float>(height));
|
||||||
|
|
||||||
|
// Check if window size has been changed
|
||||||
|
if (width > 0 && height > 0 && (_swapChain == nullptr || width != _swapChain->GetWidth() || height != _swapChain->GetHeight()))
|
||||||
|
{
|
||||||
|
OnResize(width, height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GDKWindow::UpdateCursor() const
|
||||||
|
{
|
||||||
|
if (_cursor == CursorType::Hidden)
|
||||||
|
{
|
||||||
|
::SetCursor(nullptr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 index = 0;
|
||||||
|
switch (_cursor)
|
||||||
|
{
|
||||||
|
case CursorType::Default:
|
||||||
|
break;
|
||||||
|
case CursorType::Cross:
|
||||||
|
index = 1;
|
||||||
|
break;
|
||||||
|
case CursorType::Hand:
|
||||||
|
index = 2;
|
||||||
|
break;
|
||||||
|
case CursorType::Help:
|
||||||
|
index = 3;
|
||||||
|
break;
|
||||||
|
case CursorType::IBeam:
|
||||||
|
index = 4;
|
||||||
|
break;
|
||||||
|
case CursorType::No:
|
||||||
|
index = 5;
|
||||||
|
break;
|
||||||
|
case CursorType::Wait:
|
||||||
|
index = 11;
|
||||||
|
break;
|
||||||
|
case CursorType::SizeAll:
|
||||||
|
index = 6;
|
||||||
|
break;
|
||||||
|
case CursorType::SizeNESW:
|
||||||
|
index = 7;
|
||||||
|
break;
|
||||||
|
case CursorType::SizeNS:
|
||||||
|
index = 8;
|
||||||
|
break;
|
||||||
|
case CursorType::SizeNWSE:
|
||||||
|
index = 9;
|
||||||
|
break;
|
||||||
|
case CursorType::SizeWE:
|
||||||
|
index = 10;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const LPCWSTR cursors[] =
|
||||||
|
{
|
||||||
|
IDC_ARROW,
|
||||||
|
IDC_CROSS,
|
||||||
|
IDC_HAND,
|
||||||
|
IDC_HELP,
|
||||||
|
IDC_IBEAM,
|
||||||
|
IDC_NO,
|
||||||
|
IDC_SIZEALL,
|
||||||
|
IDC_SIZENESW,
|
||||||
|
IDC_SIZENS,
|
||||||
|
IDC_SIZENWSE,
|
||||||
|
IDC_SIZEWE,
|
||||||
|
IDC_WAIT,
|
||||||
|
};
|
||||||
|
|
||||||
|
ASSERT(index >= 0 && index < ARRAY_COUNT(cursors));
|
||||||
|
const HCURSOR cursor = LoadCursorW(nullptr, cursors[index]);
|
||||||
|
::SetCursor(cursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
Windows::LRESULT GDKWindow::WndProc(Windows::UINT msg, Windows::WPARAM wParam, Windows::LPARAM lParam)
|
||||||
|
{
|
||||||
|
switch (msg)
|
||||||
|
{
|
||||||
|
case WM_SETCURSOR:
|
||||||
|
{
|
||||||
|
if (LOWORD(lParam) == HTCLIENT)
|
||||||
|
{
|
||||||
|
UpdateCursor();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case WM_CREATE:
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
case WM_SIZE:
|
||||||
|
{
|
||||||
|
if (SIZE_MINIMIZED == wParam)
|
||||||
|
{
|
||||||
|
// Set flags
|
||||||
|
_minimized = true;
|
||||||
|
_maximized = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
RECT rcCurrentClient;
|
||||||
|
GetClientRect(_handle, &rcCurrentClient);
|
||||||
|
if (rcCurrentClient.top == 0 && rcCurrentClient.bottom == 0)
|
||||||
|
{
|
||||||
|
// Rapidly clicking the task bar to minimize and restore a window can cause a WM_SIZE message with SIZE_RESTORED when
|
||||||
|
// the window has actually become minimized due to rapid change so just ignore this message.
|
||||||
|
}
|
||||||
|
else if (SIZE_MAXIMIZED == wParam)
|
||||||
|
{
|
||||||
|
// Set flags
|
||||||
|
_minimized = false;
|
||||||
|
_maximized = true;
|
||||||
|
|
||||||
|
// Check size
|
||||||
|
CheckForWindowResize();
|
||||||
|
}
|
||||||
|
else if (SIZE_RESTORED == wParam)
|
||||||
|
{
|
||||||
|
if (_maximized)
|
||||||
|
{
|
||||||
|
// Clear flag
|
||||||
|
_maximized = false;
|
||||||
|
|
||||||
|
// Check size
|
||||||
|
CheckForWindowResize();
|
||||||
|
}
|
||||||
|
else if (_minimized)
|
||||||
|
{
|
||||||
|
// Clear flag
|
||||||
|
_minimized = false;
|
||||||
|
|
||||||
|
// Check size
|
||||||
|
CheckForWindowResize();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// This WM_SIZE come from resizing the window via an API like SetWindowPos() so resize
|
||||||
|
CheckForWindowResize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case WM_SETFOCUS:
|
||||||
|
OnGotFocus();
|
||||||
|
break;
|
||||||
|
case WM_KILLFOCUS:
|
||||||
|
OnLostFocus();
|
||||||
|
break;
|
||||||
|
case WM_ACTIVATEAPP:
|
||||||
|
if (wParam == TRUE && !_focused)
|
||||||
|
{
|
||||||
|
OnGotFocus();
|
||||||
|
}
|
||||||
|
else if (wParam == FALSE && _focused)
|
||||||
|
{
|
||||||
|
OnLostFocus();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case WM_CLOSE:
|
||||||
|
Close(ClosingReason::User);
|
||||||
|
return 0;
|
||||||
|
case WM_DESTROY:
|
||||||
|
{
|
||||||
|
// Quit
|
||||||
|
PostQuitMessage(0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return DefWindowProc(_handle, msg, wParam, lParam);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
93
Source/Engine/Platform/GDK/GDKWindow.h
Normal file
93
Source/Engine/Platform/GDK/GDKWindow.h
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if PLATFORM_GDK
|
||||||
|
|
||||||
|
#include "Engine/Platform/Base/WindowBase.h"
|
||||||
|
#include "Engine/Platform/Platform.h"
|
||||||
|
#include "Engine/Platform/Win32/WindowsMinimal.h"
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Implementation of the window class for GDK platform.
|
||||||
|
/// </summary>
|
||||||
|
class GDKWindow : public WindowBase
|
||||||
|
{
|
||||||
|
friend GDKPlatform;
|
||||||
|
private:
|
||||||
|
|
||||||
|
Windows::HWND _handle;
|
||||||
|
Vector2 _clientSize;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="GDKWindow"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="settings">The initial window settings.</param>
|
||||||
|
GDKWindow(const CreateWindowSettings& settings);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finalizes an instance of the <see cref="GDKWindow"/> class.
|
||||||
|
/// </summary>
|
||||||
|
~GDKWindow();
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the window handle.
|
||||||
|
/// </summary>
|
||||||
|
FORCE_INLINE Windows::HWND GetHWND() const
|
||||||
|
{
|
||||||
|
return _handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if the window has valid handle created.
|
||||||
|
/// </summary>
|
||||||
|
FORCE_INLINE bool HasHWND() const
|
||||||
|
{
|
||||||
|
return _handle != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the information about screen which contains window.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="x">The x position.</param>
|
||||||
|
/// <param name="y">The y position.</param>
|
||||||
|
/// <param name="width">The width.</param>
|
||||||
|
/// <param name="height">The height.</param>
|
||||||
|
void GetScreenInfo(int32& x, int32& y, int32& width, int32& height) const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The Windows messages procedure.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="msg">The mMessage.</param>
|
||||||
|
/// <param name="wParam">The first parameter.</param>
|
||||||
|
/// <param name="lParam">The second parameter.</param>
|
||||||
|
/// <returns>The result.</returns>
|
||||||
|
Windows::LRESULT WndProc(Windows::UINT msg, Windows::WPARAM wParam, Windows::LPARAM lParam);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void CheckForWindowResize();
|
||||||
|
void UpdateCursor() const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
// [Window]
|
||||||
|
void* GetNativePtr() const override;
|
||||||
|
void Show() override;
|
||||||
|
void Hide() override;
|
||||||
|
void Minimize() override;
|
||||||
|
void Maximize() override;
|
||||||
|
void Restore() override;
|
||||||
|
bool IsClosed() const override;
|
||||||
|
bool IsForegroundWindow() const override;
|
||||||
|
void SetIsFullscreen(bool isFullscreen) override;
|
||||||
|
void SetCursor(CursorType type) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -58,10 +58,12 @@ public class Platform : EngineModule
|
|||||||
break;
|
break;
|
||||||
case TargetPlatform.XboxOne:
|
case TargetPlatform.XboxOne:
|
||||||
options.SourcePaths.Add(Path.Combine(FolderPath, "Win32"));
|
options.SourcePaths.Add(Path.Combine(FolderPath, "Win32"));
|
||||||
|
options.SourcePaths.Add(Path.Combine(FolderPath, "GDK"));
|
||||||
options.SourcePaths.Add(Path.Combine(Globals.EngineRoot, "Source", "Platforms", "XboxOne", "Engine", "Platform"));
|
options.SourcePaths.Add(Path.Combine(Globals.EngineRoot, "Source", "Platforms", "XboxOne", "Engine", "Platform"));
|
||||||
break;
|
break;
|
||||||
case TargetPlatform.XboxScarlett:
|
case TargetPlatform.XboxScarlett:
|
||||||
options.SourcePaths.Add(Path.Combine(FolderPath, "Win32"));
|
options.SourcePaths.Add(Path.Combine(FolderPath, "Win32"));
|
||||||
|
options.SourcePaths.Add(Path.Combine(FolderPath, "GDK"));
|
||||||
options.SourcePaths.Add(Path.Combine(Globals.EngineRoot, "Source", "Platforms", "XboxScarlett", "Engine", "Platform"));
|
options.SourcePaths.Add(Path.Combine(Globals.EngineRoot, "Source", "Platforms", "XboxScarlett", "Engine", "Platform"));
|
||||||
break;
|
break;
|
||||||
case TargetPlatform.Android:
|
case TargetPlatform.Android:
|
||||||
|
|||||||
@@ -111,8 +111,8 @@ class XboxOnePlatform;
|
|||||||
typedef XboxOnePlatform Platform;
|
typedef XboxOnePlatform Platform;
|
||||||
class Win32Thread;
|
class Win32Thread;
|
||||||
typedef Win32Thread Thread;
|
typedef Win32Thread Thread;
|
||||||
class XboxOneWindow;
|
class GDKWindow;
|
||||||
typedef XboxOneWindow Window;
|
typedef GDKWindow Window;
|
||||||
class NetworkBase;
|
class NetworkBase;
|
||||||
typedef NetworkBase Network;
|
typedef NetworkBase Network;
|
||||||
|
|
||||||
@@ -134,8 +134,8 @@ class XboxScarlettPlatform;
|
|||||||
typedef XboxScarlettPlatform Platform;
|
typedef XboxScarlettPlatform Platform;
|
||||||
class Win32Thread;
|
class Win32Thread;
|
||||||
typedef Win32Thread Thread;
|
typedef Win32Thread Thread;
|
||||||
class XboxScarlettWindow;
|
class GDKWindow;
|
||||||
typedef XboxScarlettWindow Window;
|
typedef GDKWindow Window;
|
||||||
class NetworkBase;
|
class NetworkBase;
|
||||||
typedef NetworkBase Network;
|
typedef NetworkBase Network;
|
||||||
|
|
||||||
|
|||||||
@@ -11,9 +11,9 @@
|
|||||||
#elif PLATFORM_PS4
|
#elif PLATFORM_PS4
|
||||||
#include "Platforms/PS4/Engine/Platform/PS4Window.h"
|
#include "Platforms/PS4/Engine/Platform/PS4Window.h"
|
||||||
#elif PLATFORM_XBOX_ONE
|
#elif PLATFORM_XBOX_ONE
|
||||||
#include "Platforms/XboxOne/Engine/Platform/XboxOneWindow.h"
|
#include "GDK/GDKWindow.h"
|
||||||
#elif PLATFORM_XBOX_SCARLETT
|
#elif PLATFORM_XBOX_SCARLETT
|
||||||
#include "Platforms/XboxScarlett/Engine/Platform/XboxScarlettWindow.h"
|
#include "GDK/GDKWindow.h"
|
||||||
#elif PLATFORM_ANDROID
|
#elif PLATFORM_ANDROID
|
||||||
#include "Android/AndroidWindow.h"
|
#include "Android/AndroidWindow.h"
|
||||||
#elif PLATFORM_SWITCH
|
#elif PLATFORM_SWITCH
|
||||||
|
|||||||
69
Source/Tools/Flax.Build/Platforms/GDK/GDK.cs
Normal file
69
Source/Tools/Flax.Build/Platforms/GDK/GDK.cs
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Flax.Build.Platforms
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The Microsoft Game Development Kit.
|
||||||
|
/// </summary>
|
||||||
|
/// <seealso cref="Sdk" />
|
||||||
|
public sealed class GDK : Sdk
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The singleton instance.
|
||||||
|
/// </summary>
|
||||||
|
public static readonly GDK Instance = new GDK();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override TargetPlatform[] Platforms => new[]
|
||||||
|
{
|
||||||
|
TargetPlatform.Windows,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="GDK"/> class.
|
||||||
|
/// </summary>
|
||||||
|
public GDK()
|
||||||
|
{
|
||||||
|
if (!Platforms.Contains(Platform.BuildTargetPlatform))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var sdkDir = Environment.GetEnvironmentVariable("GameDKLatest");
|
||||||
|
if (!Directory.Exists(sdkDir))
|
||||||
|
sdkDir = Environment.GetEnvironmentVariable("GRDKLatest");
|
||||||
|
if (Directory.Exists(sdkDir))
|
||||||
|
{
|
||||||
|
if (sdkDir.EndsWith("GRDK\\"))
|
||||||
|
sdkDir = sdkDir.Remove(sdkDir.Length - 6);
|
||||||
|
else if (sdkDir.EndsWith("GRDK"))
|
||||||
|
sdkDir = sdkDir.Remove(sdkDir.Length - 5);
|
||||||
|
RootPath = sdkDir;
|
||||||
|
|
||||||
|
// Read the SDK version number
|
||||||
|
string sdkManifest = Path.Combine(RootPath, "GRDK", "grdk.ini");
|
||||||
|
if (File.Exists(sdkManifest))
|
||||||
|
{
|
||||||
|
var contents = File.ReadAllText(sdkManifest);
|
||||||
|
const string prefix = "_xbld_edition=";
|
||||||
|
var start = contents.IndexOf(prefix) + prefix.Length;
|
||||||
|
var end = contents.IndexOf("\r\n_xbld_full_productbuild");
|
||||||
|
var versionText = contents.Substring(start, end - start);
|
||||||
|
Version = new Version(int.Parse(versionText), 0);
|
||||||
|
|
||||||
|
var minEdition = 200500;
|
||||||
|
if (Version.Major < minEdition)
|
||||||
|
{
|
||||||
|
Log.Error(string.Format("Unsupported GDK version {0}. Minimum supported is edition {1}.", Version.Major, minEdition));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.Info(string.Format("Found GDK {0} at {1}", Version.Major, RootPath));
|
||||||
|
IsValid = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
37
Source/Tools/Flax.Build/Platforms/GDK/GDKPlatform.cs
Normal file
37
Source/Tools/Flax.Build/Platforms/GDK/GDKPlatform.cs
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
using System.Linq;
|
||||||
|
using Flax.Build.Projects.VisualStudio;
|
||||||
|
|
||||||
|
namespace Flax.Build.Platforms
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The GDK platform implementation.
|
||||||
|
/// </summary>
|
||||||
|
/// <seealso cref="Platform" />
|
||||||
|
/// <seealso cref="Flax.Build.Platforms.WindowsPlatformBase" />
|
||||||
|
public abstract class GDKPlatform : WindowsPlatformBase
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="GDKPlatform"/> class.
|
||||||
|
/// </summary>
|
||||||
|
protected GDKPlatform()
|
||||||
|
{
|
||||||
|
// Visual Studio 2017 or newer required
|
||||||
|
var visualStudio = VisualStudioInstance.GetInstances().FirstOrDefault(x => x.Version == VisualStudioVersion.VisualStudio2017 || x.Version == VisualStudioVersion.VisualStudio2019);
|
||||||
|
if (visualStudio == null)
|
||||||
|
_hasRequiredSDKsInstalled = false;
|
||||||
|
|
||||||
|
// Windows 10.0.19041.0 SDK or newer required
|
||||||
|
var sdks = GetSDKs();
|
||||||
|
if (!sdks.ContainsKey(WindowsPlatformSDK.v10_0_19041_0))
|
||||||
|
_hasRequiredSDKsInstalled = false;
|
||||||
|
|
||||||
|
// v141 toolset or newer required
|
||||||
|
var toolsets = GetToolsets();
|
||||||
|
if (!toolsets.ContainsKey(WindowsPlatformToolset.v141) &&
|
||||||
|
!toolsets.ContainsKey(WindowsPlatformToolset.v142))
|
||||||
|
_hasRequiredSDKsInstalled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
63
Source/Tools/Flax.Build/Platforms/GDK/GDKToolchain.cs
Normal file
63
Source/Tools/Flax.Build/Platforms/GDK/GDKToolchain.cs
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using Flax.Build.NativeCpp;
|
||||||
|
|
||||||
|
namespace Flax.Build.Platforms
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The GDK toolchain implementation.
|
||||||
|
/// </summary>
|
||||||
|
/// <seealso cref="Toolchain" />
|
||||||
|
/// <seealso cref="Flax.Build.Platforms.WindowsToolchainBase" />
|
||||||
|
public abstract class GDKToolchain : WindowsToolchainBase
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="GDKToolchain"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="platform">The platform.</param>
|
||||||
|
/// <param name="architecture">The architecture.</param>
|
||||||
|
protected GDKToolchain(GDKPlatform platform, TargetArchitecture architecture)
|
||||||
|
: base(platform, architecture, WindowsPlatformToolset.Latest, WindowsPlatformSDK.v10_0_19041_0)
|
||||||
|
{
|
||||||
|
// Setup system paths
|
||||||
|
SystemIncludePaths.Add(Path.Combine(GDK.Instance.RootPath, "GRDK\\GameKit\\Include"));
|
||||||
|
SystemLibraryPaths.Add(Path.Combine(GDK.Instance.RootPath, "GRDK\\GameKit\\Lib\\amd64"));
|
||||||
|
SystemLibraryPaths.Add(Path.Combine(GDK.Instance.RootPath, "GRDK\\ExtensionLibraries\\Xbox.Services.API.C\\DesignTime\\CommonConfiguration\\Neutral\\Lib\\Release\\" + Toolset));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void SetupEnvironment(BuildOptions options)
|
||||||
|
{
|
||||||
|
base.SetupEnvironment(options);
|
||||||
|
|
||||||
|
options.CompileEnv.PreprocessorDefinitions.Add("PLATFORM_GDK");
|
||||||
|
options.CompileEnv.PreprocessorDefinitions.Add("WINAPI_FAMILY=WINAPI_FAMILY_GAMES");
|
||||||
|
options.CompileEnv.PreprocessorDefinitions.Add("_ATL_NO_DEFAULT_LIBS");
|
||||||
|
options.CompileEnv.PreprocessorDefinitions.Add("__WRL_NO_DEFAULT_LIB__");
|
||||||
|
|
||||||
|
options.LinkEnv.InputLibraries.Add("xgameruntime.lib");
|
||||||
|
options.LinkEnv.InputLibraries.Add("xgameplatform.lib");
|
||||||
|
options.LinkEnv.InputLibraries.Add("Microsoft.Xbox.Services.142.GDK.C.lib");
|
||||||
|
|
||||||
|
var toolsetPath = WindowsPlatformBase.GetToolsets()[Toolset];
|
||||||
|
var toolsPath = WindowsPlatformBase.GetVCToolPath64(Toolset);
|
||||||
|
if (options.CompileEnv.UseDebugCRT)
|
||||||
|
throw new Exception("Don't use debug CRT on GDK.");
|
||||||
|
var name = Path.GetFileName(toolsetPath);
|
||||||
|
var redistToolsPath = Path.Combine(toolsPath, "..", "..", "..", "..", "..", "..", "Redist/MSVC/");
|
||||||
|
var paths = Directory.GetDirectories(redistToolsPath, name.Substring(0, 5) + "*");
|
||||||
|
redistToolsPath = Path.Combine(paths[0], "x64", "Microsoft.VC" + (int)Toolset + ".CRT");
|
||||||
|
redistToolsPath = Utilities.RemovePathRelativeParts(redistToolsPath);
|
||||||
|
options.DependencyFiles.Add(Path.Combine(redistToolsPath, "concrt140.dll"));
|
||||||
|
options.DependencyFiles.Add(Path.Combine(redistToolsPath, "msvcp140.dll"));
|
||||||
|
options.DependencyFiles.Add(Path.Combine(redistToolsPath, "msvcp140_1.dll"));
|
||||||
|
options.DependencyFiles.Add(Path.Combine(redistToolsPath, "msvcp140_2.dll"));
|
||||||
|
options.DependencyFiles.Add(Path.Combine(redistToolsPath, "msvcp140_codecvt_ids.dll"));
|
||||||
|
options.DependencyFiles.Add(Path.Combine(redistToolsPath, "vccorlib140.dll"));
|
||||||
|
options.DependencyFiles.Add(Path.Combine(redistToolsPath, "vcruntime140.dll"));
|
||||||
|
options.DependencyFiles.Add(Path.Combine(redistToolsPath, "vcruntime140_1.dll"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user