From 31235b4b99805986cbd39dd07668c9d8d9d780e4 Mon Sep 17 00:00:00 2001
From: David Ludwig <dludwig@pobox.com>
Date: Wed, 28 Aug 2013 15:27:01 -0400
Subject: [PATCH] WinRT: made rendering work with orientation changes on
 Windows Phone

Pointer event geometry still needs to be adjusted on Windows Phone, to note.
---
 src/core/winrt/SDL_winrtapp.cpp            | 105 ++++++++++++++++-----
 src/render/direct3d11/SDL_render_d3d11.cpp |  18 ++--
 src/video/winrt/SDL_winrtvideo.cpp         |  28 +++++-
 3 files changed, 117 insertions(+), 34 deletions(-)

diff --git a/src/core/winrt/SDL_winrtapp.cpp b/src/core/winrt/SDL_winrtapp.cpp
index 167489237..de62b410f 100644
--- a/src/core/winrt/SDL_winrtapp.cpp
+++ b/src/core/winrt/SDL_winrtapp.cpp
@@ -145,6 +145,77 @@ static void WINRT_SetDisplayOrientationsPreference(void *userdata, const char *n
     DisplayProperties::AutoRotationPreferences = (DisplayOrientations) orientationFlags;
 }
 
+static void
+WINRT_ProcessWindowSizeChange()
+{
+    // Make the new window size be the one true fullscreen mode.
+    // This change was initially done, in part, to allow the Direct3D 11.1
+    // renderer to receive window-resize events as a device rotates.
+    // Before, rotating a device from landscape, to portrait, and then
+    // back to landscape would cause the Direct3D 11.1 swap buffer to
+    // not get resized appropriately.  SDL would, on the rotation from
+    // landscape to portrait, re-resize the SDL window to it's initial
+    // size (landscape).  On the subsequent rotation, SDL would drop the
+    // window-resize event as it appeared the SDL window didn't change
+    // size, and the Direct3D 11.1 renderer wouldn't resize its swap
+    // chain.
+    SDL_DisplayMode resizedDisplayMode = WINRT_CalcDisplayModeUsingNativeWindow();
+    if (resizedDisplayMode.w == 0 || resizedDisplayMode.h == 0) {
+        return;
+    }
+
+    SDL_DisplayMode oldDisplayMode;
+    SDL_zero(oldDisplayMode);
+    if (WINRT_GlobalSDLVideoDevice) {
+        oldDisplayMode = WINRT_GlobalSDLVideoDevice->displays[0].desktop_mode;
+        WINRT_GlobalSDLVideoDevice->displays[0].current_mode = resizedDisplayMode;
+        WINRT_GlobalSDLVideoDevice->displays[0].desktop_mode = resizedDisplayMode;
+        WINRT_GlobalSDLVideoDevice->displays[0].display_modes[0] = resizedDisplayMode;
+    }
+
+    if (WINRT_GlobalSDLWindow) {
+        // Send a window-resize event to the rest of SDL, and to apps:
+        SDL_SendWindowEvent(
+            WINRT_GlobalSDLWindow,
+            SDL_WINDOWEVENT_RESIZED,
+            resizedDisplayMode.w,
+            resizedDisplayMode.h);
+
+#if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP
+        // HACK: On Windows Phone, make sure that orientation changes from
+        // Landscape to LandscapeFlipped, Portrait to PortraitFlipped,
+        // or vice-versa on either of those two, lead to the Direct3D renderer
+        // getting updated.
+        const DisplayOrientations oldOrientation = (DisplayOrientations) (unsigned int) oldDisplayMode.driverdata;
+        const DisplayOrientations newOrientation = (DisplayOrientations) (unsigned int) resizedDisplayMode.driverdata;
+
+        if ((oldOrientation == DisplayOrientations::Landscape && newOrientation == DisplayOrientations::LandscapeFlipped) ||
+            (oldOrientation == DisplayOrientations::LandscapeFlipped && newOrientation == DisplayOrientations::Landscape) ||
+            (oldOrientation == DisplayOrientations::Portrait && newOrientation == DisplayOrientations::PortraitFlipped) ||
+            (oldOrientation == DisplayOrientations::PortraitFlipped && newOrientation == DisplayOrientations::Portrait))
+        {
+            // One of the reasons this event is getting sent out is because SDL
+            // will ignore requests to send out SDL_WINDOWEVENT_RESIZED events
+            // if and when the event size doesn't change (and the Direct3D 11.1
+            // renderer doesn't get the memo).
+            //
+            // Make sure that the display/window size really didn't change.  If
+            // it did, then a SDL_WINDOWEVENT_SIZE_CHANGED event got sent, and
+            // the Direct3D 11.1 renderer picked it up, presumably.
+            if (oldDisplayMode.w == resizedDisplayMode.w &&
+                oldDisplayMode.h == resizedDisplayMode.h)
+            {
+                SDL_SendWindowEvent(
+                    WINRT_GlobalSDLWindow,
+                    SDL_WINDOWEVENT_SIZE_CHANGED,
+                    resizedDisplayMode.w,
+                    resizedDisplayMode.h);
+            }
+        }
+#endif
+    }
+}
+
 SDL_WinRTApp::SDL_WinRTApp() :
     m_windowClosed(false),
     m_windowVisible(true)
@@ -194,6 +265,13 @@ void SDL_WinRTApp::OnOrientationChanged(Object^ sender)
             (int)DisplayProperties::AutoRotationPreferences);
     }
 #endif
+
+#if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP
+    // On Windows Phone, treat an orientation change as a change in window size.
+    // The native window's size doesn't seem to change, however SDL will simulate
+    // a window size change.
+    WINRT_ProcessWindowSizeChange();
+#endif
 }
 
 void SDL_WinRTApp::SetWindow(CoreWindow^ window)
@@ -294,32 +372,7 @@ void SDL_WinRTApp::OnWindowSizeChanged(CoreWindow^ sender, WindowSizeChangedEven
         (WINRT_GlobalSDLWindow ? "yes" : "no"));
 #endif
 
-    if (WINRT_GlobalSDLWindow) {
-        // Make the new window size be the one true fullscreen mode.
-        // This change was initially done, in part, to allow the Direct3D 11.1
-        // renderer to receive window-resize events as a device rotates.
-        // Before, rotating a device from landscape, to portrait, and then
-        // back to landscape would cause the Direct3D 11.1 swap buffer to
-        // not get resized appropriately.  SDL would, on the rotation from
-        // landscape to portrait, re-resize the SDL window to it's initial
-        // size (landscape).  On the subsequent rotation, SDL would drop the
-        // window-resize event as it appeared the SDL window didn't change
-        // size, and the Direct3D 11.1 renderer wouldn't resize its swap
-        // chain.
-        SDL_DisplayMode resizedDisplayMode = WINRT_CalcDisplayModeUsingNativeWindow();
-        WINRT_GlobalSDLVideoDevice->displays[0].current_mode = resizedDisplayMode;
-        WINRT_GlobalSDLVideoDevice->displays[0].desktop_mode = resizedDisplayMode;
-        WINRT_GlobalSDLVideoDevice->displays[0].display_modes[0] = resizedDisplayMode;
-
-        // Send the window-resize event to the rest of SDL, and to apps:
-        const int windowWidth = (int) ceil(args->Size.Width);
-        const int windowHeight = (int) ceil(args->Size.Height);
-        SDL_SendWindowEvent(
-            WINRT_GlobalSDLWindow,
-            SDL_WINDOWEVENT_RESIZED,
-            windowWidth,
-            windowHeight);
-    }
+    WINRT_ProcessWindowSizeChange();
 }
 
 void SDL_WinRTApp::OnVisibilityChanged(CoreWindow^ sender, VisibilityChangedEventArgs^ args)
diff --git a/src/render/direct3d11/SDL_render_d3d11.cpp b/src/render/direct3d11/SDL_render_d3d11.cpp
index 44032ddd7..c9e11b5e6 100644
--- a/src/render/direct3d11/SDL_render_d3d11.cpp
+++ b/src/render/direct3d11/SDL_render_d3d11.cpp
@@ -920,7 +920,7 @@ D3D11_WindowEvent(SDL_Renderer * renderer, const SDL_WindowEvent *event)
 {
     //D3D11_RenderData *data = (D3D11_RenderData *) renderer->driverdata;
 
-    if (event->event == SDL_WINDOWEVENT_RESIZED) {
+    if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED) {
         D3D11_UpdateForWindowSizeChange(renderer);
     }
 }
@@ -1215,16 +1215,16 @@ D3D11_UpdateViewport(SDL_Renderer * renderer)
         // Windows Phone rotations
         //
         case DisplayOrientations::Landscape:
-            // 90-degree Z-rotation
-            XMStoreFloat4x4(&data->vertexShaderConstantsData.projection, XMMatrixRotationZ(XM_PIDIV2));
+            // 270-degree (-90 degree) Z-rotation
+            XMStoreFloat4x4(&data->vertexShaderConstantsData.projection, XMMatrixRotationZ(-XM_PIDIV2));
             break;
         case DisplayOrientations::Portrait:
             // 0-degree Z-rotation
             XMStoreFloat4x4(&data->vertexShaderConstantsData.projection, XMMatrixIdentity());
             break;
         case DisplayOrientations::LandscapeFlipped:
-            // 270-degree (-90 degree) Z-rotation
-            XMStoreFloat4x4(&data->vertexShaderConstantsData.projection, XMMatrixRotationZ(-XM_PIDIV2));
+            // 90-degree Z-rotation
+            XMStoreFloat4x4(&data->vertexShaderConstantsData.projection, XMMatrixRotationZ(XM_PIDIV2));
             break;
         case DisplayOrientations::PortraitFlipped:
             // 180-degree Z-rotation
@@ -1280,11 +1280,15 @@ D3D11_UpdateViewport(SDL_Renderer * renderer)
 
     //
     // Update the Direct3D viewport, which seems to be aligned to the
-    // swap buffer's coordinate space, which is always in landscape:
+    // swap buffer's coordinate space, which is always in either
+    // a landscape mode, for all Windows 8/RT devices, or a portrait mode,
+    // for Windows Phone devices.
     //
     SDL_FRect orientationAlignedViewport;
 #if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP
-    const bool swapDimensions = false;
+    const bool swapDimensions =
+        data->orientation == DisplayOrientations::Landscape ||
+        data->orientation == DisplayOrientations::LandscapeFlipped;
 #else
     const bool swapDimensions =
         data->orientation == DisplayOrientations::Portrait ||
diff --git a/src/video/winrt/SDL_winrtvideo.cpp b/src/video/winrt/SDL_winrtvideo.cpp
index 8fecf2cd2..940798bab 100644
--- a/src/video/winrt/SDL_winrtvideo.cpp
+++ b/src/video/winrt/SDL_winrtvideo.cpp
@@ -154,6 +154,8 @@ WINRT_VideoInit(_THIS)
 SDL_DisplayMode
 WINRT_CalcDisplayModeUsingNativeWindow()
 {
+    using namespace Windows::Graphics::Display;
+
     // Create an empty, zeroed-out display mode:
     SDL_DisplayMode mode;
     SDL_zero(mode);
@@ -168,7 +170,7 @@ WINRT_CalcDisplayModeUsingNativeWindow()
     // Fill in most fields:
     mode.format = SDL_PIXELFORMAT_RGB888;
     mode.refresh_rate = 0;  // TODO, WinRT: see if refresh rate data is available, or relevant (for WinRT apps)
-    mode.driverdata = NULL;
+    mode.driverdata = (void *) DisplayProperties::CurrentOrientation;
 
     // Calculate the display size given the window size, taking into account
     // the current display's DPI:
@@ -177,6 +179,30 @@ WINRT_CalcDisplayModeUsingNativeWindow()
     mode.w = (int) ((CoreWindow::GetForCurrentThread()->Bounds.Width * currentDPI) / dipsPerInch);
     mode.h = (int) ((CoreWindow::GetForCurrentThread()->Bounds.Height * currentDPI) / dipsPerInch);
 
+#if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP
+    // On Windows Phone, the native window's size is always in portrait,
+    // regardless of the device's orientation.  This is in contrast to
+    // Windows 8/RT, which will resize the native window as the device's
+    // orientation changes.  In order to compensate for this behavior,
+    // on Windows Phone, the mode's width and height will be swapped when
+    // the device is in a landscape (non-portrait) mode.
+    switch (DisplayProperties::CurrentOrientation) {
+        case DisplayOrientations::Landscape:
+        case DisplayOrientations::LandscapeFlipped:
+        {
+            const int tmp = mode.h;
+            mode.h = mode.w;
+            mode.w = tmp;
+            break;
+        }
+
+        default:
+            break;
+    }
+
+    // Attach the mode to te
+#endif
+
     return mode;
 }