Files
FlaxEngine/Source/Engine/Platform/UWP/UWPWindow.cpp
2023-01-10 15:29:37 +01:00

603 lines
16 KiB
C++

// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
#if PLATFORM_UWP
#include "UWPWindow.h"
#include "UWPPlatform.h"
#include "Engine/Engine/Engine.h"
#include "Engine/Core/Log.h"
#include "Engine/GraphicsDevice/DirectX/DX11/GPUSwapChainDX11.h"
#include "Engine/Graphics/RenderTask.h"
namespace Impl
{
UWPWindow::UWPKeyboard* Keyboard = nullptr;
UWPWindow::UWPMouse* Mouse = nullptr;
UWPWindow* Window = nullptr;
}
void OnSizeChanged(float width, float height, void* userData)
{
((UWPWindow*)userData)->onSizeChanged(width, height);
}
void OnVisibilityChanged(bool visible, void* userData)
{
((UWPWindow*)userData)->onVisibilityChanged(visible);
}
void OnDpiChanged(float dpi, void* userData)
{
((UWPWindow*)userData)->onDpiChanged(dpi);
}
void OnClosed(void* userData)
{
((UWPWindow*)userData)->onClosed();
}
void OnFocusChanged(bool focused, void* userData)
{
((UWPWindow*)userData)->onFocusChanged(focused);
}
void OnKeyDown(int key, void* userData)
{
((UWPWindow*)userData)->onKeyDown(key);
}
void OnKeyUp(int key, void* userData)
{
((UWPWindow*)userData)->onKeyUp(key);
}
void OnCharacterReceived(int key, void* userData)
{
((UWPWindow*)userData)->onCharacterReceived(key);
}
void OnMouseMoved(float x, float y, void* userData)
{
((UWPWindow*)userData)->onMouseMoved(x, y);
}
void OnPointerPressed(UWPWindowImpl::PointerData* pointer, void* userData)
{
((UWPWindow*)userData)->onPointerPressed(pointer);
}
void OnPointerMoved(UWPWindowImpl::PointerData* pointer, void* userData)
{
((UWPWindow*)userData)->onPointerMoved(pointer);
}
void OnPointerWheelChanged(UWPWindowImpl::PointerData* pointer, void* userData)
{
((UWPWindow*)userData)->onPointerWheelChanged(pointer);
}
void OnPointerReleased(UWPWindowImpl::PointerData* pointer, void* userData)
{
((UWPWindow*)userData)->onPointerReleased(pointer);
}
void OnPointerExited(UWPWindowImpl::PointerData* pointer, void* userData)
{
((UWPWindow*)userData)->onPointerExited(pointer);
}
// Converts a length in device-independent pixels (DIPs) to a length in physical pixels.
inline float ConvertDipsToPixels(float dips, float dpi)
{
static const float dipsPerInch = 96.0f;
return floorf(dips * dpi / dipsPerInch + 0.5f); // Round to nearest integer.
}
// Converts a length in physical pixels to a length in device-independent pixels (DIPs) using the provided DPI. This removes the need
// to call this function from a thread associated with a CoreWindow, which is required by DisplayInformation::GetForCurrentView().
inline float ConvertPixelsToDips(float pixels, float dpi)
{
static const float dipsPerInch = 96.0f;
return pixels * dipsPerInch / dpi; // Do not round.
}
void UWPWindow::UWPMouse::onMouseMoved(float x, float y)
{
const Float2 mousePos(x, y);
if (!Float2::NearEqual(mousePos, MousePosition))
{
MousePosition = mousePos;
OnMouseMove(mousePos);
}
}
void UWPWindow::UWPMouse::onPointerPressed(UWPWindowImpl::PointerData* pointer)
{
const Float2 mousePos(pointer->PositionX, pointer->PositionY);
#define CHECK_MOUSE_BUTTON(button, val1) \
if (!val1 && pointer->val1) \
{ \
val1 = pointer->val1; \
OnMouseDown(mousePos, MouseButton::button); \
}
CHECK_MOUSE_BUTTON(Left, IsLeftButtonPressed);
CHECK_MOUSE_BUTTON(Right, IsRightButtonPressed);
CHECK_MOUSE_BUTTON(Middle, IsMiddleButtonPressed);
CHECK_MOUSE_BUTTON(Extended1, IsXButton1Pressed);
CHECK_MOUSE_BUTTON(Extended2, IsXButton2Pressed);
#undef CHECK_MOUSE_BUTTON
}
void UWPWindow::UWPMouse::onPointerMoved(UWPWindowImpl::PointerData* pointer)
{
// Note: UWP reports pointer pressed event for the first mouse button clicked and pointer released event for the last button released but other buttons are reported using pointer moved event.
// Reference: https://docs.microsoft.com/en-us/windows/uwp/design/input/handle-pointer-input
const Float2 mousePos(pointer->PositionX, pointer->PositionY);
#define CHECK_MOUSE_BUTTON(button, val1) \
if (val1 != pointer->val1) \
{ \
val1 = pointer->val1; \
if (val1) \
OnMouseDown(mousePos, MouseButton::button); \
else \
OnMouseUp(mousePos, MouseButton::button); \
}
CHECK_MOUSE_BUTTON(Left, IsLeftButtonPressed);
CHECK_MOUSE_BUTTON(Right, IsRightButtonPressed);
CHECK_MOUSE_BUTTON(Middle, IsMiddleButtonPressed);
CHECK_MOUSE_BUTTON(Extended1, IsXButton1Pressed);
CHECK_MOUSE_BUTTON(Extended2, IsXButton2Pressed);
#undef CHECK_MOUSE_BUTTON
if (!Float2::NearEqual(mousePos, MousePosition))
{
MousePosition = mousePos;
OnMouseMove(mousePos);
}
}
void UWPWindow::UWPMouse::onPointerWheelChanged(UWPWindowImpl::PointerData* pointer)
{
int32 delta = pointer->MouseWheelDelta;
if (delta != 0)
{
const float deltaNormalized = static_cast<float>(delta) / WHEEL_DELTA;
OnMouseWheel(MousePosition, deltaNormalized);
}
}
void UWPWindow::UWPMouse::onPointerReleased(UWPWindowImpl::PointerData* pointer)
{
Float2 mousePos = Float2(pointer->PositionX, pointer->PositionY);
#define CHECK_MOUSE_BUTTON(button, val1) \
if (!val1 && pointer->val1) \
{ \
val1 = pointer->val1; \
OnMouseUp(mousePos, MouseButton::button); \
}
CHECK_MOUSE_BUTTON(Left, IsLeftButtonPressed);
CHECK_MOUSE_BUTTON(Right, IsRightButtonPressed);
CHECK_MOUSE_BUTTON(Middle, IsMiddleButtonPressed);
CHECK_MOUSE_BUTTON(Extended1, IsXButton1Pressed);
CHECK_MOUSE_BUTTON(Extended2, IsXButton2Pressed);
#undef CHECK_MOUSE_BUTTON
}
void UWPWindow::UWPMouse::onPointerExited(UWPWindowImpl::PointerData* pointer)
{
MousePosition = Float2(pointer->PositionX, pointer->PositionY);
OnMouseLeave();
}
void UWPWindow::UWPMouse::SetMousePosition(const Float2& newPosition)
{
const float dpiScale = Impl::Window->_dpiScale;
Impl::Window->GetImpl()->SetMousePosition(newPosition.X / dpiScale, newPosition.Y / dpiScale);
OnMouseMoved(newPosition);
}
void UWPWindow::UWPKeyboard::onCharacterReceived(int key)
{
OnCharInput((Char)key);
}
void UWPWindow::UWPKeyboard::onKeyDown(int key)
{
OnKeyDown(static_cast<KeyboardKeys>(key));
}
void UWPWindow::UWPKeyboard::onKeyUp(int key)
{
OnKeyUp(static_cast<KeyboardKeys>(key));
}
// See GamepadButtons enum
#define UWP_GAMEPAD_Menu 1
#define UWP_GAMEPAD_View 2
#define UWP_GAMEPAD_A 4
#define UWP_GAMEPAD_B 8
#define UWP_GAMEPAD_X 16
#define UWP_GAMEPAD_Y 32
#define UWP_GAMEPAD_DPadUp 64
#define UWP_GAMEPAD_DPadDown 128
#define UWP_GAMEPAD_DPadLeft 256
#define UWP_GAMEPAD_DPadRight 512
#define UWP_GAMEPAD_LeftShoulder 1024
#define UWP_GAMEPAD_RightShoulder 2048
#define UWP_GAMEPAD_LeftThumbstick 4096
#define UWP_GAMEPAD_RightThumbstick 8192
#define UWP_GAMEPAD_Paddle1 16384
#define UWP_GAMEPAD_Paddle2 32768
#define UWP_GAMEPAD_Paddle3 65536
#define UWP_GAMEPAD_Paddle4 131072
void UWPWindow::UWPGamepad::SetVibration(const GamepadVibrationState& state)
{
Gamepad::SetVibration(state);
Window->GetImpl()->SetGamepadVibration(Index, *(UWPGamepadStateVibration*)&state);
}
bool UWPWindow::UWPGamepad::UpdateState()
{
UWPGamepadState state;
Window->GetImpl()->GetGamepadState(Index, state);
// Gather buttons state
const float deadZone = 0.01f;
_state.Buttons[(int32)GamepadButton::A] = !!(state.Buttons & UWP_GAMEPAD_A);
_state.Buttons[(int32)GamepadButton::B] = !!(state.Buttons & UWP_GAMEPAD_B);
_state.Buttons[(int32)GamepadButton::X] = !!(state.Buttons & UWP_GAMEPAD_X);
_state.Buttons[(int32)GamepadButton::Y] = !!(state.Buttons & UWP_GAMEPAD_Y);
_state.Buttons[(int32)GamepadButton::LeftShoulder] = !!(state.Buttons & UWP_GAMEPAD_LeftShoulder);
_state.Buttons[(int32)GamepadButton::RightShoulder] = !!(state.Buttons & UWP_GAMEPAD_RightShoulder);
_state.Buttons[(int32)GamepadButton::Back] = !!(state.Buttons & UWP_GAMEPAD_View);
_state.Buttons[(int32)GamepadButton::Start] = !!(state.Buttons & UWP_GAMEPAD_Menu);
_state.Buttons[(int32)GamepadButton::LeftThumb] = !!(state.Buttons & UWP_GAMEPAD_LeftThumbstick);
_state.Buttons[(int32)GamepadButton::RightThumb] = !!(state.Buttons & UWP_GAMEPAD_RightThumbstick);
_state.Buttons[(int32)GamepadButton::LeftTrigger] = state.LeftTrigger > deadZone;
_state.Buttons[(int32)GamepadButton::RightTrigger] = state.RightTrigger > deadZone;
_state.Buttons[(int32)GamepadButton::DPadUp] = !!(state.Buttons & UWP_GAMEPAD_DPadUp);
_state.Buttons[(int32)GamepadButton::DPadDown] = !!(state.Buttons & UWP_GAMEPAD_DPadDown);
_state.Buttons[(int32)GamepadButton::DPadLeft] = !!(state.Buttons & UWP_GAMEPAD_DPadLeft);
_state.Buttons[(int32)GamepadButton::DPadRight] = !!(state.Buttons & UWP_GAMEPAD_DPadRight);
_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;
// Gather axis 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;
return false;
}
UWPWindow::UWPWindow(const CreateWindowSettings& settings, UWPWindowImpl* impl)
: WindowBase(settings)
, _impl(impl)
, _logicalSize(Float2::Zero)
{
ASSERT(Impl::Window == nullptr);
Impl::Window = this;
// Link
_impl->UserData = this;
_impl->SizeChanged = &::OnSizeChanged;
_impl->VisibilityChanged = &::OnVisibilityChanged;
_impl->DpiChanged = &::OnDpiChanged;
_impl->Closed = &::OnClosed;
_impl->FocusChanged = &::OnFocusChanged;
_impl->KeyDown = &::OnKeyDown;
_impl->KeyUp = &::OnKeyUp;
_impl->CharacterReceived = &::OnCharacterReceived;
_impl->MouseMoved = &::OnMouseMoved;
_impl->PointerPressed = &::OnPointerPressed;
_impl->PointerMoved = &::OnPointerMoved;
_impl->PointerWheelChanged = &::OnPointerWheelChanged;
_impl->PointerReleased = &::OnPointerReleased;
_impl->PointerExited = &::OnPointerExited;
// Peek properties
Char buffer[200];
_impl->GetTitle(buffer, 200);
_title = buffer;
_impl->GetDpi(&_dpi);
_dpiScale = _dpi / 96.0f;
float x, y;
_impl->GetBounds(&x, &y, &_logicalSize.X, &_logicalSize.Y);
_impl->GetMousePosition(&Impl::Mouse->MousePosition.X, &Impl::Mouse->MousePosition.Y);
OnSizeChange();
}
UWPWindow::~UWPWindow()
{
}
void UWPWindow::Show()
{
if (!_visible)
{
// TODO: show
// Base
WindowBase::Show();
}
}
void UWPWindow::Hide()
{
// Not supported
}
void UWPWindow::Minimize()
{
// Not supported
}
void UWPWindow::Maximize()
{
// Not supported
}
void UWPWindow::Restore()
{
// Not supported
}
bool UWPWindow::IsClosed() const
{
return _isClosing;
}
void UWPWindow::BringToFront(bool force)
{
Focus();
}
void UWPWindow::SetClientBounds(const Rectangle& clientArea)
{
// Not supported
}
void UWPWindow::SetPosition(const Float2& position)
{
// Not supported
}
void UWPWindow::SetClientPosition(const Float2& position)
{
SetClientPosition(position);
}
Float2 UWPWindow::GetPosition() const
{
float x, y, width, height;
_impl->GetBounds(&x, &y, &width, &height);
return Float2(x, y);
}
Float2 UWPWindow::GetSize() const
{
return _clientSize;
}
Float2 UWPWindow::GetClientSize() const
{
return GetSize();
}
Float2 UWPWindow::ScreenToClient(const Float2& screenPos) const
{
return screenPos - GetPosition();
}
Float2 UWPWindow::ClientToScreen(const Float2& clientPos) const
{
return clientPos + GetPosition();
}
void UWPWindow::Focus()
{
_impl->Activate();
}
void UWPWindow::SetTitle(const StringView& title)
{
_impl->SetTitle(title.Get());
}
DragDropEffect UWPWindow::DoDragDrop(const StringView& data)
{
// Not supported
return DragDropEffect::None;
}
void UWPWindow::StartTrackingMouse(bool useMouseScreenOffset)
{
if (!_isTrackingMouse)
{
_isTrackingMouse = true;
_trackingMouseOffset = Float2::Zero;
_isUsingMouseOffset = useMouseScreenOffset;
}
}
void UWPWindow::EndTrackingMouse()
{
if (_isTrackingMouse)
{
_isTrackingMouse = false;
}
}
void UWPWindow::SetCursor(CursorType type)
{
_impl->SetCursor((UWPWindowImpl::CursorType)type);
// Base
WindowBase::SetCursor(type);
}
void UWPWindow::onSizeChanged(float width, float height)
{
_logicalSize.X = width;
_logicalSize.Y = height;
OnSizeChange();
}
void UWPWindow::onVisibilityChanged(bool visible)
{
}
void UWPWindow::onDpiChanged(float dpi)
{
if (_dpi != dpi)
{
_dpi = (int)dpi;
_dpiScale = _dpi / 96.0f;
// When the display DPI changes, the logical size of the window (measured in Dips) also changes and needs to be updated
float x, y;
_impl->GetBounds(&x, &y, &_logicalSize.X, &_logicalSize.Y);
OnSizeChange();
}
}
void UWPWindow::onClosed()
{
}
void UWPWindow::onFocusChanged(bool focused)
{
if (focused)
OnGotFocus();
else
OnLostFocus();
}
void UWPWindow::onKeyDown(int key)
{
if (key < 7)
return;
Impl::Keyboard->onKeyDown(key);
}
void UWPWindow::onKeyUp(int key)
{
if (key < 7)
return;
Impl::Keyboard->onKeyUp(key);
}
void UWPWindow::onCharacterReceived(int key)
{
Impl::Keyboard->onCharacterReceived(key);
}
void UWPWindow::onMouseMoved(float x, float y)
{
Impl::Mouse->onMouseMoved(x * _dpiScale, y * _dpiScale);
}
void UWPWindow::onPointerPressed(UWPWindowImpl::PointerData* pointer)
{
if (pointer->IsMouse)
{
pointer->PositionX *= _dpiScale;
pointer->PositionY *= _dpiScale;
Impl::Mouse->onPointerPressed(pointer);
}
// TODO: impl mobile touch support
}
void UWPWindow::onPointerMoved(UWPWindowImpl::PointerData* pointer)
{
if (pointer->IsMouse)
{
pointer->PositionX *= _dpiScale;
pointer->PositionY *= _dpiScale;
Impl::Mouse->onPointerMoved(pointer);
}
// TODO: impl mobile touch support
}
void UWPWindow::onPointerWheelChanged(UWPWindowImpl::PointerData* pointer)
{
if (pointer->IsMouse)
{
pointer->PositionX *= _dpiScale;
pointer->PositionY *= _dpiScale;
Impl::Mouse->onPointerWheelChanged(pointer);
}
}
void UWPWindow::onPointerReleased(UWPWindowImpl::PointerData* pointer)
{
if (pointer->IsMouse)
{
pointer->PositionX *= _dpiScale;
pointer->PositionY *= _dpiScale;
Impl::Mouse->onPointerReleased(pointer);
}
// TODO: impl mobile touch support
}
void UWPWindow::onPointerExited(UWPWindowImpl::PointerData* pointer)
{
if (pointer->IsMouse)
{
pointer->PositionX *= _dpiScale;
pointer->PositionY *= _dpiScale;
Impl::Mouse->onPointerExited(pointer);
}
// TODO: impl mobile touch support
}
void UWPWindow::OnSizeChange()
{
// Update the actual rendering output resolution
_clientSize.X = ConvertDipsToPixels(_logicalSize.X, (float)_dpi);
_clientSize.Y = ConvertDipsToPixels(_logicalSize.Y, (float)_dpi);
// Check if output has been created
if (_swapChain != nullptr)
{
const int32 width = Math::Max((int32)_clientSize.X, 0);
const int32 height = Math::Max((int32)_clientSize.Y, 0);
if (width > 0 && height > 0 && (width != _swapChain->GetWidth() || height != _swapChain->GetHeight()))
{
OnResize(width, height);
}
}
}
#endif