src/video/winrt/SDL_winrtvideo.cpp
author Philipp Wiesemann <philipp.wiesemann@arcor.de>
Mon, 13 Apr 2015 20:52:18 +0200
changeset 9557 e05c7ad6ec57
parent 9214 dad92c567585
child 9619 b94b6d0bff0f
permissions -rw-r--r--
WinRT: Fixed format string for error message.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2014 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_internal.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 #include <wrl/client.h>
    34 using namespace Windows::UI::Core;
    35 
    36 
    37 /* SDL includes */
    38 extern "C" {
    39 #include "SDL_video.h"
    40 #include "SDL_mouse.h"
    41 #include "../SDL_sysvideo.h"
    42 #include "../SDL_pixels_c.h"
    43 #include "../../events/SDL_events_c.h"
    44 #include "../../render/SDL_sysrender.h"
    45 #include "SDL_syswm.h"
    46 #include "SDL_winrtopengles.h"
    47 }
    48 
    49 #include "../../core/winrt/SDL_winrtapp_direct3d.h"
    50 #include "../../core/winrt/SDL_winrtapp_xaml.h"
    51 #include "SDL_winrtvideo_cpp.h"
    52 #include "SDL_winrtevents_c.h"
    53 #include "SDL_winrtmouse_c.h"
    54 #include "SDL_main.h"
    55 #include "SDL_system.h"
    56 //#include "SDL_log.h"
    57 
    58 
    59 /* Initialization/Query functions */
    60 static int WINRT_VideoInit(_THIS);
    61 static int WINRT_InitModes(_THIS);
    62 static int WINRT_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode);
    63 static void WINRT_VideoQuit(_THIS);
    64 
    65 
    66 /* Window functions */
    67 static int WINRT_CreateWindow(_THIS, SDL_Window * window);
    68 static void WINRT_DestroyWindow(_THIS, SDL_Window * window);
    69 static SDL_bool WINRT_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info);
    70 
    71 
    72 /* SDL-internal globals: */
    73 SDL_Window * WINRT_GlobalSDLWindow = NULL;
    74 
    75 
    76 /* WinRT driver bootstrap functions */
    77 
    78 static int
    79 WINRT_Available(void)
    80 {
    81     return (1);
    82 }
    83 
    84 static void
    85 WINRT_DeleteDevice(SDL_VideoDevice * device)
    86 {
    87     if (device->driverdata) {
    88         SDL_VideoData * video_data = (SDL_VideoData *)device->driverdata;
    89         if (video_data->winrtEglWindow) {
    90             video_data->winrtEglWindow->Release();
    91         }
    92         SDL_free(video_data);
    93     }
    94 
    95     SDL_free(device);
    96 }
    97 
    98 static SDL_VideoDevice *
    99 WINRT_CreateDevice(int devindex)
   100 {
   101     SDL_VideoDevice *device;
   102     SDL_VideoData *data;
   103 
   104     /* Initialize all variables that we clean on shutdown */
   105     device = (SDL_VideoDevice *) SDL_calloc(1, sizeof(SDL_VideoDevice));
   106     if (!device) {
   107         SDL_OutOfMemory();
   108         if (device) {
   109             SDL_free(device);
   110         }
   111         return (0);
   112     }
   113 
   114     data = (SDL_VideoData *) SDL_calloc(1, sizeof(SDL_VideoData));
   115     if (!data) {
   116         SDL_OutOfMemory();
   117         return (0);
   118     }
   119     SDL_zerop(data);
   120     device->driverdata = data;
   121 
   122     /* Set the function pointers */
   123     device->VideoInit = WINRT_VideoInit;
   124     device->VideoQuit = WINRT_VideoQuit;
   125     device->CreateWindow = WINRT_CreateWindow;
   126     device->DestroyWindow = WINRT_DestroyWindow;
   127     device->SetDisplayMode = WINRT_SetDisplayMode;
   128     device->PumpEvents = WINRT_PumpEvents;
   129     device->GetWindowWMInfo = WINRT_GetWindowWMInfo;
   130 #ifdef SDL_VIDEO_OPENGL_EGL
   131     device->GL_LoadLibrary = WINRT_GLES_LoadLibrary;
   132     device->GL_GetProcAddress = WINRT_GLES_GetProcAddress;
   133     device->GL_UnloadLibrary = WINRT_GLES_UnloadLibrary;
   134     device->GL_CreateContext = WINRT_GLES_CreateContext;
   135     device->GL_MakeCurrent = WINRT_GLES_MakeCurrent;
   136     device->GL_SetSwapInterval = WINRT_GLES_SetSwapInterval;
   137     device->GL_GetSwapInterval = WINRT_GLES_GetSwapInterval;
   138     device->GL_SwapWindow = WINRT_GLES_SwapWindow;
   139     device->GL_DeleteContext = WINRT_GLES_DeleteContext;
   140 #endif
   141     device->free = WINRT_DeleteDevice;
   142 
   143     return device;
   144 }
   145 
   146 #define WINRTVID_DRIVER_NAME "winrt"
   147 VideoBootStrap WINRT_bootstrap = {
   148     WINRTVID_DRIVER_NAME, "SDL WinRT video driver",
   149     WINRT_Available, WINRT_CreateDevice
   150 };
   151 
   152 int
   153 WINRT_VideoInit(_THIS)
   154 {
   155     if (WINRT_InitModes(_this) < 0) {
   156         return -1;
   157     }
   158     WINRT_InitMouse(_this);
   159     WINRT_InitTouch(_this);
   160 
   161     return 0;
   162 }
   163 
   164 int
   165 WINRT_CalcDisplayModeUsingNativeWindow(SDL_DisplayMode * mode)
   166 {
   167     SDL_DisplayModeData * driverdata;
   168 
   169     using namespace Windows::Graphics::Display;
   170 
   171     // Go no further if a native window cannot be accessed.  This can happen,
   172     // for example, if this function is called from certain threads, such as
   173     // the SDL/XAML thread.
   174     if (!CoreWindow::GetForCurrentThread()) {
   175         return SDL_SetError("SDL/WinRT display modes cannot be calculated outside of the main thread, such as in SDL's XAML thread");
   176     }
   177 
   178     //SDL_Log("%s, size={%f,%f}, current orientation=%d, native orientation=%d, auto rot. pref=%d, DPI = %f\n",
   179     //    __FUNCTION__,
   180     //    CoreWindow::GetForCurrentThread()->Bounds.Width, CoreWindow::GetForCurrentThread()->Bounds.Height,
   181     //    WINRT_DISPLAY_PROPERTY(CurrentOrientation),
   182     //    WINRT_DISPLAY_PROPERTY(NativeOrientation),
   183     //    WINRT_DISPLAY_PROPERTY(AutoRotationPreferences),
   184     //    WINRT_DISPLAY_PROPERTY(LogicalDpi));
   185 
   186     // Calculate the display size given the window size, taking into account
   187     // the current display's DPI:
   188     const float currentDPI = WINRT_DISPLAY_PROPERTY(LogicalDpi);
   189     const float dipsPerInch = 96.0f;
   190     const int w = (int) ((CoreWindow::GetForCurrentThread()->Bounds.Width * currentDPI) / dipsPerInch);
   191     const int h = (int) ((CoreWindow::GetForCurrentThread()->Bounds.Height * currentDPI) / dipsPerInch);
   192     if (w == 0 || w == h) {
   193         return SDL_SetError("Unable to calculate the WinRT window/display's size");
   194     }
   195 
   196     // Create a driverdata field:
   197     driverdata = (SDL_DisplayModeData *) SDL_malloc(sizeof(*driverdata));
   198     if (!driverdata) {
   199         return SDL_OutOfMemory();
   200     }
   201     SDL_zerop(driverdata);
   202 
   203     // Fill in most fields:
   204     SDL_zerop(mode);
   205     mode->format = SDL_PIXELFORMAT_RGB888;
   206     mode->refresh_rate = 0;  // TODO, WinRT: see if refresh rate data is available, or relevant (for WinRT apps)
   207     mode->w = w;
   208     mode->h = h;
   209     mode->driverdata = driverdata;
   210     driverdata->currentOrientation = WINRT_DISPLAY_PROPERTY(CurrentOrientation);
   211 
   212 #if (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) && (NTDDI_VERSION == NTDDI_WIN8)
   213     // On Windows Phone 8.0, the native window's size is always in portrait,
   214     // regardless of the device's orientation.  This is in contrast to
   215     // Windows 8.x/RT and Windows Phone 8.1, which will resize the native window as the device's
   216     // orientation changes.  In order to compensate for this behavior,
   217     // on Windows Phone, the mode's width and height will be swapped when
   218     // the device is in a landscape (non-portrait) mode.
   219     switch (driverdata->currentOrientation) {
   220         case DisplayOrientations::Landscape:
   221         case DisplayOrientations::LandscapeFlipped:
   222         {
   223             const int tmp = mode->h;
   224             mode->h = mode->w;
   225             mode->w = tmp;
   226             break;
   227         }
   228 
   229         default:
   230             break;
   231     }
   232 #endif
   233 
   234     return 0;
   235 }
   236 
   237 int
   238 WINRT_DuplicateDisplayMode(SDL_DisplayMode * dest, const SDL_DisplayMode * src)
   239 {
   240     SDL_DisplayModeData * driverdata;
   241     driverdata = (SDL_DisplayModeData *) SDL_malloc(sizeof(*driverdata));
   242     if (!driverdata) {
   243         return SDL_OutOfMemory();
   244     }
   245     SDL_memcpy(driverdata, src->driverdata, sizeof(SDL_DisplayModeData));
   246     SDL_memcpy(dest, src, sizeof(SDL_DisplayMode));
   247     dest->driverdata = driverdata;
   248     return 0;
   249 }
   250 
   251 int
   252 WINRT_InitModes(_THIS)
   253 {
   254     // Retrieve the display mode:
   255     SDL_DisplayMode mode, desktop_mode;
   256     if (WINRT_CalcDisplayModeUsingNativeWindow(&mode) != 0) {
   257         return -1;	// If WINRT_CalcDisplayModeUsingNativeWindow fails, it'll already have set the SDL error
   258     }
   259 
   260     if (WINRT_DuplicateDisplayMode(&desktop_mode, &mode) != 0) {
   261         return -1;
   262     }
   263     if (SDL_AddBasicVideoDisplay(&desktop_mode) < 0) {
   264         return -1;
   265     }
   266 
   267     SDL_AddDisplayMode(&_this->displays[0], &mode);
   268     return 0;
   269 }
   270 
   271 static int
   272 WINRT_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode)
   273 {
   274     return 0;
   275 }
   276 
   277 void
   278 WINRT_VideoQuit(_THIS)
   279 {
   280     WINRT_QuitMouse(_this);
   281 }
   282 
   283 int
   284 WINRT_CreateWindow(_THIS, SDL_Window * window)
   285 {
   286     // Make sure that only one window gets created, at least until multimonitor
   287     // support is added.
   288     if (WINRT_GlobalSDLWindow != NULL) {
   289         SDL_SetError("WinRT only supports one window");
   290         return -1;
   291     }
   292 
   293     SDL_WindowData *data = new SDL_WindowData;
   294     if (!data) {
   295         SDL_OutOfMemory();
   296         return -1;
   297     }
   298     window->driverdata = data;
   299     data->sdlWindow = window;
   300 
   301     /* To note, when XAML support is enabled, access to the CoreWindow will not
   302        be possible, at least not via the SDL/XAML thread.  Attempts to access it
   303        from there will throw exceptions.  As such, the SDL_WindowData's
   304        'coreWindow' field will only be set (to a non-null value) if XAML isn't
   305        enabled.
   306     */
   307     if (!WINRT_XAMLWasEnabled) {
   308         data->coreWindow = CoreWindow::GetForCurrentThread();
   309     }
   310 
   311 #if SDL_VIDEO_OPENGL_EGL
   312     /* Setup the EGL surface, but only if OpenGL ES 2 was requested. */
   313     if (!(window->flags & SDL_WINDOW_OPENGL)) {
   314         /* OpenGL ES 2 wasn't requested.  Don't set up an EGL surface. */
   315         data->egl_surface = EGL_NO_SURFACE;
   316     } else {
   317         /* OpenGL ES 2 was reuqested.  Set up an EGL surface. */
   318         SDL_VideoData * video_data = (SDL_VideoData *)_this->driverdata;
   319 
   320         /* Call SDL_EGL_ChooseConfig and eglCreateWindowSurface directly,
   321          * rather than via SDL_EGL_CreateSurface, as older versions of
   322          * ANGLE/WinRT may require that a C++ object, ComPtr<IUnknown>,
   323          * be passed into eglCreateWindowSurface.
   324          */
   325         if (SDL_EGL_ChooseConfig(_this) != 0) {
   326             char buf[512];
   327             SDL_snprintf(buf, sizeof(buf), "SDL_EGL_ChooseConfig failed: %s", SDL_GetError());
   328             return SDL_SetError("%s", buf);
   329         }
   330 
   331         if (video_data->winrtEglWindow) {   /* ... is the 'old' version of ANGLE/WinRT being used? */
   332             /* Attempt to create a window surface using older versions of
   333              * ANGLE/WinRT:
   334              */
   335             Microsoft::WRL::ComPtr<IUnknown> cpp_winrtEglWindow = video_data->winrtEglWindow;
   336             data->egl_surface = ((eglCreateWindowSurface_Old_Function)_this->egl_data->eglCreateWindowSurface)(
   337                 _this->egl_data->egl_display,
   338                 _this->egl_data->egl_config,
   339                 cpp_winrtEglWindow, NULL);
   340             if (data->egl_surface == NULL) {
   341                 return SDL_SetError("eglCreateWindowSurface failed");
   342             }
   343         } else if (data->coreWindow.Get() != nullptr) {
   344             /* Attempt to create a window surface using newer versions of
   345              * ANGLE/WinRT:
   346              */
   347             IInspectable * coreWindowAsIInspectable = reinterpret_cast<IInspectable *>(data->coreWindow.Get());
   348             data->egl_surface = _this->egl_data->eglCreateWindowSurface(
   349                 _this->egl_data->egl_display,
   350                 _this->egl_data->egl_config,
   351                 coreWindowAsIInspectable,
   352                 NULL);
   353             if (data->egl_surface == NULL) {
   354                 return SDL_SetError("eglCreateWindowSurface failed");
   355             }
   356         } else {
   357             return SDL_SetError("No supported means to create an EGL window surface are available");
   358         }
   359     }
   360 #endif
   361 
   362     /* Make sure the window is considered to be positioned at {0,0},
   363        and is considered fullscreen, shown, and the like.
   364     */
   365     window->x = 0;
   366     window->y = 0;
   367     window->flags =
   368         SDL_WINDOW_FULLSCREEN |
   369         SDL_WINDOW_SHOWN |
   370         SDL_WINDOW_BORDERLESS |
   371         SDL_WINDOW_MAXIMIZED |
   372         SDL_WINDOW_INPUT_GRABBED;
   373 
   374 #if SDL_VIDEO_OPENGL_EGL
   375     if (data->egl_surface) {
   376         window->flags |= SDL_WINDOW_OPENGL;
   377     }
   378 #endif
   379 
   380     /* WinRT does not, as of this writing, appear to support app-adjustable
   381        window sizes.  Set the window size to whatever the native WinRT
   382        CoreWindow is set at.
   383 
   384        TODO, WinRT: if and when non-fullscreen XAML control support is added to SDL, consider making those resizable via SDL_Window's interfaces.
   385     */
   386     window->w = _this->displays[0].current_mode.w;
   387     window->h = _this->displays[0].current_mode.h;
   388 
   389     /* For now, treat WinRT apps as if they always have focus.
   390        TODO, WinRT: try tracking keyboard and mouse focus state with respect to snapped apps
   391      */
   392     SDL_SetMouseFocus(window);
   393     SDL_SetKeyboardFocus(window);
   394  
   395     /* Make sure the WinRT app's IFramworkView can post events on
   396        behalf of SDL:
   397     */
   398     WINRT_GlobalSDLWindow = window;
   399 
   400     /* All done! */
   401     return 0;
   402 }
   403 
   404 void
   405 WINRT_DestroyWindow(_THIS, SDL_Window * window)
   406 {
   407     SDL_WindowData * data = (SDL_WindowData *) window->driverdata;
   408 
   409     if (WINRT_GlobalSDLWindow == window) {
   410         WINRT_GlobalSDLWindow = NULL;
   411     }
   412 
   413     if (data) {
   414         // Delete the internal window data:
   415         delete data;
   416         data = NULL;
   417         window->driverdata = NULL;
   418     }
   419 }
   420 
   421 SDL_bool
   422 WINRT_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info)
   423 {
   424     SDL_WindowData * data = (SDL_WindowData *) window->driverdata;
   425 
   426     if (info->version.major <= SDL_MAJOR_VERSION) {
   427         info->subsystem = SDL_SYSWM_WINRT;
   428         info->info.winrt.window = reinterpret_cast<IInspectable *>(data->coreWindow.Get());
   429         return SDL_TRUE;
   430     } else {
   431         SDL_SetError("Application not compiled with SDL %d.%d\n",
   432                      SDL_MAJOR_VERSION, SDL_MINOR_VERSION);
   433         return SDL_FALSE;
   434     }
   435     return SDL_FALSE;
   436 }
   437 
   438 #endif /* SDL_VIDEO_DRIVER_WINRT */
   439 
   440 /* vi: set ts=4 sw=4 expandtab: */