src/video/winrt/SDL_winrtvideo.cpp
author David Ludwig <dludwig@pobox.com>
Thu, 26 Dec 2013 13:59:01 -0500
changeset 8572 f8a89aa53309
parent 8551 666f05b079ea
child 8574 bf4991b6a4ec
permissions -rw-r--r--
WinRT: fixed crash on ARM and x64 during OpenGL window init
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 #include "SDL_config.h"
    22 
    23 #if SDL_VIDEO_DRIVER_WINRT
    24 
    25 /* WinRT SDL video driver implementation
    26 
    27    Initial work on this was done by David Ludwig (dludwig@pobox.com), and
    28    was based off of SDL's "dummy" video driver.
    29  */
    30 
    31 /* Windows includes */
    32 #include <agile.h>
    33 using namespace Windows::UI::Core;
    34 
    35 
    36 /* SDL includes */
    37 extern "C" {
    38 #include "SDL_video.h"
    39 #include "SDL_mouse.h"
    40 #include "../SDL_sysvideo.h"
    41 #include "../SDL_pixels_c.h"
    42 #include "../../events/SDL_events_c.h"
    43 #include "../../render/SDL_sysrender.h"
    44 #include "SDL_syswm.h"
    45 #include "SDL_winrtopengles.h"
    46 }
    47 
    48 #include "../../core/winrt/SDL_winrtapp_direct3d.h"
    49 #include "../../core/winrt/SDL_winrtapp_xaml.h"
    50 #include "SDL_winrtvideo_cpp.h"
    51 #include "SDL_winrtevents_c.h"
    52 #include "SDL_winrtmouse_c.h"
    53 #include "SDL_main.h"
    54 #include "SDL_system.h"
    55 
    56 
    57 /* Initialization/Query functions */
    58 static int WINRT_VideoInit(_THIS);
    59 static int WINRT_InitModes(_THIS);
    60 static int WINRT_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode);
    61 static void WINRT_VideoQuit(_THIS);
    62 
    63 
    64 /* Window functions */
    65 static int WINRT_CreateWindow(_THIS, SDL_Window * window);
    66 static void WINRT_DestroyWindow(_THIS, SDL_Window * window);
    67 static SDL_bool WINRT_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info);
    68 
    69 
    70 /* SDL-internal globals: */
    71 SDL_Window * WINRT_GlobalSDLWindow = NULL;
    72 SDL_VideoDevice * WINRT_GlobalSDLVideoDevice = NULL;
    73 
    74 
    75 /* WinRT driver bootstrap functions */
    76 
    77 static int
    78 WINRT_Available(void)
    79 {
    80     return (1);
    81 }
    82 
    83 static void
    84 WINRT_DeleteDevice(SDL_VideoDevice * device)
    85 {
    86     if (device == WINRT_GlobalSDLVideoDevice) {
    87         WINRT_GlobalSDLVideoDevice = NULL;
    88     }
    89     SDL_free(device);
    90 }
    91 
    92 static SDL_VideoDevice *
    93 WINRT_CreateDevice(int devindex)
    94 {
    95     SDL_VideoDevice *device;
    96 
    97     /* Initialize all variables that we clean on shutdown */
    98     device = (SDL_VideoDevice *) SDL_calloc(1, sizeof(SDL_VideoDevice));
    99     if (!device) {
   100         SDL_OutOfMemory();
   101         if (device) {
   102             SDL_free(device);
   103         }
   104         return (0);
   105     }
   106 
   107     /* Set the function pointers */
   108     device->VideoInit = WINRT_VideoInit;
   109     device->VideoQuit = WINRT_VideoQuit;
   110     device->CreateWindow = WINRT_CreateWindow;
   111     device->DestroyWindow = WINRT_DestroyWindow;
   112     device->SetDisplayMode = WINRT_SetDisplayMode;
   113     device->PumpEvents = WINRT_PumpEvents;
   114     device->GetWindowWMInfo = WINRT_GetWindowWMInfo;
   115 #ifdef SDL_VIDEO_OPENGL_EGL
   116     device->GL_LoadLibrary = WINRT_GLES_LoadLibrary;
   117     device->GL_GetProcAddress = WINRT_GLES_GetProcAddress;
   118     device->GL_UnloadLibrary = WINRT_GLES_UnloadLibrary;
   119     device->GL_CreateContext = WINRT_GLES_CreateContext;
   120     device->GL_MakeCurrent = WINRT_GLES_MakeCurrent;
   121     device->GL_SetSwapInterval = WINRT_GLES_SetSwapInterval;
   122     device->GL_GetSwapInterval = WINRT_GLES_GetSwapInterval;
   123     device->GL_SwapWindow = WINRT_GLES_SwapWindow;
   124     device->GL_DeleteContext = WINRT_GLES_DeleteContext;
   125 #endif
   126     device->free = WINRT_DeleteDevice;
   127     WINRT_GlobalSDLVideoDevice = device;
   128 
   129     return device;
   130 }
   131 
   132 #define WINRTVID_DRIVER_NAME "winrt"
   133 VideoBootStrap WINRT_bootstrap = {
   134     WINRTVID_DRIVER_NAME, "SDL WinRT video driver",
   135     WINRT_Available, WINRT_CreateDevice
   136 };
   137 
   138 int
   139 WINRT_VideoInit(_THIS)
   140 {
   141     if (WINRT_InitModes(_this) < 0) {
   142         return -1;
   143     }
   144     WINRT_InitMouse(_this);
   145     WINRT_InitTouch(_this);
   146 
   147     return 0;
   148 }
   149 
   150 SDL_DisplayMode
   151 WINRT_CalcDisplayModeUsingNativeWindow()
   152 {
   153     using namespace Windows::Graphics::Display;
   154 
   155     // Create an empty, zeroed-out display mode:
   156     SDL_DisplayMode mode;
   157     SDL_zero(mode);
   158 
   159     // Go no further if a native window cannot be accessed.  This can happen,
   160     // for example, if this function is called from certain threads, such as
   161     // the SDL/XAML thread.
   162     if (!CoreWindow::GetForCurrentThread()) {
   163         return mode;
   164     }
   165 
   166     // Fill in most fields:
   167     mode.format = SDL_PIXELFORMAT_RGB888;
   168     mode.refresh_rate = 0;  // TODO, WinRT: see if refresh rate data is available, or relevant (for WinRT apps)
   169     mode.driverdata = (void *) DisplayProperties::CurrentOrientation;
   170 
   171     // Calculate the display size given the window size, taking into account
   172     // the current display's DPI:
   173     const float currentDPI = Windows::Graphics::Display::DisplayProperties::LogicalDpi; 
   174     const float dipsPerInch = 96.0f;
   175     mode.w = (int) ((CoreWindow::GetForCurrentThread()->Bounds.Width * currentDPI) / dipsPerInch);
   176     mode.h = (int) ((CoreWindow::GetForCurrentThread()->Bounds.Height * currentDPI) / dipsPerInch);
   177 
   178 #if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP
   179     // On Windows Phone, the native window's size is always in portrait,
   180     // regardless of the device's orientation.  This is in contrast to
   181     // Windows 8/RT, which will resize the native window as the device's
   182     // orientation changes.  In order to compensate for this behavior,
   183     // on Windows Phone, the mode's width and height will be swapped when
   184     // the device is in a landscape (non-portrait) mode.
   185     switch (DisplayProperties::CurrentOrientation) {
   186         case DisplayOrientations::Landscape:
   187         case DisplayOrientations::LandscapeFlipped:
   188         {
   189             const int tmp = mode.h;
   190             mode.h = mode.w;
   191             mode.w = tmp;
   192             break;
   193         }
   194 
   195         default:
   196             break;
   197     }
   198 
   199     // Attach the mode to te
   200 #endif
   201 
   202     return mode;
   203 }
   204 
   205 int
   206 WINRT_InitModes(_THIS)
   207 {
   208     // Retrieve the display mode:
   209     SDL_DisplayMode mode = WINRT_CalcDisplayModeUsingNativeWindow();
   210     if (mode.w == 0 || mode.h == 0) {
   211         return SDL_SetError("Unable to calculate the WinRT window/display's size");
   212     }
   213 
   214     if (SDL_AddBasicVideoDisplay(&mode) < 0) {
   215         return -1;
   216     }
   217 
   218     SDL_AddDisplayMode(&_this->displays[0], &mode);
   219     return 0;
   220 }
   221 
   222 static int
   223 WINRT_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode)
   224 {
   225     return 0;
   226 }
   227 
   228 void
   229 WINRT_VideoQuit(_THIS)
   230 {
   231     WINRT_QuitMouse(_this);
   232 }
   233 
   234 int
   235 WINRT_CreateWindow(_THIS, SDL_Window * window)
   236 {
   237     // Make sure that only one window gets created, at least until multimonitor
   238     // support is added.
   239     if (WINRT_GlobalSDLWindow != NULL) {
   240         SDL_SetError("WinRT only supports one window");
   241         return -1;
   242     }
   243 
   244     SDL_WindowData *data = new SDL_WindowData;
   245     if (!data) {
   246         SDL_OutOfMemory();
   247         return -1;
   248     }
   249     window->driverdata = data;
   250     data->sdlWindow = window;
   251 
   252     /* To note, when XAML support is enabled, access to the CoreWindow will not
   253        be possible, at least not via the SDL/XAML thread.  Attempts to access it
   254        from there will throw exceptions.  As such, the SDL_WindowData's
   255        'coreWindow' field will only be set (to a non-null value) if XAML isn't
   256        enabled.
   257     */
   258     if (!WINRT_XAMLWasEnabled) {
   259         data->coreWindow = CoreWindow::GetForCurrentThread();
   260     }
   261 
   262 #if SDL_VIDEO_OPENGL_EGL
   263     /* Setup the EGL surface, but only if OpenGL ES 2 was requested. */
   264     if (!(window->flags & SDL_WINDOW_OPENGL)) {
   265         /* OpenGL ES 2 wasn't requested.  Don't set up an EGL surface. */
   266         data->egl_surface = EGL_NO_SURFACE;
   267     } else {
   268         /* OpenGL ES 2 was reuqested.  Set up an EGL surface. */
   269 
   270         /* HACK: ANGLE/WinRT currently uses non-pointer, C++ objects to represent
   271            native windows.  The object only contains a single pointer to a COM
   272            interface pointer, which on x86 appears to be castable to the object
   273            without apparant problems.  On other platforms, notable ARM and x64,
   274            doing so will cause a crash.  To avoid this crash, we'll bypass
   275            SDL's normal call to eglCreateWindowSurface, which is invoked from C
   276            code, and call it here, where an appropriate C++ object may be
   277            passed in.
   278          */
   279         typedef EGLSurface (*eglCreateWindowSurfaceFunction)(EGLDisplay dpy, EGLConfig config,
   280             Microsoft::WRL::ComPtr<IUnknown> win,
   281             const EGLint *attrib_list);
   282         eglCreateWindowSurfaceFunction WINRT_eglCreateWindowSurface =
   283             (eglCreateWindowSurfaceFunction) _this->egl_data->eglCreateWindowSurface;
   284 
   285         Microsoft::WRL::ComPtr<IUnknown> nativeWindow = reinterpret_cast<IUnknown *>(data->coreWindow.Get());
   286         data->egl_surface = WINRT_eglCreateWindowSurface(
   287             _this->egl_data->egl_display,
   288             _this->egl_data->egl_config,
   289             nativeWindow, NULL);
   290         if (data->egl_surface == NULL) {
   291             // TODO, WinRT: see if SDL_EGL_CreateSurface, or its callee(s), sets an error message.  If so, attach it to the SDL error.
   292             return SDL_SetError("SDL_EGL_CreateSurface failed");
   293         }
   294     }
   295 #endif
   296 
   297     /* Make sure the window is considered to be positioned at {0,0},
   298        and is considered fullscreen, shown, and the like.
   299     */
   300     window->x = 0;
   301     window->y = 0;
   302     window->flags =
   303         SDL_WINDOW_FULLSCREEN |
   304         SDL_WINDOW_SHOWN |
   305         SDL_WINDOW_BORDERLESS |
   306         SDL_WINDOW_MAXIMIZED |
   307         SDL_WINDOW_INPUT_GRABBED;
   308 
   309 #if SDL_VIDEO_OPENGL_EGL
   310     if (data->egl_surface) {
   311         window->flags |= SDL_WINDOW_OPENGL;
   312     }
   313 #endif
   314 
   315     /* WinRT does not, as of this writing, appear to support app-adjustable
   316        window sizes.  Set the window size to whatever the native WinRT
   317        CoreWindow is set at.
   318 
   319        TODO, WinRT: if and when non-fullscreen XAML control support is added to SDL, consider making those resizable via SDL_Window's interfaces.
   320     */
   321     window->w = _this->displays[0].current_mode.w;
   322     window->h = _this->displays[0].current_mode.h;
   323 
   324     /* For now, treat WinRT apps as if they always have focus.
   325        TODO, WinRT: try tracking keyboard and mouse focus state with respect to snapped apps
   326      */
   327     SDL_SetMouseFocus(window);
   328     SDL_SetKeyboardFocus(window);
   329  
   330     /* Make sure the WinRT app's IFramworkView can post events on
   331        behalf of SDL:
   332     */
   333     WINRT_GlobalSDLWindow = window;
   334 
   335     /* All done! */
   336     return 0;
   337 }
   338 
   339 void
   340 WINRT_DestroyWindow(_THIS, SDL_Window * window)
   341 {
   342     SDL_WindowData * data = (SDL_WindowData *) window->driverdata;
   343 
   344     if (WINRT_GlobalSDLWindow == window) {
   345         WINRT_GlobalSDLWindow = NULL;
   346     }
   347 
   348     if (data) {
   349         // Delete the internal window data:
   350         delete data;
   351         data = NULL;
   352     }
   353 }
   354 
   355 SDL_bool
   356 WINRT_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info)
   357 {
   358     SDL_WindowData * data = (SDL_WindowData *) window->driverdata;
   359 
   360     if (info->version.major <= SDL_MAJOR_VERSION) {
   361         info->subsystem = SDL_SYSWM_WINRT;
   362         info->info.winrt.window = reinterpret_cast<IInspectable *>(data->coreWindow.Get());
   363         return SDL_TRUE;
   364     } else {
   365         SDL_SetError("Application not compiled with SDL %d.%d\n",
   366                      SDL_MAJOR_VERSION, SDL_MINOR_VERSION);
   367         return SDL_FALSE;
   368     }
   369     return SDL_FALSE;
   370 }
   371 
   372 #endif /* SDL_VIDEO_DRIVER_WINRT */
   373 
   374 /* vi: set ts=4 sw=4 expandtab: */