Refactor GDK platform to be base for Xbox consoles
This commit is contained in:
@@ -117,9 +117,6 @@ bool GraphicsService::Init()
|
||||
// Platform default
|
||||
if (!device)
|
||||
{
|
||||
#if PLATFORM_WINDOWS
|
||||
|
||||
// Windows
|
||||
#if GRAPHICS_API_DIRECTX11
|
||||
if (!device)
|
||||
device = CreateGPUDeviceDX11();
|
||||
@@ -132,65 +129,9 @@ bool GraphicsService::Init()
|
||||
if (!device)
|
||||
device = CreateGPUDeviceVulkan();
|
||||
#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 (!device)
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
#if PLATFORM_XBOX_SCARLETT || PLATFORM_XBOX_ONE
|
||||
@@ -264,12 +250,9 @@ bool GPUDeviceDX12::Init()
|
||||
LOG(Info, "Hardware Version: {0}", hwVer);
|
||||
updateFrameEvents();
|
||||
|
||||
#if PLATFORM_XBOX_SCARLETT
|
||||
XboxScarlett::OnSuspend.Bind<GPUDeviceDX12, &GPUDeviceDX12::OnSuspend>(this);
|
||||
XboxScarlett::OnResume.Bind<GPUDeviceDX12, &GPUDeviceDX12::OnResume>(this);
|
||||
#elif PLATFORM_XBOX_ONE
|
||||
XboxOne::OnSuspend.Bind<GPUDeviceDX12, &GPUDeviceDX12::OnSuspend>(this);
|
||||
XboxOne::OnResume.Bind<GPUDeviceDX12, &GPUDeviceDX12::OnResume>(this);
|
||||
#if PLATFORM_GDK
|
||||
GDKPlatform::OnSuspend.Bind<GPUDeviceDX12, &GPUDeviceDX12::OnSuspend>(this);
|
||||
GDKPlatform::OnResume.Bind<GPUDeviceDX12, &GPUDeviceDX12::OnResume>(this);
|
||||
#endif
|
||||
#else
|
||||
// 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;
|
||||
case TargetPlatform.XboxOne:
|
||||
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"));
|
||||
break;
|
||||
case TargetPlatform.XboxScarlett:
|
||||
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"));
|
||||
break;
|
||||
case TargetPlatform.Android:
|
||||
|
||||
@@ -111,8 +111,8 @@ class XboxOnePlatform;
|
||||
typedef XboxOnePlatform Platform;
|
||||
class Win32Thread;
|
||||
typedef Win32Thread Thread;
|
||||
class XboxOneWindow;
|
||||
typedef XboxOneWindow Window;
|
||||
class GDKWindow;
|
||||
typedef GDKWindow Window;
|
||||
class NetworkBase;
|
||||
typedef NetworkBase Network;
|
||||
|
||||
@@ -134,8 +134,8 @@ class XboxScarlettPlatform;
|
||||
typedef XboxScarlettPlatform Platform;
|
||||
class Win32Thread;
|
||||
typedef Win32Thread Thread;
|
||||
class XboxScarlettWindow;
|
||||
typedef XboxScarlettWindow Window;
|
||||
class GDKWindow;
|
||||
typedef GDKWindow Window;
|
||||
class NetworkBase;
|
||||
typedef NetworkBase Network;
|
||||
|
||||
|
||||
@@ -11,9 +11,9 @@
|
||||
#elif PLATFORM_PS4
|
||||
#include "Platforms/PS4/Engine/Platform/PS4Window.h"
|
||||
#elif PLATFORM_XBOX_ONE
|
||||
#include "Platforms/XboxOne/Engine/Platform/XboxOneWindow.h"
|
||||
#include "GDK/GDKWindow.h"
|
||||
#elif PLATFORM_XBOX_SCARLETT
|
||||
#include "Platforms/XboxScarlett/Engine/Platform/XboxScarlettWindow.h"
|
||||
#include "GDK/GDKWindow.h"
|
||||
#elif PLATFORM_ANDROID
|
||||
#include "Android/AndroidWindow.h"
|
||||
#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