src/video/winrt/SDL_winrtvideo.cpp
author Sam Lantinga <slouken@libsdl.org>
Thu, 13 Mar 2014 00:40:08 -0700
changeset 8615 097646deaef2
parent 8600 092802455aed
child 8621 5252788cb448
permissions -rw-r--r--
Fixed the copyright date on files contributed by David Ludwig
     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 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 int
   151 WINRT_CalcDisplayModeUsingNativeWindow(SDL_DisplayMode * mode)
   152 {
   153     SDL_DisplayModeData * driverdata;
   154 
   155     using namespace Windows::Graphics::Display;
   156 
   157     // Go no further if a native window cannot be accessed.  This can happen,
   158     // for example, if this function is called from certain threads, such as
   159     // the SDL/XAML thread.
   160     if (!CoreWindow::GetForCurrentThread()) {
   161         return SDL_SetError("SDL/WinRT display modes cannot be calculated outside of the main thread, such as in SDL's XAML thread");
   162     }
   163 
   164     // Calculate the display size given the window size, taking into account
   165     // the current display's DPI:
   166     const float currentDPI = Windows::Graphics::Display::DisplayProperties::LogicalDpi; 
   167     const float dipsPerInch = 96.0f;
   168     const int w = (int) ((CoreWindow::GetForCurrentThread()->Bounds.Width * currentDPI) / dipsPerInch);
   169     const int h = (int) ((CoreWindow::GetForCurrentThread()->Bounds.Height * currentDPI) / dipsPerInch);
   170     if (w == 0 || w == h) {
   171         return SDL_SetError("Unable to calculate the WinRT window/display's size");
   172     }
   173 
   174     // Create a driverdata field:
   175     driverdata = (SDL_DisplayModeData *) SDL_malloc(sizeof(*driverdata));
   176     if (!driverdata) {
   177         return SDL_OutOfMemory();
   178     }
   179     SDL_zerop(driverdata);
   180 
   181     // Fill in most fields:
   182     SDL_zerop(mode);
   183     mode->format = SDL_PIXELFORMAT_RGB888;
   184     mode->refresh_rate = 0;  // TODO, WinRT: see if refresh rate data is available, or relevant (for WinRT apps)
   185     mode->w = w;
   186     mode->h = h;
   187     mode->driverdata = driverdata;
   188     driverdata->currentOrientation = DisplayProperties::CurrentOrientation;
   189 
   190 #if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP
   191     // On Windows Phone, the native window's size is always in portrait,
   192     // regardless of the device's orientation.  This is in contrast to
   193     // Windows 8/RT, which will resize the native window as the device's
   194     // orientation changes.  In order to compensate for this behavior,
   195     // on Windows Phone, the mode's width and height will be swapped when
   196     // the device is in a landscape (non-portrait) mode.
   197     switch (DisplayProperties::CurrentOrientation) {
   198         case DisplayOrientations::Landscape:
   199         case DisplayOrientations::LandscapeFlipped:
   200         {
   201             const int tmp = mode->h;
   202             mode->h = mode->w;
   203             mode->w = tmp;
   204             break;
   205         }
   206 
   207         default:
   208             break;
   209     }
   210 #endif
   211 
   212     return 0;
   213 }
   214 
   215 int
   216 WINRT_DuplicateDisplayMode(SDL_DisplayMode * dest, const SDL_DisplayMode * src)
   217 {
   218     SDL_DisplayModeData * driverdata;
   219     driverdata = (SDL_DisplayModeData *) SDL_malloc(sizeof(*driverdata));
   220     if (!driverdata) {
   221         return SDL_OutOfMemory();
   222     }
   223     SDL_memcpy(driverdata, src->driverdata, sizeof(SDL_DisplayModeData));
   224     SDL_memcpy(dest, src, sizeof(SDL_DisplayMode));
   225     dest->driverdata = driverdata;
   226     return 0;
   227 }
   228 
   229 int
   230 WINRT_InitModes(_THIS)
   231 {
   232     // Retrieve the display mode:
   233     SDL_DisplayMode mode, desktop_mode;
   234     if (WINRT_CalcDisplayModeUsingNativeWindow(&mode) != 0) {
   235         return -1;	// If WINRT_CalcDisplayModeUsingNativeWindow fails, it'll already have set the SDL error
   236     }
   237 
   238     if (WINRT_DuplicateDisplayMode(&desktop_mode, &mode) != 0) {
   239         return -1;
   240     }
   241     if (SDL_AddBasicVideoDisplay(&desktop_mode) < 0) {
   242         return -1;
   243     }
   244 
   245     SDL_AddDisplayMode(&_this->displays[0], &mode);
   246     return 0;
   247 }
   248 
   249 static int
   250 WINRT_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode)
   251 {
   252     return 0;
   253 }
   254 
   255 void
   256 WINRT_VideoQuit(_THIS)
   257 {
   258     WINRT_QuitMouse(_this);
   259 }
   260 
   261 int
   262 WINRT_CreateWindow(_THIS, SDL_Window * window)
   263 {
   264     // Make sure that only one window gets created, at least until multimonitor
   265     // support is added.
   266     if (WINRT_GlobalSDLWindow != NULL) {
   267         SDL_SetError("WinRT only supports one window");
   268         return -1;
   269     }
   270 
   271     SDL_WindowData *data = new SDL_WindowData;
   272     if (!data) {
   273         SDL_OutOfMemory();
   274         return -1;
   275     }
   276     window->driverdata = data;
   277     data->sdlWindow = window;
   278 
   279     /* To note, when XAML support is enabled, access to the CoreWindow will not
   280        be possible, at least not via the SDL/XAML thread.  Attempts to access it
   281        from there will throw exceptions.  As such, the SDL_WindowData's
   282        'coreWindow' field will only be set (to a non-null value) if XAML isn't
   283        enabled.
   284     */
   285     if (!WINRT_XAMLWasEnabled) {
   286         data->coreWindow = CoreWindow::GetForCurrentThread();
   287     }
   288 
   289 #if SDL_VIDEO_OPENGL_EGL
   290     /* Setup the EGL surface, but only if OpenGL ES 2 was requested. */
   291     if (!(window->flags & SDL_WINDOW_OPENGL)) {
   292         /* OpenGL ES 2 wasn't requested.  Don't set up an EGL surface. */
   293         data->egl_surface = EGL_NO_SURFACE;
   294     } else {
   295         /* OpenGL ES 2 was reuqested.  Set up an EGL surface. */
   296 
   297         /* HACK: ANGLE/WinRT currently uses non-pointer, C++ objects to represent
   298            native windows.  The object only contains a single pointer to a COM
   299            interface pointer, which on x86 appears to be castable to the object
   300            without apparant problems.  On other platforms, notable ARM and x64,
   301            doing so will cause a crash.  To avoid this crash, we'll bypass
   302            SDL's normal call to eglCreateWindowSurface, which is invoked from C
   303            code, and call it here, where an appropriate C++ object may be
   304            passed in.
   305          */
   306         typedef EGLSurface (*eglCreateWindowSurfaceFunction)(EGLDisplay dpy, EGLConfig config,
   307             Microsoft::WRL::ComPtr<IUnknown> win,
   308             const EGLint *attrib_list);
   309         eglCreateWindowSurfaceFunction WINRT_eglCreateWindowSurface =
   310             (eglCreateWindowSurfaceFunction) _this->egl_data->eglCreateWindowSurface;
   311 
   312         Microsoft::WRL::ComPtr<IUnknown> nativeWindow = reinterpret_cast<IUnknown *>(data->coreWindow.Get());
   313         data->egl_surface = WINRT_eglCreateWindowSurface(
   314             _this->egl_data->egl_display,
   315             _this->egl_data->egl_config,
   316             nativeWindow, NULL);
   317         if (data->egl_surface == NULL) {
   318             // TODO, WinRT: see if eglCreateWindowSurface, or its callee(s), sets an error message.  If so, attach it to the SDL error.
   319             return SDL_SetError("eglCreateWindowSurface failed");
   320         }
   321     }
   322 #endif
   323 
   324     /* Make sure the window is considered to be positioned at {0,0},
   325        and is considered fullscreen, shown, and the like.
   326     */
   327     window->x = 0;
   328     window->y = 0;
   329     window->flags =
   330         SDL_WINDOW_FULLSCREEN |
   331         SDL_WINDOW_SHOWN |
   332         SDL_WINDOW_BORDERLESS |
   333         SDL_WINDOW_MAXIMIZED |
   334         SDL_WINDOW_INPUT_GRABBED;
   335 
   336 #if SDL_VIDEO_OPENGL_EGL
   337     if (data->egl_surface) {
   338         window->flags |= SDL_WINDOW_OPENGL;
   339     }
   340 #endif
   341 
   342     /* WinRT does not, as of this writing, appear to support app-adjustable
   343        window sizes.  Set the window size to whatever the native WinRT
   344        CoreWindow is set at.
   345 
   346        TODO, WinRT: if and when non-fullscreen XAML control support is added to SDL, consider making those resizable via SDL_Window's interfaces.
   347     */
   348     window->w = _this->displays[0].current_mode.w;
   349     window->h = _this->displays[0].current_mode.h;
   350 
   351     /* For now, treat WinRT apps as if they always have focus.
   352        TODO, WinRT: try tracking keyboard and mouse focus state with respect to snapped apps
   353      */
   354     SDL_SetMouseFocus(window);
   355     SDL_SetKeyboardFocus(window);
   356  
   357     /* Make sure the WinRT app's IFramworkView can post events on
   358        behalf of SDL:
   359     */
   360     WINRT_GlobalSDLWindow = window;
   361 
   362     /* All done! */
   363     return 0;
   364 }
   365 
   366 void
   367 WINRT_DestroyWindow(_THIS, SDL_Window * window)
   368 {
   369     SDL_WindowData * data = (SDL_WindowData *) window->driverdata;
   370 
   371     if (WINRT_GlobalSDLWindow == window) {
   372         WINRT_GlobalSDLWindow = NULL;
   373     }
   374 
   375     if (data) {
   376         // Delete the internal window data:
   377         delete data;
   378         data = NULL;
   379     }
   380 }
   381 
   382 SDL_bool
   383 WINRT_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info)
   384 {
   385     SDL_WindowData * data = (SDL_WindowData *) window->driverdata;
   386 
   387     if (info->version.major <= SDL_MAJOR_VERSION) {
   388         info->subsystem = SDL_SYSWM_WINRT;
   389         info->info.winrt.window = reinterpret_cast<IInspectable *>(data->coreWindow.Get());
   390         return SDL_TRUE;
   391     } else {
   392         SDL_SetError("Application not compiled with SDL %d.%d\n",
   393                      SDL_MAJOR_VERSION, SDL_MINOR_VERSION);
   394         return SDL_FALSE;
   395     }
   396     return SDL_FALSE;
   397 }
   398 
   399 #endif /* SDL_VIDEO_DRIVER_WINRT */
   400 
   401 /* vi: set ts=4 sw=4 expandtab: */