#include "lib/win/UWPCommon.hpp" #include "boo/IApplication.hpp" #include "boo/IGraphicsContext.hpp" #include "boo/IWindow.hpp" #include "boo/audiodev/IAudioVoiceEngine.hpp" #include "boo/graphicsdev/D3D.hpp" #include using namespace Windows::UI; using namespace Windows::UI::Core; using namespace Windows::UI::ViewManagement; using namespace Windows::System; using namespace Windows::Graphics::Display; using namespace Windows::Foundation; using namespace Platform; #include namespace boo { static logvisor::Module Log("boo::WindowWin32"); #if _WIN32_WINNT_WIN10 IGraphicsCommandQueue* _NewD3D12CommandQueue(D3D12Context* ctx, D3D12Context::Window* windowCtx, IGraphicsContext* parent, ID3D12CommandQueue** cmdQueueOut); IGraphicsDataFactory* _NewD3D12DataFactory(D3D12Context* ctx, IGraphicsContext* parent, uint32_t sampleCount); #endif IGraphicsCommandQueue* _NewD3D11CommandQueue(D3D11Context* ctx, D3D11Context::Window* windowCtx, IGraphicsContext* parent); IGraphicsDataFactory* _NewD3D11DataFactory(D3D11Context* ctx, IGraphicsContext* parent, uint32_t sampleCount); struct GraphicsContextUWP : IGraphicsContext { EGraphicsAPI m_api; EPixelFormat m_pf; IWindow* m_parentWindow; Boo3DAppContextUWP& m_3dCtx; ComPtr m_output; GraphicsContextUWP(EGraphicsAPI api, IWindow* parentWindow, Boo3DAppContextUWP& b3dCtx) : m_api(api), m_pf(EPixelFormat::RGBA8), m_parentWindow(parentWindow), m_3dCtx(b3dCtx) {} virtual void resized(const SWindowRect& rect) { m_3dCtx.resize(m_parentWindow, rect.size[0], rect.size[1]); } }; struct GraphicsContextUWPD3D : GraphicsContextUWP { ComPtr m_swapChain; IGraphicsCommandQueue* m_commandQueue = nullptr; IGraphicsDataFactory* m_dataFactory = nullptr; public: IWindowCallback* m_callback; GraphicsContextUWPD3D(EGraphicsAPI api, IWindow* parentWindow, Agile& coreWindow, Boo3DAppContextUWP& b3dCtx) : GraphicsContextUWP(api, parentWindow, b3dCtx) { /* Create Swap Chain */ DXGI_SWAP_CHAIN_DESC1 scDesc = {}; scDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; scDesc.SampleDesc.Count = 1; scDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; scDesc.BufferCount = 2; #if !WINDOWS_STORE scDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; #else scDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; #endif scDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH; IUnknown* cw = reinterpret_cast(coreWindow.Get()); #if _WIN32_WINNT_WIN10 if (b3dCtx.m_ctx12.m_dev) { auto insIt = b3dCtx.m_ctx12.m_windows.emplace(std::make_pair(parentWindow, D3D12Context::Window())); D3D12Context::Window& w = insIt.first->second; ID3D12CommandQueue* cmdQueue; m_dataFactory = _NewD3D12DataFactory(&b3dCtx.m_ctx12, this); m_commandQueue = _NewD3D12CommandQueue(&b3dCtx.m_ctx12, &w, this, &cmdQueue); scDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; HRESULT hr = b3dCtx.m_ctx12.m_dxFactory->CreateSwapChainForCoreWindow(cmdQueue, cw, &scDesc, nullptr, &m_swapChain); if (FAILED(hr)) Log.report(logvisor::Fatal, fmt("unable to create swap chain")); m_swapChain.As(&w.m_swapChain); ComPtr fb; m_swapChain->GetBuffer(0, __uuidof(ID3D12Resource), &fb); w.m_backBuf = w.m_swapChain->GetCurrentBackBufferIndex(); D3D12_RESOURCE_DESC resDesc = fb->GetDesc(); w.width = resDesc.Width; w.height = resDesc.Height; if (FAILED(m_swapChain->GetContainingOutput(&m_output))) Log.report(logvisor::Fatal, fmt("unable to get DXGI output")); } else #endif { if (FAILED(b3dCtx.m_ctx11.m_dxFactory->CreateSwapChainForCoreWindow(b3dCtx.m_ctx11.m_dev.Get(), cw, &scDesc, nullptr, &m_swapChain))) Log.report(logvisor::Fatal, fmt("unable to create swap chain")); auto insIt = b3dCtx.m_ctx11.m_windows.emplace(std::make_pair(parentWindow, D3D11Context::Window())); D3D11Context::Window& w = insIt.first->second; m_swapChain.As(&w.m_swapChain); ComPtr fbRes; m_swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), &fbRes); D3D11_TEXTURE2D_DESC resDesc; fbRes->GetDesc(&resDesc); w.width = resDesc.Width; w.height = resDesc.Height; m_dataFactory = _NewD3D11DataFactory(&b3dCtx.m_ctx11, this); m_commandQueue = _NewD3D11CommandQueue(&b3dCtx.m_ctx11, &insIt.first->second, this); if (FAILED(m_swapChain->GetContainingOutput(&m_output))) Log.report(logvisor::Fatal, fmt("unable to get DXGI output")); } } ~GraphicsContextUWPD3D() override { #if _WIN32_WINNT_WIN10 if (m_3dCtx.m_ctx12.m_dev) m_3dCtx.m_ctx12.m_windows.erase(m_parentWindow); else #endif m_3dCtx.m_ctx11.m_windows.erase(m_parentWindow); } void _setCallback(IWindowCallback* cb) override { m_callback = cb; } EGraphicsAPI getAPI() const override { return m_api; } EPixelFormat getPixelFormat() const override { return m_pf; } void setPixelFormat(EPixelFormat pf) override { if (pf > EPixelFormat::RGBAF32_Z24) return; m_pf = pf; } bool initializeContext(void*) override { return true; } void makeCurrent() override {} void postInit() override {} void present() override {} IGraphicsCommandQueue* getCommandQueue() override { return m_commandQueue; } IGraphicsDataFactory* getDataFactory() override { return m_dataFactory; } IGraphicsDataFactory* getMainContextDataFactory() override { return m_dataFactory; } IGraphicsDataFactory* getLoadContextDataFactory() override { return m_dataFactory; } }; static uint32_t translateKeysym(CoreWindow ^ window, VirtualKey sym, ESpecialKey& specialSym, EModifierKey& modifierSym) { specialSym = ESpecialKey::None; modifierSym = EModifierKey::None; if (sym >= VirtualKey::F1 && sym <= VirtualKey::F12) specialSym = ESpecialKey(uint32_t(ESpecialKey::F1) + uint32_t(sym - VirtualKey::F1)); else if (sym == VirtualKey::Escape) specialSym = ESpecialKey::Esc; else if (sym == VirtualKey::Enter) specialSym = ESpecialKey::Enter; else if (sym == VirtualKey::Back) specialSym = ESpecialKey::Backspace; else if (sym == VirtualKey::Insert) specialSym = ESpecialKey::Insert; else if (sym == VirtualKey::Delete) specialSym = ESpecialKey::Delete; else if (sym == VirtualKey::Home) specialSym = ESpecialKey::Home; else if (sym == VirtualKey::End) specialSym = ESpecialKey::End; else if (sym == VirtualKey::PageUp) specialSym = ESpecialKey::PgUp; else if (sym == VirtualKey::PageDown) specialSym = ESpecialKey::PgDown; else if (sym == VirtualKey::Left) specialSym = ESpecialKey::Left; else if (sym == VirtualKey::Right) specialSym = ESpecialKey::Right; else if (sym == VirtualKey::Up) specialSym = ESpecialKey::Up; else if (sym == VirtualKey::Down) specialSym = ESpecialKey::Down; else if (sym == VirtualKey::Shift) modifierSym = EModifierKey::Shift; else if (sym == VirtualKey::Control) modifierSym = EModifierKey::Ctrl; else if (sym == VirtualKey::Menu) modifierSym = EModifierKey::Alt; else if (sym >= VirtualKey::A && sym <= VirtualKey::Z) return uint32_t(sym - VirtualKey::A) + (window->GetKeyState(VirtualKey::Shift) != CoreVirtualKeyStates::None) ? 'A' : 'a'; return 0; } static EModifierKey translateModifiers(CoreWindow ^ window) { EModifierKey retval = EModifierKey::None; if (window->GetKeyState(VirtualKey::Shift) != CoreVirtualKeyStates::None) retval |= EModifierKey::Shift; if (window->GetKeyState(VirtualKey::Control) != CoreVirtualKeyStates::None) retval |= EModifierKey::Ctrl; if (window->GetKeyState(VirtualKey::Menu) != CoreVirtualKeyStates::None) retval |= EModifierKey::Alt; return retval; } class WindowUWP : public IWindow { friend struct GraphicsContextUWP; ApplicationView ^ m_appView = ApplicationView::GetForCurrentView(); Platform::Agile m_coreWindow; Rect m_bounds; float m_dispInfoDpiFactor = 1.f; std::unique_ptr m_gfxCtx; IWindowCallback* m_callback = nullptr; public: ref struct EventReceiver sealed { void OnKeyDown(CoreWindow ^ window, KeyEventArgs ^ keyEventArgs) { w.OnKeyDown(window, keyEventArgs); } void OnKeyUp(CoreWindow ^ window, KeyEventArgs ^ keyEventArgs) { w.OnKeyUp(window, keyEventArgs); } void OnPointerEntered(CoreWindow ^ window, PointerEventArgs ^ args) { w.OnPointerEntered(window, args); } void OnPointerExited(CoreWindow ^ window, PointerEventArgs ^ args) { w.OnPointerExited(window, args); } void OnPointerMoved(CoreWindow ^ window, PointerEventArgs ^ args) { w.OnPointerMoved(window, args); } void OnPointerPressed(CoreWindow ^ window, PointerEventArgs ^ args) { w.OnPointerPressed(window, args); } void OnPointerReleased(CoreWindow ^ window, PointerEventArgs ^ args) { w.OnPointerReleased(window, args); } void OnPointerWheelChanged(CoreWindow ^ window, PointerEventArgs ^ args) { w.OnPointerWheelChanged(window, args); } void OnClosed(CoreWindow ^ sender, CoreWindowEventArgs ^ args) { w.OnClosed(sender, args); } void SizeChanged(CoreWindow ^ window, WindowSizeChangedEventArgs ^) { w.m_bounds = window->Bounds; w._resized(); } void DisplayInfoChanged(DisplayInformation ^ di, Object ^) { w.m_dispInfoDpiFactor = di->LogicalDpi / 96.f; w._resized(); } internal : WindowUWP& w; EventReceiver(WindowUWP& w) : w(w) { w.m_coreWindow->KeyDown += ref new TypedEventHandler(this, &EventReceiver::OnKeyDown); w.m_coreWindow->KeyUp += ref new TypedEventHandler(this, &EventReceiver::OnKeyUp); w.m_coreWindow->PointerEntered += ref new TypedEventHandler(this, &EventReceiver::OnPointerEntered); w.m_coreWindow->PointerExited += ref new TypedEventHandler(this, &EventReceiver::OnPointerExited); w.m_coreWindow->PointerMoved += ref new TypedEventHandler(this, &EventReceiver::OnPointerMoved); w.m_coreWindow->PointerPressed += ref new TypedEventHandler(this, &EventReceiver::OnPointerPressed); w.m_coreWindow->PointerReleased += ref new TypedEventHandler(this, &EventReceiver::OnPointerReleased); w.m_coreWindow->PointerWheelChanged += ref new TypedEventHandler(this, &EventReceiver::OnPointerWheelChanged); w.m_coreWindow->Closed += ref new TypedEventHandler(this, &EventReceiver::OnClosed); w.m_coreWindow->SizeChanged += ref new TypedEventHandler(this, &EventReceiver::SizeChanged); DisplayInformation::GetForCurrentView()->DpiChanged += ref new TypedEventHandler(this, &EventReceiver::DisplayInfoChanged); } }; EventReceiver ^ m_eventReceiver; WindowUWP(SystemStringView title, Boo3DAppContextUWP& b3dCtx) : m_coreWindow(CoreWindow::GetForCurrentThread()), m_eventReceiver(ref new EventReceiver(*this)) { IGraphicsContext::EGraphicsAPI api = IGraphicsContext::EGraphicsAPI::D3D11; #if _WIN32_WINNT_WIN10 if (b3dCtx.m_ctx12.m_dev) api = IGraphicsContext::EGraphicsAPI::D3D12; #endif m_gfxCtx.reset(new GraphicsContextUWPD3D(api, this, m_coreWindow, b3dCtx)); setTitle(title); m_bounds = m_coreWindow->Bounds; m_dispInfoDpiFactor = DisplayInformation::GetForCurrentView()->LogicalDpi / 96.f; if (auto titleBar = ApplicationView::GetForCurrentView()->TitleBar) { Color grey = {0xFF, 0x33, 0x33, 0x33}; Color transWhite = {0xFF, 0x88, 0x88, 0x88}; titleBar->ButtonBackgroundColor = grey; titleBar->ButtonForegroundColor = Colors::White; titleBar->BackgroundColor = grey; titleBar->ForegroundColor = Colors::White; titleBar->ButtonInactiveBackgroundColor = grey; titleBar->ButtonInactiveForegroundColor = transWhite; titleBar->InactiveBackgroundColor = grey; titleBar->InactiveForegroundColor = transWhite; } } ~WindowUWP() override = default; void setCallback(IWindowCallback* cb) override { m_callback = cb; } void closeWindow() override { m_coreWindow->Close(); } void showWindow() override {} void hideWindow() override {} SystemString getTitle() override { return SystemString(m_appView->Title->Data()); } void setTitle(SystemStringView title) override { m_appView->Title = ref new Platform::String(title.data()); } void setCursor(EMouseCursor cursor) override {} void setWaitCursor(bool wait) override {} double getWindowRefreshRate() const { /* TODO: Actually get refresh rate */ return 60.0; } void setWindowFrameDefault() override {} void getWindowFrame(float& xOut, float& yOut, float& wOut, float& hOut) const override { xOut = m_bounds.X * m_dispInfoDpiFactor; yOut = m_bounds.Y * m_dispInfoDpiFactor; wOut = m_bounds.Width * m_dispInfoDpiFactor; hOut = m_bounds.Height * m_dispInfoDpiFactor; } void getWindowFrame(int& xOut, int& yOut, int& wOut, int& hOut) const override { xOut = m_bounds.X * m_dispInfoDpiFactor; yOut = m_bounds.Y * m_dispInfoDpiFactor; wOut = m_bounds.Width * m_dispInfoDpiFactor; hOut = m_bounds.Height * m_dispInfoDpiFactor; } void setWindowFrame(float x, float y, float w, float h) override {} void setWindowFrame(int x, int y, int w, int h) override {} float getVirtualPixelFactor() const override { return m_dispInfoDpiFactor; } bool isFullscreen() const override { return ApplicationView::GetForCurrentView()->IsFullScreenMode; } void setFullscreen(bool fs) override { if (fs) ApplicationView::GetForCurrentView()->TryEnterFullScreenMode(); else ApplicationView::GetForCurrentView()->ExitFullScreenMode(); } void claimKeyboardFocus(const int coord[2]) override {} bool clipboardCopy(EClipboardType type, const uint8_t* data, size_t sz) override { return false; } std::unique_ptr clipboardPaste(EClipboardType type, size_t& sz) override { return std::unique_ptr(); } int waitForRetrace(IAudioVoiceEngine* engine) override { if (engine) engine->pumpAndMixVoices(); m_gfxCtx->m_output->WaitForVBlank(); return 1; } uintptr_t getPlatformHandle() const override { return 0; } bool _incomingEvent(void* ev) override { return false; } void OnKeyDown(CoreWindow ^ window, KeyEventArgs ^ keyEventArgs) { ESpecialKey specialKey; EModifierKey modifierKey; uint32_t charCode = translateKeysym(m_coreWindow.Get(), keyEventArgs->VirtualKey, specialKey, modifierKey); EModifierKey modifierMask = translateModifiers(window); bool repeat = keyEventArgs->KeyStatus.RepeatCount > 1; if (charCode) m_callback->charKeyDown(charCode, modifierMask, repeat); else if (specialKey != ESpecialKey::None) m_callback->specialKeyDown(specialKey, modifierMask, repeat); else if (modifierKey != EModifierKey::None) m_callback->modKeyDown(modifierKey, repeat); } void OnKeyUp(CoreWindow ^ window, KeyEventArgs ^ keyEventArgs) { ESpecialKey specialKey; EModifierKey modifierKey; uint32_t charCode = translateKeysym(m_coreWindow.Get(), keyEventArgs->VirtualKey, specialKey, modifierKey); EModifierKey modifierMask = translateModifiers(window); if (charCode) m_callback->charKeyUp(charCode, modifierMask); else if (specialKey != ESpecialKey::None) m_callback->specialKeyUp(specialKey, modifierMask); else if (modifierKey != EModifierKey::None) m_callback->modKeyUp(modifierKey); } SWindowCoord GetCursorCoords(const Point& point) { SWindowCoord coord = {point.X * m_dispInfoDpiFactor, (m_bounds.Height - point.Y) * m_dispInfoDpiFactor, point.X, m_bounds.Height - point.Y, point.X / m_bounds.Width, (m_bounds.Height - point.Y) / m_bounds.Height}; return coord; } void OnPointerEntered(CoreWindow ^ window, PointerEventArgs ^ args) { m_callback->mouseEnter(GetCursorCoords(args->CurrentPoint->Position)); } void OnPointerExited(CoreWindow ^ window, PointerEventArgs ^ args) { m_callback->mouseLeave(GetCursorCoords(args->CurrentPoint->Position)); } void OnPointerMoved(CoreWindow ^ window, PointerEventArgs ^ args) { m_callback->mouseMove(GetCursorCoords(args->CurrentPoint->Position)); } boo::EMouseButton m_pressedButton = boo::EMouseButton::None; void OnPointerPressed(CoreWindow ^ window, PointerEventArgs ^ args) { auto properties = args->CurrentPoint->Properties; boo::EMouseButton button = boo::EMouseButton::None; if (properties->IsLeftButtonPressed) button = boo::EMouseButton::Primary; else if (properties->IsMiddleButtonPressed) button = boo::EMouseButton::Middle; else if (properties->IsRightButtonPressed) button = boo::EMouseButton::Secondary; else if (properties->IsXButton1Pressed) button = boo::EMouseButton::Aux1; else if (properties->IsXButton2Pressed) button = boo::EMouseButton::Aux2; m_callback->mouseDown(GetCursorCoords(args->CurrentPoint->Position), button, translateModifiers(m_coreWindow.Get())); m_pressedButton = button; } void OnPointerReleased(CoreWindow ^ window, PointerEventArgs ^ args) { auto properties = args->CurrentPoint->Properties; m_callback->mouseUp(GetCursorCoords(args->CurrentPoint->Position), m_pressedButton, translateModifiers(m_coreWindow.Get())); } void OnPointerWheelChanged(CoreWindow ^ window, PointerEventArgs ^ args) { auto properties = args->CurrentPoint->Properties; SScrollDelta scroll = {}; scroll.delta[1] = properties->MouseWheelDelta / double(WHEEL_DELTA); m_callback->scroll(GetCursorCoords(args->CurrentPoint->Position), scroll); } void OnClosed(CoreWindow ^ sender, CoreWindowEventArgs ^ args) { if (m_callback) m_callback->destroyed(); } void _resized() { boo::SWindowRect rect(m_bounds.X * m_dispInfoDpiFactor, m_bounds.Y * m_dispInfoDpiFactor, m_bounds.Width * m_dispInfoDpiFactor, m_bounds.Height * m_dispInfoDpiFactor); m_gfxCtx->resized(rect); if (m_callback) m_callback->resized(rect, false); } ETouchType getTouchType() const override { return ETouchType::None; } void setStyle(EWindowStyle style) override {} EWindowStyle getStyle() const override { EWindowStyle retval = EWindowStyle::None; return retval; } IGraphicsCommandQueue* getCommandQueue() override { return m_gfxCtx->getCommandQueue(); } IGraphicsDataFactory* getDataFactory() override { return m_gfxCtx->getDataFactory(); } /* Creates a new context on current thread!! Call from main client thread */ IGraphicsDataFactory* getMainContextDataFactory() override { return m_gfxCtx->getMainContextDataFactory(); } /* Creates a new context on current thread!! Call from client loading thread */ IGraphicsDataFactory* getLoadContextDataFactory() override { return m_gfxCtx->getLoadContextDataFactory(); } }; std::shared_ptr _WindowUWPNew(SystemStringView title, Boo3DAppContextUWP& d3dCtx) { return std::make_shared(title, d3dCtx); } } // namespace boo