diff --git a/Aerofoil/GpAppEnvironment.cpp b/Aerofoil/GpAppEnvironment.cpp index 9faec13..bde43d5 100644 --- a/Aerofoil/GpAppEnvironment.cpp +++ b/Aerofoil/GpAppEnvironment.cpp @@ -94,9 +94,9 @@ void GpAppEnvironment::Render() GpAppInterface_Get()->PL_Render(m_displayDriver); } -bool GpAppEnvironment::AdjustRequestedResolution(unsigned int &width, unsigned int &height) +bool GpAppEnvironment::AdjustRequestedResolution(uint32_t &physicalWidth, uint32_t &physicalHeight, uint32_t &virtualWidth, uint32_t &virtualheight, float &pixelScaleX, float &pixelScaleY) { - return GpAppInterface_Get()->PL_AdjustRequestedResolution(width, height); + return GpAppInterface_Get()->PL_AdjustRequestedResolution(physicalWidth, physicalHeight, virtualWidth, virtualheight, pixelScaleX, pixelScaleY); } void GpAppEnvironment::SetDisplayDriver(IGpDisplayDriver *displayDriver) @@ -175,6 +175,10 @@ void GpAppEnvironment::DispatchSystemCall(PortabilityLayer::HostSuspendCallID ca m_applicationState = ApplicationState_TimedSuspend; m_delaySuspendTicks = args[0].m_uint; break; + case PortabilityLayer::HostSuspendCallID_CallOnVOSThread: + args[0].m_functionPtr(static_cast(args[1].m_constPointer), static_cast(args[2].m_pointer)); + m_applicationState = ApplicationState_Running; + break; default: assert(false); } diff --git a/Aerofoil/GpAppEnvironment.h b/Aerofoil/GpAppEnvironment.h index 4b3cc3f..a4f8593 100644 --- a/Aerofoil/GpAppEnvironment.h +++ b/Aerofoil/GpAppEnvironment.h @@ -28,7 +28,7 @@ public: GpDisplayDriverTickStatus_t Tick(IGpFiber *vosFiber); void Render(); - bool AdjustRequestedResolution(unsigned int &width, unsigned int &height); + bool AdjustRequestedResolution(uint32_t &physicalWidth, uint32_t &physicalHeight, uint32_t &virtualWidth, uint32_t &virtualheight, float &pixelScaleX, float &pixelScaleY); void SetDisplayDriver(IGpDisplayDriver *displayDriver); void SetAudioDriver(IGpAudioDriver *audioDriver); diff --git a/Aerofoil/GpFileSystem_Win32.cpp b/Aerofoil/GpFileSystem_Win32.cpp index 0a8d208..eb27b22 100644 --- a/Aerofoil/GpFileSystem_Win32.cpp +++ b/Aerofoil/GpFileSystem_Win32.cpp @@ -13,6 +13,8 @@ #include +extern GpWindowsGlobals g_gpWindowsGlobals; + class GpDirectoryCursor_Win32 final : public PortabilityLayer::HostDirectoryCursor { public: @@ -307,6 +309,7 @@ bool GpFileSystem_Win32::PromptSaveFile(PortabilityLayer::VirtualDirectory_t vir ofn.nMaxFile = MAX_PATH; ofn.lpstrInitialDir = baseDir; ofn.Flags = OFN_EXPLORER | OFN_NOCHANGEDIR | OFN_OVERWRITEPROMPT; + ofn.hwndOwner = g_gpWindowsGlobals.m_hwnd; if (!GetSaveFileNameW(&ofn)) return false; @@ -387,6 +390,7 @@ bool GpFileSystem_Win32::PromptOpenFile(PortabilityLayer::VirtualDirectory_t vir ofn.nMaxFile = MAX_PATH; ofn.lpstrInitialDir = baseDir; ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST; + ofn.hwndOwner = g_gpWindowsGlobals.m_hwnd; if (!GetOpenFileNameW(&ofn)) return false; diff --git a/Aerofoil/GpMain.cpp b/Aerofoil/GpMain.cpp index d79cedb..0ff86b2 100644 --- a/Aerofoil/GpMain.cpp +++ b/Aerofoil/GpMain.cpp @@ -28,9 +28,9 @@ namespace static_cast(context)->Render(); } - bool AdjustRequestedResolution(void *context, unsigned int &width, unsigned int &height) + bool AdjustRequestedResolution(void *context, uint32_t &physicalWidth, uint32_t &physicalHeight, uint32_t &virtualWidth, uint32_t &virtualheight, float &pixelScaleX, float &pixelScaleY) { - return static_cast(context)->AdjustRequestedResolution(width, height); + return static_cast(context)->AdjustRequestedResolution(physicalWidth, physicalHeight, virtualWidth, virtualheight, pixelScaleX, pixelScaleY); } } diff --git a/Aerofoil/GpMain_Win32.cpp b/Aerofoil/GpMain_Win32.cpp index ec3990c..c484d56 100644 --- a/Aerofoil/GpMain_Win32.cpp +++ b/Aerofoil/GpMain_Win32.cpp @@ -24,7 +24,7 @@ extern "C" __declspec(dllimport) IGpAudioDriver *GpDriver_CreateAudioDriver_XAud extern "C" __declspec(dllimport) IGpDisplayDriver *GpDriver_CreateDisplayDriver_D3D11(const GpDisplayDriverProperties &properties); extern "C" __declspec(dllimport) IGpInputDriver *GpDriver_CreateInputDriver_XInput(const GpInputDriverProperties &properties); -static void PostMouseEvent(IGpVOSEventQueue *eventQueue, GpMouseEventType_t eventType, GpMouseButton_t button, int32_t x, int32_t y) +static void PostMouseEvent(IGpVOSEventQueue *eventQueue, GpMouseEventType_t eventType, GpMouseButton_t button, int32_t x, int32_t y, float pixelScaleX, float pixelScaleY) { if (GpVOSEvent *evt = eventQueue->QueueEvent()) { @@ -35,6 +35,12 @@ static void PostMouseEvent(IGpVOSEventQueue *eventQueue, GpMouseEventType_t even mEvent.m_x = x; mEvent.m_y = y; mEvent.m_eventType = eventType; + + if (pixelScaleX != 1.0f) + mEvent.m_x = static_cast(static_cast(x) / pixelScaleX); + + if (pixelScaleY != 1.0f) + mEvent.m_y = static_cast(static_cast(y) / pixelScaleX); } } @@ -291,7 +297,7 @@ static void PostKeyboardEvent(IGpVOSEventQueue *eventQueue, GpKeyboardInputEvent } } -static void TranslateWindowsMessage(const MSG *msg, IGpVOSEventQueue *eventQueue) +static void TranslateWindowsMessage(const MSG *msg, IGpVOSEventQueue *eventQueue, float pixelScaleX, float pixelScaleY) { WPARAM wParam = msg->wParam; LPARAM lParam = msg->lParam; @@ -299,40 +305,40 @@ static void TranslateWindowsMessage(const MSG *msg, IGpVOSEventQueue *eventQueue switch (msg->message) { case WM_LBUTTONDOWN: - PostMouseEvent(eventQueue, GpMouseEventTypes::kDown, GpMouseButtons::kLeft, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + PostMouseEvent(eventQueue, GpMouseEventTypes::kDown, GpMouseButtons::kLeft, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), pixelScaleX, pixelScaleY); break; case WM_LBUTTONUP: - PostMouseEvent(eventQueue, GpMouseEventTypes::kUp, GpMouseButtons::kLeft, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + PostMouseEvent(eventQueue, GpMouseEventTypes::kUp, GpMouseButtons::kLeft, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), pixelScaleX, pixelScaleY); break; case WM_MBUTTONDOWN: - PostMouseEvent(eventQueue, GpMouseEventTypes::kDown, GpMouseButtons::kMiddle, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + PostMouseEvent(eventQueue, GpMouseEventTypes::kDown, GpMouseButtons::kMiddle, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), pixelScaleX, pixelScaleY); break; case WM_MBUTTONUP: - PostMouseEvent(eventQueue, GpMouseEventTypes::kUp, GpMouseButtons::kMiddle, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + PostMouseEvent(eventQueue, GpMouseEventTypes::kUp, GpMouseButtons::kMiddle, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), pixelScaleX, pixelScaleY); break; case WM_RBUTTONDOWN: - PostMouseEvent(eventQueue, GpMouseEventTypes::kDown, GpMouseButtons::kRight, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + PostMouseEvent(eventQueue, GpMouseEventTypes::kDown, GpMouseButtons::kRight, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), pixelScaleX, pixelScaleY); break; case WM_RBUTTONUP: - PostMouseEvent(eventQueue, GpMouseEventTypes::kUp, GpMouseButtons::kRight, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + PostMouseEvent(eventQueue, GpMouseEventTypes::kUp, GpMouseButtons::kRight, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), pixelScaleX, pixelScaleY); break; case WM_XBUTTONDOWN: if (GET_XBUTTON_WPARAM(wParam) == XBUTTON1) - PostMouseEvent(eventQueue, GpMouseEventTypes::kDown, GpMouseButtons::kX1, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + PostMouseEvent(eventQueue, GpMouseEventTypes::kDown, GpMouseButtons::kX1, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), pixelScaleX, pixelScaleY); else if (GET_XBUTTON_WPARAM(wParam) == XBUTTON2) - PostMouseEvent(eventQueue, GpMouseEventTypes::kDown, GpMouseButtons::kX2, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + PostMouseEvent(eventQueue, GpMouseEventTypes::kDown, GpMouseButtons::kX2, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), pixelScaleX, pixelScaleY); break; case WM_XBUTTONUP: if (GET_XBUTTON_WPARAM(wParam) == XBUTTON1) - PostMouseEvent(eventQueue, GpMouseEventTypes::kUp, GpMouseButtons::kX1, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + PostMouseEvent(eventQueue, GpMouseEventTypes::kUp, GpMouseButtons::kX1, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), pixelScaleX, pixelScaleY); else if (GET_XBUTTON_WPARAM(wParam) == XBUTTON2) - PostMouseEvent(eventQueue, GpMouseEventTypes::kUp, GpMouseButtons::kX2, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + PostMouseEvent(eventQueue, GpMouseEventTypes::kUp, GpMouseButtons::kX2, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), pixelScaleX, pixelScaleY); break; case WM_MOUSEMOVE: - PostMouseEvent(eventQueue, GpMouseEventTypes::kMove, GpMouseButtons::kNone, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + PostMouseEvent(eventQueue, GpMouseEventTypes::kMove, GpMouseButtons::kNone, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), pixelScaleX, pixelScaleY); break; case WM_MOUSELEAVE: - PostMouseEvent(eventQueue, GpMouseEventTypes::kLeave, GpMouseButtons::kNone, 0, 0); + PostMouseEvent(eventQueue, GpMouseEventTypes::kLeave, GpMouseButtons::kNone, 0, 0, pixelScaleX, pixelScaleY); break; case WM_KEYDOWN: case WM_SYSKEYDOWN: @@ -388,6 +394,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine g_gpWindowsGlobals.m_cmdLine = lpCmdLine; g_gpWindowsGlobals.m_nCmdShow = nCmdShow; g_gpWindowsGlobals.m_baseDir = GpFileSystem_Win32::GetInstance()->GetBasePath(); + g_gpWindowsGlobals.m_hwnd = nullptr; g_gpWindowsGlobals.m_createFiberFunc = GpFiber_Win32::Create; g_gpWindowsGlobals.m_loadCursorFunc = GpCursor_Win32::Load; diff --git a/GpApp/Environ.cpp b/GpApp/Environ.cpp index ebfdbfa..195a0a6 100644 --- a/GpApp/Environ.cpp +++ b/GpApp/Environ.cpp @@ -17,6 +17,8 @@ #include "IGpDisplayDriver.h" #include "WindowManager.h" +#include + #define kSwitchDepthAlert 130 #define kSetMemoryAlert 180 #define kLowMemoryAlert 181 @@ -344,8 +346,20 @@ public: HandleResolutionChange(prevWidth, prevHeight, newWidth, newHeight); } - void AdjustRequestedResolution(uint32_t &width, uint32_t &height) override + void AdjustRequestedResolution(uint32_t &physicalWidth, uint32_t &physicalHeight, uint32_t &virtualWidth, uint32_t &virtualHeight, float &pixelScaleX, float &pixelScaleY) override { + double xMul = static_cast(physicalWidth) / 640; + double yMul = static_cast(physicalHeight) / 480; + + xMul = floor(xMul); + yMul = floor(yMul); + + double minMul = std::max(1.0, std::min(xMul, yMul)); + + virtualWidth = physicalWidth / minMul; + virtualHeight = physicalHeight / minMul; + pixelScaleX = static_cast(minMul); + pixelScaleY = static_cast(minMul); } static GpAppResolutionChangeHandler ms_instance; diff --git a/GpApp/GpAppInterface.cpp b/GpApp/GpAppInterface.cpp index 9c01d3b..df6c90c 100644 --- a/GpApp/GpAppInterface.cpp +++ b/GpApp/GpAppInterface.cpp @@ -25,7 +25,7 @@ public: void PL_HostFontHandler_SetInstance(PortabilityLayer::HostFontHandler *instance) override; void PL_HostVOSEventQueue_SetInstance(PortabilityLayer::HostVOSEventQueue *instance) override; void PL_InstallHostSuspendHook(PortabilityLayer::HostSuspendHook_t hook, void *context) override; - bool PL_AdjustRequestedResolution(unsigned int &width, unsigned int &height) override; + bool PL_AdjustRequestedResolution(uint32_t &physicalWidth, uint32_t &physicalHeight, uint32_t &virtualWidth, uint32_t &virtualheight, float &pixelScaleX, float &pixelScaleY) override; }; @@ -80,18 +80,14 @@ void GpAppInterfaceImpl::PL_InstallHostSuspendHook(PortabilityLayer::HostSuspend PortabilityLayer::InstallHostSuspendHook(hook, context); } -bool GpAppInterfaceImpl::PL_AdjustRequestedResolution(unsigned int &width, unsigned int &height) +bool GpAppInterfaceImpl::PL_AdjustRequestedResolution(uint32_t &physicalWidth, uint32_t &physicalHeight, uint32_t &virtualWidth, uint32_t &virtualheight, float &pixelScaleX, float &pixelScaleY) { PortabilityLayer::DisplayDeviceManager::IResolutionChangeHandler *handler = PortabilityLayer::DisplayDeviceManager::GetInstance()->GetResolutionChangeHandler(); if (!handler) return false; - uint32_t w32 = width; - uint32_t h32 = height; - handler->AdjustRequestedResolution(w32, h32); - width = w32; - height = h32; + handler->AdjustRequestedResolution(physicalWidth, physicalHeight, virtualWidth, virtualheight, pixelScaleX, pixelScaleY); return true; } diff --git a/GpCommon/GpDisplayDriverProperties.h b/GpCommon/GpDisplayDriverProperties.h index f479420..24d3ab7 100644 --- a/GpCommon/GpDisplayDriverProperties.h +++ b/GpCommon/GpDisplayDriverProperties.h @@ -3,6 +3,8 @@ #include "EGpDisplayDriverType.h" #include "GpDisplayDriverTickStatus.h" +#include + struct IGpDisplayDriver; struct IGpFiber; struct IGpVOSEventQueue; @@ -11,7 +13,7 @@ struct GpDisplayDriverProperties { typedef GpDisplayDriverTickStatus_t (*TickFunc_t)(void *context, IGpFiber *vosFiber); typedef void(*RenderFunc_t)(void *context); - typedef bool(*AdjustRequestedResolutionFunc_t)(void *context, unsigned int &width, unsigned int &height); + typedef bool(*AdjustRequestedResolutionFunc_t)(void *context, uint32_t &physicalWidth, uint32_t &physicalHeight, uint32_t &virtualWidth, uint32_t &virtualheight, float &pixelScaleX, float &pixelScaleY); EGpDisplayDriverType m_type; diff --git a/GpCommon/GpWindows.h b/GpCommon/GpWindows.h index 1bfb8a4..c0ded57 100644 --- a/GpCommon/GpWindows.h +++ b/GpCommon/GpWindows.h @@ -21,9 +21,10 @@ struct GpWindowsGlobals HINSTANCE m_hPrevInstance; LPCSTR m_cmdLine; LPCWSTR m_baseDir; + HWND m_hwnd; int m_nCmdShow; IGpFiber *(*m_createFiberFunc)(LPVOID fiber); IGpCursor_Win32 *(*m_loadCursorFunc)(const wchar_t *path); - void (*m_translateWindowsMessageFunc)(const MSG *msg, IGpVOSEventQueue *eventQueue); + void (*m_translateWindowsMessageFunc)(const MSG *msg, IGpVOSEventQueue *eventQueue, float pixelScaleX, float pixelScaleY); }; diff --git a/GpCommon/IGpDisplayDriver.h b/GpCommon/IGpDisplayDriver.h index 98474cb..332cd97 100644 --- a/GpCommon/IGpDisplayDriver.h +++ b/GpCommon/IGpDisplayDriver.h @@ -25,4 +25,6 @@ public: virtual void UpdatePalette(const void *paletteData) = 0; virtual void SetBackgroundColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a) = 0; + + virtual void RequestToggleFullScreen(uint32_t timestamp) = 0; }; diff --git a/GpDisplayDriver_D3D11/GpDisplayDriverD3D11.cpp b/GpDisplayDriver_D3D11/GpDisplayDriverD3D11.cpp index 7a05e6d..785e672 100644 --- a/GpDisplayDriver_D3D11/GpDisplayDriverD3D11.cpp +++ b/GpDisplayDriver_D3D11/GpDisplayDriverD3D11.cpp @@ -102,11 +102,9 @@ bool InitSwapChainForWindow(HWND hWnd, ID3D11Device *device, GpComPtrMakeWindowAssociation(hWnd, DXGI_MWA_NO_ALT_ENTER); if (result != S_OK) return false; -#endif outSwapChain = swapChain; @@ -413,8 +411,8 @@ GpDisplayDriverTickStatus_t GpDisplayDriverD3D11::PresentFrameAndSync() D3D11_VIEWPORT viewport; viewport.TopLeftX = 0; viewport.TopLeftY = 0; - viewport.Width = static_cast(m_windowWidth); - viewport.Height = static_cast(m_windowHeight); + viewport.Width = static_cast(m_windowWidthPhysical); + viewport.Height = static_cast(m_windowHeightPhysical); viewport.MinDepth = 0.0f; viewport.MaxDepth = 1.0f; @@ -592,7 +590,7 @@ void GpDisplayDriverD3D11::ChangeToCursor(HCURSOR cursor) if (m_mouseIsInClientArea) ::SetCursor(cursor); - SetClassLongPtrW(m_hwnd, GCLP_HCURSOR, reinterpret_cast(cursor)); + SetClassLongPtrW(m_osGlobals->m_hwnd, GCLP_HCURSOR, reinterpret_cast(cursor)); } void GpDisplayDriverD3D11::ChangeToStandardCursor(EGpStandardCursor_t cursor) @@ -612,6 +610,105 @@ void GpDisplayDriverD3D11::ChangeToStandardCursor(EGpStandardCursor_t cursor) } } +void GpDisplayDriverD3D11::BecomeFullScreen(LONG &windowStyle) +{ + assert(!m_isFullScreen); + + RECT windowRect; + if (!GetWindowRect(m_osGlobals->m_hwnd, &windowRect)) + return; // ??? + + HMONITOR monitor = MonitorFromRect(&windowRect, MONITOR_DEFAULTTONULL); + if (!monitor) + { + // If the window is off-screen, use the primary monitor + monitor = MonitorFromRect(&windowRect, MONITOR_DEFAULTTOPRIMARY); + } + else + { + // Otherwise, use the nearest + monitor = MonitorFromRect(&windowRect, MONITOR_DEFAULTTONEAREST); + } + + if (!monitor) + return; // No monitor? + + MONITORINFO monitorInfo; + monitorInfo.cbSize = sizeof(monitorInfo); + if (!GetMonitorInfoA(monitor, &monitorInfo)) + return; + + m_windowModeRevertRect = windowRect; + SetWindowLongPtr(m_osGlobals->m_hwnd, GWL_STYLE, WS_VISIBLE | WS_POPUP); + + SetWindowPos(m_osGlobals->m_hwnd, HWND_TOP, monitorInfo.rcMonitor.left, monitorInfo.rcMonitor.top, monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left, monitorInfo.rcMonitor.bottom - monitorInfo.rcMonitor.top, SWP_FRAMECHANGED); + + m_isFullScreen = true; + windowStyle = (WS_VISIBLE | WS_POPUP); +} + +void GpDisplayDriverD3D11::BecomeWindowed(LONG &windowStyle) +{ + assert(m_isFullScreen); + + RECT revertRect = m_windowModeRevertRect; + + HMONITOR monitor = MonitorFromRect(&m_windowModeRevertRect, MONITOR_DEFAULTTONULL); + if (!monitor) + { + // If the window is off-screen, use the primary monitor + monitor = MonitorFromRect(&revertRect, MONITOR_DEFAULTTOPRIMARY); + if (!monitor) + return; + + MONITORINFO monitorInfo; + monitorInfo.cbSize = sizeof(monitorInfo); + if (!GetMonitorInfoA(monitor, &monitorInfo)) + return; + + RECT monitorRect = monitorInfo.rcWork; + LONG monitorWidth = monitorRect.right - monitorRect.left; + LONG monitorHeight = monitorRect.bottom - monitorRect.top; + + LONG revertHeight = revertRect.bottom - revertRect.top; + LONG revertWidth = revertRect.right - revertRect.left; + + if (revertWidth > monitorWidth) + revertWidth = monitorWidth; + + if (revertHeight > monitorHeight) + revertHeight = monitorHeight; + + revertRect.bottom = revertRect.top + revertHeight; + revertRect.right = revertRect.right + revertWidth; + + LONG xDelta = 0; + if (revertRect.right > monitorRect.right) + xDelta = monitorRect.right - revertRect.right; + else if (revertRect.left < monitorRect.left) + xDelta = monitorRect.left - revertRect.left; + + LONG yDelta = 0; + if (revertRect.bottom > monitorRect.bottom) + yDelta = monitorRect.bottom - revertRect.bottom; + else if (revertRect.top < monitorRect.top) + yDelta = monitorRect.top - revertRect.top; + + + revertRect.left = revertRect.left + xDelta; + revertRect.top = revertRect.top + yDelta; + revertRect.bottom = revertRect.top + revertHeight; + revertRect.right = revertRect.right + revertWidth; + } + + SetWindowLongPtr(m_osGlobals->m_hwnd, GWL_STYLE, WS_VISIBLE | WS_OVERLAPPEDWINDOW); + + SetWindowPos(m_osGlobals->m_hwnd, HWND_TOP, revertRect.left, revertRect.top, revertRect.right - revertRect.left, revertRect.bottom - revertRect.top, SWP_FRAMECHANGED); + + m_isFullScreen = false; + windowStyle = (WS_VISIBLE | WS_OVERLAPPEDWINDOW); +} + void GpDisplayDriverD3D11::Run() { WNDCLASSEX wc; @@ -638,14 +735,14 @@ void GpDisplayDriverD3D11::Run() HMENU menus = NULL; // TODO: Fix the resolution here - RECT wr = { 0, 0, m_windowWidth, m_windowHeight }; + RECT wr = { 0, 0, m_windowWidthPhysical, m_windowHeightPhysical }; AdjustWindowRect(&wr, windowStyle, menus != NULL); - m_hwnd = CreateWindowExW(NULL, L"GPD3D11WindowClass", GP_APPLICATION_NAME_W L" (Direct3D 11)", WS_OVERLAPPEDWINDOW, 300, 300, wr.right - wr.left, wr.bottom - wr.top, NULL, menus, m_osGlobals->m_hInstance, NULL); + m_osGlobals->m_hwnd = CreateWindowExW(NULL, L"GPD3D11WindowClass", GP_APPLICATION_NAME_W L" (Direct3D 11)", WS_OVERLAPPEDWINDOW, 300, 300, wr.right - wr.left, wr.bottom - wr.top, NULL, menus, m_osGlobals->m_hInstance, NULL); - ShowWindow(m_hwnd, m_osGlobals->m_nCmdShow); + ShowWindow(m_osGlobals->m_hwnd, m_osGlobals->m_nCmdShow); - StartD3DForWindow(m_hwnd, m_swapChain, m_device, m_deviceContext); + StartD3DForWindow(m_osGlobals->m_hwnd, m_swapChain, m_device, m_deviceContext); InitResources(); @@ -674,7 +771,7 @@ void GpDisplayDriverD3D11::Run() tme.cbSize = sizeof(tme); tme.dwFlags = TME_LEAVE; - tme.hwndTrack = m_hwnd; + tme.hwndTrack = m_osGlobals->m_hwnd; tme.dwHoverTime = HOVER_DEFAULT; TrackMouseEvent(&tme); } @@ -682,38 +779,55 @@ void GpDisplayDriverD3D11::Run() else if (msg.message == WM_MOUSELEAVE) m_mouseIsInClientArea = false; - m_osGlobals->m_translateWindowsMessageFunc(&msg, m_properties.m_eventQueue); + m_osGlobals->m_translateWindowsMessageFunc(&msg, m_properties.m_eventQueue, m_pixelScaleX, m_pixelScaleY); } } else { + if (m_isFullScreen != m_isFullScreenDesired) + { + if (m_isFullScreenDesired) + BecomeFullScreen(windowStyle); + else + BecomeWindowed(windowStyle); + } + RECT clientRect; - GetClientRect(m_hwnd, &clientRect); + GetClientRect(m_osGlobals->m_hwnd, &clientRect); unsigned int desiredWidth = clientRect.right - clientRect.left; unsigned int desiredHeight = clientRect.bottom - clientRect.top; - if (clientRect.right - clientRect.left != m_windowWidth || clientRect.bottom - clientRect.top != m_windowHeight) + if (clientRect.right - clientRect.left != m_windowWidthPhysical || clientRect.bottom - clientRect.top != m_windowHeightPhysical) { - uint32_t prevWidth = m_windowWidth; - uint32_t prevHeight = m_windowHeight; + uint32_t prevWidth = m_windowWidthPhysical; + uint32_t prevHeight = m_windowHeightPhysical; + uint32_t virtualWidth = m_windowWidthVirtual; + uint32_t virtualHeight = m_windowHeightVirtual; + float pixelScaleX = 1.0f; + float pixelScaleY = 1.0f; - if (m_properties.m_adjustRequestedResolutionFunc(m_properties.m_adjustRequestedResolutionFuncContext, desiredWidth, desiredHeight)) + if (m_properties.m_adjustRequestedResolutionFunc(m_properties.m_adjustRequestedResolutionFuncContext, desiredWidth, desiredHeight, virtualWidth, virtualHeight, pixelScaleX, pixelScaleY)) { - bool resizedOK = ResizeD3DWindow(m_hwnd, m_windowWidth, m_windowHeight, desiredWidth, desiredHeight, windowStyle, menus); + bool resizedOK = ResizeD3DWindow(m_osGlobals->m_hwnd, m_windowWidthPhysical, m_windowHeightPhysical, desiredWidth, desiredHeight, windowStyle, menus); resizedOK = resizedOK && DetachSwapChain(); - resizedOK = resizedOK && ResizeSwapChain(m_swapChain, m_windowWidth, m_windowHeight); + resizedOK = resizedOK && ResizeSwapChain(m_swapChain, m_windowWidthPhysical, m_windowHeightPhysical); resizedOK = resizedOK && InitBackBuffer(); if (!resizedOK) break; // Critical video driver error, exit + m_windowWidthVirtual = virtualWidth; + m_windowHeightVirtual = virtualHeight; + m_pixelScaleX = pixelScaleX; + m_pixelScaleY = pixelScaleY; + if (GpVOSEvent *resizeEvent = m_properties.m_eventQueue->QueueEvent()) { resizeEvent->m_eventType = GpVOSEventTypes::kVideoResolutionChanged; resizeEvent->m_event.m_resolutionChangedEvent.m_prevWidth = prevWidth; resizeEvent->m_event.m_resolutionChangedEvent.m_prevHeight = prevHeight; - resizeEvent->m_event.m_resolutionChangedEvent.m_newWidth = m_windowWidth; - resizeEvent->m_event.m_resolutionChangedEvent.m_newHeight = m_windowHeight; + resizeEvent->m_event.m_resolutionChangedEvent.m_newWidth = m_windowWidthVirtual; + resizeEvent->m_event.m_resolutionChangedEvent.m_newHeight = m_windowHeightVirtual; } } } @@ -737,9 +851,9 @@ void GpDisplayDriverD3D11::Shutdown() void GpDisplayDriverD3D11::GetDisplayResolution(unsigned int *width, unsigned int *height, GpPixelFormat_t *pixelFormat) { if (width) - *width = m_windowWidth; + *width = m_windowWidthVirtual; if (height) - *height = m_windowHeight; + *height = m_windowHeightVirtual; if (pixelFormat) *pixelFormat = GpPixelFormats::k8BitStandard; } @@ -760,8 +874,8 @@ void GpDisplayDriverD3D11::DrawSurface(IGpDisplayDriverSurface *surface, int32_t //m_deviceContext->OMSetDepthStencilState(m_drawQuadDepthStencilState, 0); { - const float twoDivWidth = 2.0f / static_cast(m_windowWidth); - const float negativeTwoDivHeight = -2.0f / static_cast(m_windowHeight); + const float twoDivWidth = 2.0f / static_cast(m_windowWidthVirtual); + const float negativeTwoDivHeight = -2.0f / static_cast(m_windowHeightVirtual); DrawQuadVertexConstants constantsData; constantsData.m_ndcOriginX = static_cast(x) * twoDivWidth - 1.0f; @@ -906,6 +1020,16 @@ void GpDisplayDriverD3D11::SetBackgroundColor(uint8_t r, uint8_t g, uint8_t b, u m_bgColor[3] = static_cast(a) / 255.0f; } +void GpDisplayDriverD3D11::RequestToggleFullScreen(uint32_t timestamp) +{ + // Alt-Enter gets re-sent after a full-screen toggle, so we ignore toggle requests until half a second has seconds have elapsed + if (timestamp > m_lastFullScreenToggleTimeStamp + 30) + { + m_isFullScreenDesired = !m_isFullScreenDesired; + m_lastFullScreenToggleTimeStamp = timestamp; + } +} + GpDisplayDriverD3D11 *GpDisplayDriverD3D11::Create(const GpDisplayDriverProperties &properties) { void *storage = malloc(sizeof(GpDisplayDriverD3D11)); @@ -918,8 +1042,12 @@ GpDisplayDriverD3D11 *GpDisplayDriverD3D11::Create(const GpDisplayDriverProperti GpDisplayDriverD3D11::GpDisplayDriverD3D11(const GpDisplayDriverProperties &properties) : m_properties(properties) , m_frameTimeAccumulated(0) - , m_windowWidth(640) - , m_windowHeight(480) + , m_windowWidthPhysical(640) + , m_windowHeightPhysical(480) + , m_windowWidthVirtual(640) + , m_windowHeightVirtual(480) + , m_pixelScaleX(1.0f) + , m_pixelScaleY(1.0f) , m_vosFiber(nullptr) , m_osGlobals(static_cast(properties.m_osGlobals)) , m_pendingCursor(nullptr) @@ -927,8 +1055,12 @@ GpDisplayDriverD3D11::GpDisplayDriverD3D11(const GpDisplayDriverProperties &prop , m_currentStandardCursor(EGpStandardCursors::kArrow) , m_pendingStandardCursor(EGpStandardCursors::kArrow) , m_mouseIsInClientArea(false) + , m_isFullScreen(false) + , m_isFullScreenDesired(false) + , m_lastFullScreenToggleTimeStamp(0) { memset(&m_syncTimeBase, 0, sizeof(m_syncTimeBase)); + memset(&m_windowModeRevertRect, 0, sizeof(m_windowModeRevertRect)); QueryPerformanceFrequency(&m_QPFrequency); diff --git a/GpDisplayDriver_D3D11/GpDisplayDriverD3D11.h b/GpDisplayDriver_D3D11/GpDisplayDriverD3D11.h index 9acf1c6..bee4905 100644 --- a/GpDisplayDriver_D3D11/GpDisplayDriverD3D11.h +++ b/GpDisplayDriver_D3D11/GpDisplayDriverD3D11.h @@ -48,6 +48,8 @@ public: void SetBackgroundColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a) override; + void RequestToggleFullScreen(uint32_t timestamp) override; + static GpDisplayDriverD3D11 *Create(const GpDisplayDriverProperties &properties); private: @@ -81,6 +83,9 @@ private: void ChangeToCursor(HCURSOR cursor); void ChangeToStandardCursor(EGpStandardCursor_t cursor); + void BecomeFullScreen(LONG &windowStyle); + void BecomeWindowed(LONG &windowStyle); + GpComPtr m_swapChain; GpComPtr m_device; GpComPtr m_deviceContext; @@ -108,11 +113,20 @@ private: UINT m_expectedSyncDelta; bool m_isResettingSwapChain; + bool m_isFullScreen; + bool m_isFullScreenDesired; + RECT m_windowModeRevertRect; + uint32_t m_lastFullScreenToggleTimeStamp; + LONGLONG m_frameTimeAccumulated; LONGLONG m_frameTimeSliceSize; - DWORD m_windowWidth; - DWORD m_windowHeight; + DWORD m_windowWidthPhysical; // Physical resolution is the resolution of the actual window + DWORD m_windowHeightPhysical; + DWORD m_windowWidthVirtual; // Virtual resolution is the resolution reported to teh game + DWORD m_windowHeightVirtual; + float m_pixelScaleX; + float m_pixelScaleY; IGpCursor_Win32 *m_activeCursor; IGpCursor_Win32 *m_pendingCursor; @@ -126,7 +140,6 @@ private: HCURSOR m_arrowCursor; HCURSOR m_waitCursor; HCURSOR m_ibeamCursor; - HWND m_hwnd; float m_bgColor[4]; }; diff --git a/PortabilityLayer/DisplayDeviceManager.h b/PortabilityLayer/DisplayDeviceManager.h index d929a0d..d383c0b 100644 --- a/PortabilityLayer/DisplayDeviceManager.h +++ b/PortabilityLayer/DisplayDeviceManager.h @@ -15,7 +15,7 @@ namespace PortabilityLayer struct IResolutionChangeHandler { virtual void OnResolutionChanged(uint32_t prevWidth, uint32_t prevHeight, uint32_t newWidth, uint32_t newHeight) = 0; - virtual void AdjustRequestedResolution(uint32_t &width, uint32_t &height) = 0; + virtual void AdjustRequestedResolution(uint32_t &physicalWidth, uint32_t &physicalHeight, uint32_t &virtualWidth, uint32_t &virtualheight, float &pixelScaleX, float &pixelScaleY) = 0; }; virtual void Init() = 0; diff --git a/PortabilityLayer/FileManager.cpp b/PortabilityLayer/FileManager.cpp index fdac7d3..bb9a66d 100644 --- a/PortabilityLayer/FileManager.cpp +++ b/PortabilityLayer/FileManager.cpp @@ -6,6 +6,7 @@ #include "MacFileMem.h" #include "PLPasStr.h" #include "PLErrorCodes.h" +#include "PLSysCalls.h" #include "ResTypeID.h" #include "HostSystemServices.h" @@ -184,7 +185,7 @@ namespace PortabilityLayer bool FileManagerImpl::PromptOpenFile(VirtualDirectory_t dirID, char *path, size_t &outPathLength, size_t pathCapacity) { - return PortabilityLayer::HostFileSystem::GetInstance()->PromptOpenFile(dirID, path, outPathLength, pathCapacity); + return PLSysCalls::PromptOpenFile(dirID, path, outPathLength, pathCapacity); } FileManagerImpl *FileManagerImpl::GetInstance() diff --git a/PortabilityLayer/GpAppInterface.h b/PortabilityLayer/GpAppInterface.h index 301e8b4..82a0d67 100644 --- a/PortabilityLayer/GpAppInterface.h +++ b/PortabilityLayer/GpAppInterface.h @@ -46,7 +46,7 @@ public: virtual void PL_HostVOSEventQueue_SetInstance(PortabilityLayer::HostVOSEventQueue *instance) = 0; virtual void PL_InstallHostSuspendHook(PortabilityLayer::HostSuspendHook_t hook, void *context) = 0; - virtual bool PL_AdjustRequestedResolution(unsigned int &width, unsigned int &height) = 0; + virtual bool PL_AdjustRequestedResolution(uint32_t &physicalWidth, uint32_t &physicalHeight, uint32_t &virtualWidth, uint32_t &virtualheight, float &pixelScaleX, float &pixelScaleY) = 0; }; GP_APP_DLL_EXPORT_API GpAppInterface *GpAppInterface_Get(); diff --git a/PortabilityLayer/HostSuspendCallArgument.h b/PortabilityLayer/HostSuspendCallArgument.h index 5d3b334..364700d 100644 --- a/PortabilityLayer/HostSuspendCallArgument.h +++ b/PortabilityLayer/HostSuspendCallArgument.h @@ -12,6 +12,7 @@ namespace PortabilityLayer int32_t m_int; size_t m_size; void *m_pointer; - const void *m_constPointer; + const void *m_constPointer; + void (*m_functionPtr)(const HostSuspendCallArgument *args, HostSuspendCallArgument *returnValue); }; } diff --git a/PortabilityLayer/HostSuspendCallID.h b/PortabilityLayer/HostSuspendCallID.h index 4b3e0d5..cd9e90b 100644 --- a/PortabilityLayer/HostSuspendCallID.h +++ b/PortabilityLayer/HostSuspendCallID.h @@ -9,6 +9,7 @@ namespace PortabilityLayer HostSuspendCallID_Unknown, HostSuspendCallID_Delay, + HostSuspendCallID_CallOnVOSThread, }; } diff --git a/PortabilityLayer/PLSysCalls.cpp b/PortabilityLayer/PLSysCalls.cpp index ce67e17..fc66abe 100644 --- a/PortabilityLayer/PLSysCalls.cpp +++ b/PortabilityLayer/PLSysCalls.cpp @@ -6,7 +6,10 @@ #include "PLTimeTaggedVOSEvent.h" #include "DisplayDeviceManager.h" #include "GpVOSEvent.h" +#include "IGpDisplayDriver.h" #include "InputManager.h" +#include "HostDisplayDriver.h" +#include "HostFileSystem.h" #include "HostSuspendCallArgument.h" #include "HostSuspendHook.h" #include "HostVOSEventQueue.h" @@ -66,6 +69,20 @@ static void TranslateKeyboardInputEvent(const GpVOSEvent &vosEventBase, uint32_t if (vosEvent.m_eventType == GpKeyboardInputEventTypes::kUp || vosEvent.m_eventType == GpKeyboardInputEventTypes::kDown) inputManager->ApplyKeyboardEvent(vosEvent); + // Special handling of alt-enter, redirect to display driver + if (vosEventBase.m_eventType == GpKeyboardInputEventTypes::kDown && + vosEventBase.m_event.m_keyboardInputEvent.m_keyIDSubset == GpKeyIDSubsets::kSpecial && + vosEventBase.m_event.m_keyboardInputEvent.m_key.m_specialKey == GpKeySpecials::kEnter) + { + const KeyDownStates *keyStates = inputManager->GetKeys(); + if (keyStates->m_special.Get(GpKeySpecials::kLeftAlt) || keyStates->m_special.Get(GpKeySpecials::kRightAlt)) + { + IGpDisplayDriver *dd = PortabilityLayer::HostDisplayDriver::GetInstance(); + if (dd) + dd->RequestToggleFullScreen(timestamp); + } + } + if (TimeTaggedVOSEvent *evt = queue->Enqueue()) *evt = TimeTaggedVOSEvent::Create(vosEventBase, timestamp); } @@ -149,4 +166,63 @@ namespace PLSysCalls AnimationManager::GetInstance()->TickPlayers(ticks); } } + + static void PromptOpenFileCallback(const PortabilityLayer::HostSuspendCallArgument *args, PortabilityLayer::HostSuspendCallArgument *returnValue) + { + bool result = PortabilityLayer::HostFileSystem::GetInstance()->PromptOpenFile(static_cast(args[0].m_int), static_cast(args[1].m_pointer), *static_cast(args[2].m_pointer), args[3].m_uint); + returnValue->m_uint = (result ? 1 : 0); + } + + bool PromptOpenFile(PortabilityLayer::VirtualDirectory_t dirID, char *path, size_t &outPathLength, size_t pathCapacity) + { + PortabilityLayer::HostSuspendCallArgument cbArgs[4]; + cbArgs[0].m_int = static_cast(dirID); + cbArgs[1].m_pointer = path; + cbArgs[2].m_pointer = &outPathLength; + cbArgs[3].m_size = pathCapacity; + + PortabilityLayer::HostSuspendCallArgument cbReturnValue; + + PortabilityLayer::HostSuspendCallArgument dispatchArgs[3]; + dispatchArgs[0].m_functionPtr = PromptOpenFileCallback; + dispatchArgs[1].m_constPointer = cbArgs; + dispatchArgs[2].m_pointer = &cbReturnValue; + + PortabilityLayer::SuspendApplication(PortabilityLayer::HostSuspendCallID_CallOnVOSThread, dispatchArgs, nullptr); + + return cbReturnValue.m_uint != 0; + } + + static void PromptSaveFileCallback(const PortabilityLayer::HostSuspendCallArgument *args, PortabilityLayer::HostSuspendCallArgument *returnValue) + { + bool result = PortabilityLayer::HostFileSystem::GetInstance()->PromptSaveFile( + static_cast(args[0].m_int), + static_cast(args[1].m_pointer), + *static_cast(args[2].m_pointer), + args[3].m_uint, + static_cast(args[4].m_constPointer)); + + returnValue->m_uint = (result ? 1 : 0); + } + + bool PromptSaveFile(PortabilityLayer::VirtualDirectory_t virtualDirectory, char *path, size_t &outPathLength, size_t pathCapacity, const char *initialFileName) + { + PortabilityLayer::HostSuspendCallArgument cbArgs[5]; + cbArgs[0].m_int = static_cast(virtualDirectory); + cbArgs[1].m_pointer = path; + cbArgs[2].m_pointer = &outPathLength; + cbArgs[3].m_size = pathCapacity; + cbArgs[3].m_constPointer = initialFileName; + + PortabilityLayer::HostSuspendCallArgument cbReturnValue; + + PortabilityLayer::HostSuspendCallArgument dispatchArgs[3]; + dispatchArgs[0].m_functionPtr = PromptSaveFileCallback; + dispatchArgs[1].m_constPointer = cbArgs; + dispatchArgs[2].m_pointer = &cbReturnValue; + + PortabilityLayer::SuspendApplication(PortabilityLayer::HostSuspendCallID_CallOnVOSThread, dispatchArgs, nullptr); + + return cbReturnValue.m_uint != 0; + } } diff --git a/PortabilityLayer/PLSysCalls.h b/PortabilityLayer/PLSysCalls.h index c54a0e7..8178aa5 100644 --- a/PortabilityLayer/PLSysCalls.h +++ b/PortabilityLayer/PLSysCalls.h @@ -1,8 +1,12 @@ #pragma once -#include +#include "VirtualDirectory.h" + +#include namespace PLSysCalls -{ +{ void Sleep(uint32_t ticks); + bool PromptOpenFile(PortabilityLayer::VirtualDirectory_t dirID, char *path, size_t &outPathLength, size_t pathCapacity); + bool PromptSaveFile(PortabilityLayer::VirtualDirectory_t virtualDirectory, char *path, size_t &outPathLength, size_t pathCapacity, const char *initialFileName); }