add in High DPI support (aka Retina)
authorEdward Rudd <urkle@outoforder.cc>
Fri, 20 Sep 2013 13:43:00 -0400
changeset 7746 6a05d7352575
parent 7745 c52d519d104a
child 7747 0ebf642117f4
add in High DPI support (aka Retina) - based on Jørgen's patch with a few bug fixes
include/SDL_hints.h
include/SDL_video.h
src/render/SDL_render.c
src/render/opengl/SDL_render_gl.c
src/test/SDL_test_common.c
src/video/SDL_sysvideo.h
src/video/SDL_video.c
src/video/cocoa/SDL_cocoaopengl.h
src/video/cocoa/SDL_cocoaopengl.m
src/video/cocoa/SDL_cocoavideo.m
src/video/cocoa/SDL_cocoawindow.m
test/testgl2.c
--- a/include/SDL_hints.h	Fri Sep 27 22:09:51 2013 -0700
+++ b/include/SDL_hints.h	Fri Sep 20 13:43:00 2013 -0400
@@ -257,6 +257,11 @@
 #define SDL_HINT_TIMER_RESOLUTION "SDL_TIMER_RESOLUTION"
 
 
+/**
+ *  \brief If set to 1, then do not allow high-DPI windows. ("Retina" on Mac)
+ */
+#define SDL_HINT_VIDEO_HIGHDPI_DISABLED "SDL_HIGHDPI_DISABLED"
+
 
 /**
  *  \brief  An enumeration of hint priorities
--- a/include/SDL_video.h	Fri Sep 27 22:09:51 2013 -0700
+++ b/include/SDL_video.h	Fri Sep 20 13:43:00 2013 -0400
@@ -107,7 +107,8 @@
     SDL_WINDOW_INPUT_FOCUS = 0x00000200,        /**< window has input focus */
     SDL_WINDOW_MOUSE_FOCUS = 0x00000400,        /**< window has mouse focus */
     SDL_WINDOW_FULLSCREEN_DESKTOP = ( SDL_WINDOW_FULLSCREEN | 0x00001000 ),
-    SDL_WINDOW_FOREIGN = 0x00000800             /**< window not created by SDL */
+    SDL_WINDOW_FOREIGN = 0x00000800,            /**< window not created by SDL */
+    SDL_WINDOW_ALLOW_HIGHDPI = 0x00002000,      /**< window should be created in high-DPI mode if supported */
 } SDL_WindowFlags;
 
 /**
@@ -393,10 +394,11 @@
  *  \param w     The width of the window.
  *  \param h     The height of the window.
  *  \param flags The flags for the window, a mask of any of the following:
- *               ::SDL_WINDOW_FULLSCREEN, ::SDL_WINDOW_OPENGL,
- *               ::SDL_WINDOW_HIDDEN,     ::SDL_WINDOW_BORDERLESS,
- *               ::SDL_WINDOW_RESIZABLE,  ::SDL_WINDOW_MAXIMIZED,
- *               ::SDL_WINDOW_MINIMIZED,  ::SDL_WINDOW_INPUT_GRABBED.
+ *               ::SDL_WINDOW_FULLSCREEN,    ::SDL_WINDOW_OPENGL,
+ *               ::SDL_WINDOW_HIDDEN,        ::SDL_WINDOW_BORDERLESS,
+ *               ::SDL_WINDOW_RESIZABLE,     ::SDL_WINDOW_MAXIMIZED,
+ *               ::SDL_WINDOW_MINIMIZED,     ::SDL_WINDOW_INPUT_GRABBED,
+ *               ::SDL_WINDOW_ALLOW_HIGHDPI.
  *
  *  \return The id of the window created, or zero if window creation failed.
  *
@@ -900,6 +902,23 @@
 extern DECLSPEC SDL_GLContext SDLCALL SDL_GL_GetCurrentContext(void);
 
 /**
+ *  \brief Get the size of a window's underlying drawable (for use with glViewport).
+ *
+ *  \param w        Pointer to variable for storing the width, may be NULL
+ *  \param h        Pointer to variable for storing the height, may be NULL
+ *
+ * This may differ from SDL_GetWindowSize if we're rendering to a high-DPI
+ * drawable, i.e. the window was created with SDL_WINDOW_ALLOW_HIGHDPI on a
+ * platform with high-DPI support (Apple calls this "Retina"), and not disabled
+ * by the SDL_HINT_VIDEO_HIGHDPI_DISABLED hint.
+ *
+ *  \sa SDL_GetWindowSize()
+ *  \sa SDL_CreateWindow()
+ */
+extern DECLSPEC void SDLCALL SDL_GL_GetDrawableSize(SDL_Window * window, int *w,
+                                                    int *h);
+
+/**
  *  \brief Set the swap interval for the current OpenGL context.
  *
  *  \param interval 0 for immediate updates, 1 for updates synchronized with the
--- a/src/render/SDL_render.c	Fri Sep 27 22:09:51 2013 -0700
+++ b/src/render/SDL_render.c	Fri Sep 20 13:43:00 2013 -0400
@@ -117,7 +117,12 @@
                     /* Window was resized, reset viewport */
                     int w, h;
 
-                    SDL_GetWindowSize(window, &w, &h);
+                    if (renderer->GetOutputSize) {
+                        renderer->GetOutputSize(renderer, &w, &h);
+                    } else {
+                        SDL_GetWindowSize(renderer->window, &w, &h);
+                    }
+
                     if (renderer->target) {
                         renderer->viewport_backup.x = 0;
                         renderer->viewport_backup.y = 0;
@@ -335,11 +340,11 @@
 
     if (renderer->target) {
         return SDL_QueryTexture(renderer->target, NULL, NULL, w, h);
+    } else if (renderer->GetOutputSize) {
+        return renderer->GetOutputSize(renderer, w, h);
     } else if (renderer->window) {
         SDL_GetWindowSize(renderer->window, w, h);
         return 0;
-    } else if (renderer->GetOutputSize) {
-        return renderer->GetOutputSize(renderer, w, h);
     } else {
         /* This should never happen */
         SDL_SetError("Renderer doesn't support querying output size");
--- a/src/render/opengl/SDL_render_gl.c	Fri Sep 27 22:09:51 2013 -0700
+++ b/src/render/opengl/SDL_render_gl.c	Fri Sep 20 13:43:00 2013 -0400
@@ -47,6 +47,7 @@
 static SDL_Renderer *GL_CreateRenderer(SDL_Window * window, Uint32 flags);
 static void GL_WindowEvent(SDL_Renderer * renderer,
                            const SDL_WindowEvent *event);
+static int GL_GetOutputSize(SDL_Renderer * renderer, int *w, int *h);
 static int GL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture);
 static int GL_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture,
                             const SDL_Rect * rect, const void *pixels,
@@ -399,6 +400,7 @@
     }
 
     renderer->WindowEvent = GL_WindowEvent;
+    renderer->GetOutputSize = GL_GetOutputSize;
     renderer->CreateTexture = GL_CreateTexture;
     renderer->UpdateTexture = GL_UpdateTexture;
     renderer->LockTexture = GL_LockTexture;
@@ -539,6 +541,14 @@
     }
 }
 
+static int
+GL_GetOutputSize(SDL_Renderer * renderer, int *w, int *h)
+{
+    SDL_GL_GetDrawableSize(renderer->window, w, h);
+
+    return 0;
+}
+
 SDL_FORCE_INLINE int
 power_of_2(int input)
 {
--- a/src/test/SDL_test_common.c	Fri Sep 27 22:09:51 2013 -0700
+++ b/src/test/SDL_test_common.c	Fri Sep 20 13:43:00 2013 -0400
@@ -27,7 +27,7 @@
 #include <stdio.h>
 
 #define VIDEO_USAGE \
-"[--video driver] [--renderer driver] [--gldebug] [--info all|video|modes|render|event] [--log all|error|system|audio|video|render|input] [--display N] [--fullscreen | --fullscreen-desktop | --windows N] [--title title] [--icon icon.bmp] [--center | --position X,Y] [--geometry WxH] [--min-geometry WxH] [--max-geometry WxH] [--logical WxH] [--scale N] [--depth N] [--refresh R] [--vsync] [--noframe] [--resize] [--minimize] [--maximize] [--grab]"
+"[--video driver] [--renderer driver] [--gldebug] [--info all|video|modes|render|event] [--log all|error|system|audio|video|render|input] [--display N] [--fullscreen | --fullscreen-desktop | --windows N] [--title title] [--icon icon.bmp] [--center | --position X,Y] [--geometry WxH] [--min-geometry WxH] [--max-geometry WxH] [--logical WxH] [--scale N] [--depth N] [--refresh R] [--vsync] [--noframe] [--resize] [--minimize] [--maximize] [--grab] [--allow-hidpi]"
 
 #define AUDIO_USAGE \
 "[--rate N] [--format U8|S8|U16|U16LE|U16BE|S16|S16LE|S16BE] [--channels N] [--samples N]"
@@ -194,6 +194,10 @@
         state->num_windows = 1;
         return 1;
     }
+    if (SDL_strcasecmp(argv[index], "--allow-highdpi") == 0) {
+        state->window_flags |= SDL_WINDOW_ALLOW_HIGHDPI;
+        return 1;
+    }
     if (SDL_strcasecmp(argv[index], "--windows") == 0) {
         ++index;
         if (!argv[index] || !SDL_isdigit(*argv[index])) {
--- a/src/video/SDL_sysvideo.h	Fri Sep 27 22:09:51 2013 -0700
+++ b/src/video/SDL_sysvideo.h	Fri Sep 20 13:43:00 2013 -0400
@@ -224,6 +224,7 @@
     void (*GL_UnloadLibrary) (_THIS);
       SDL_GLContext(*GL_CreateContext) (_THIS, SDL_Window * window);
     int (*GL_MakeCurrent) (_THIS, SDL_Window * window, SDL_GLContext context);
+    void (*GL_GetDrawableSize) (_THIS, SDL_Window * window, int *w, int *h);
     int (*GL_SetSwapInterval) (_THIS, int interval);
     int (*GL_GetSwapInterval) (_THIS);
     void (*GL_SwapWindow) (_THIS, SDL_Window * window);
--- a/src/video/SDL_video.c	Fri Sep 27 22:09:51 2013 -0700
+++ b/src/video/SDL_video.c	Fri Sep 20 13:43:00 2013 -0400
@@ -1187,6 +1187,7 @@
 SDL_CreateWindow(const char *title, int x, int y, int w, int h, Uint32 flags)
 {
     SDL_Window *window;
+    const char *hint;
 
     if (!_this) {
         /* Initialize the video system if needed */
@@ -1245,6 +1246,17 @@
     window->flags = ((flags & CREATE_FLAGS) | SDL_WINDOW_HIDDEN);
     window->brightness = 1.0f;
     window->next = _this->windows;
+
+    /* Unless the user has specified the high-DPI disabling hint, respect the
+     * SDL_WINDOW_ALLOW_HIGHDPI flag.
+     */
+    hint = SDL_GetHint(SDL_HINT_VIDEO_HIGHDPI_DISABLED);
+    if (!hint || *hint != '1') {
+        if ((flags & SDL_WINDOW_ALLOW_HIGHDPI)) {
+            window->flags |= SDL_WINDOW_ALLOW_HIGHDPI;
+        }
+    }
+
     if (_this->windows) {
         _this->windows->prev = window;
     }
@@ -2813,6 +2825,17 @@
     return (SDL_GLContext)SDL_TLSGet(_this->current_glctx_tls);
 }
 
+void SDL_GL_GetDrawableSize(SDL_Window * window, int *w, int *h)
+{
+    CHECK_WINDOW_MAGIC(window, );
+
+    if (_this->GL_GetDrawableSize) {
+        _this->GL_GetDrawableSize(_this, window, w, h);
+    } else {
+        SDL_GetWindowSize(window, w, h);
+    }
+}
+
 int
 SDL_GL_SetSwapInterval(int interval)
 {
--- a/src/video/cocoa/SDL_cocoaopengl.h	Fri Sep 27 22:09:51 2013 -0700
+++ b/src/video/cocoa/SDL_cocoaopengl.h	Fri Sep 20 13:43:00 2013 -0400
@@ -54,6 +54,8 @@
 extern SDL_GLContext Cocoa_GL_CreateContext(_THIS, SDL_Window * window);
 extern int Cocoa_GL_MakeCurrent(_THIS, SDL_Window * window,
                                 SDL_GLContext context);
+extern void Cocoa_GL_GetDrawableSize(_THIS, SDL_Window * window,
+                                     int * w, int * h);
 extern int Cocoa_GL_SetSwapInterval(_THIS, int interval);
 extern int Cocoa_GL_GetSwapInterval(_THIS);
 extern void Cocoa_GL_SwapWindow(_THIS, SDL_Window * window);
--- a/src/video/cocoa/SDL_cocoaopengl.m	Fri Sep 27 22:09:51 2013 -0700
+++ b/src/video/cocoa/SDL_cocoaopengl.m	Fri Sep 20 13:43:00 2013 -0400
@@ -35,6 +35,18 @@
 
 #define DEFAULT_OPENGL  "/System/Library/Frameworks/OpenGL.framework/Libraries/libGL.dylib"
 
+#if MAC_OS_X_VERSION_MAX_ALLOWED < 1070
+/* New methods for converting to and from backing store pixels, taken from
+ * AppKite/NSView.h in 10.8 SDK. */
+@interface NSView (Backing)
+- (NSPoint)convertPointToBacking:(NSPoint)aPoint;
+- (NSPoint)convertPointFromBacking:(NSPoint)aPoint;
+- (NSSize)convertSizeToBacking:(NSSize)aSize;
+- (NSSize)convertSizeFromBacking:(NSSize)aSize;
+- (NSRect)convertRectToBacking:(NSRect)aRect;
+- (NSRect)convertRectFromBacking:(NSRect)aRect;
+@end
+#endif
 
 #ifndef kCGLPFAOpenGLProfile
 #define kCGLPFAOpenGLProfile 99
@@ -294,6 +306,28 @@
     return 0;
 }
 
+void
+Cocoa_GL_GetDrawableSize(_THIS, SDL_Window * window, int * w, int * h)
+{
+    SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
+    NSView *contentView = [windata->nswindow contentView];
+    NSRect viewport = [contentView bounds];
+
+    /* This gives us the correct viewport for a Retina-enabled view, only
+     * supported on 10.7+. */
+    if ([contentView respondsToSelector:@selector(convertRectToBacking:)]) {
+        viewport = [contentView convertRectToBacking:viewport];
+    }
+
+    if (w) {
+        *w = viewport.size.width;
+    }
+
+    if (h) {
+        *h = viewport.size.height;
+    }
+}
+
 int
 Cocoa_GL_SetSwapInterval(_THIS, int interval)
 {
--- a/src/video/cocoa/SDL_cocoavideo.m	Fri Sep 27 22:09:51 2013 -0700
+++ b/src/video/cocoa/SDL_cocoavideo.m	Fri Sep 20 13:43:00 2013 -0400
@@ -119,6 +119,7 @@
     device->GL_UnloadLibrary = Cocoa_GL_UnloadLibrary;
     device->GL_CreateContext = Cocoa_GL_CreateContext;
     device->GL_MakeCurrent = Cocoa_GL_MakeCurrent;
+    device->GL_GetDrawableSize = Cocoa_GL_GetDrawableSize;
     device->GL_SetSwapInterval = Cocoa_GL_SetSwapInterval;
     device->GL_GetSwapInterval = Cocoa_GL_GetSwapInterval;
     device->GL_SwapWindow = Cocoa_GL_SwapWindow;
--- a/src/video/cocoa/SDL_cocoawindow.m	Fri Sep 27 22:09:51 2013 -0700
+++ b/src/video/cocoa/SDL_cocoawindow.m	Fri Sep 20 13:43:00 2013 -0400
@@ -34,6 +34,13 @@
 #include "SDL_cocoamouse.h"
 #include "SDL_cocoaopengl.h"
 
+#if MAC_OS_X_VERSION_MAX_ALLOWED < 1070
+/* Taken from AppKit/NSOpenGLView.h in 10.8 SDK. */
+@interface NSView (NSOpenGLSurfaceResolution)
+- (BOOL)wantsBestResolutionOpenGLSurface;
+- (void)setWantsBestResolutionOpenGLSurface:(BOOL)flag;
+@end
+#endif
 
 static Uint32 s_moveHack;
 
@@ -739,6 +746,13 @@
     /* Create a default view for this window */
     rect = [nswindow contentRectForFrameRect:[nswindow frame]];
     NSView *contentView = [[SDLView alloc] initWithFrame:rect];
+
+    if ((window->flags & SDL_WINDOW_ALLOW_HIGHDPI) > 0) {
+        if ([contentView respondsToSelector:@selector(setWantsBestResolutionOpenGLSurface:)]) {
+            [contentView setWantsBestResolutionOpenGLSurface:YES];
+        }
+    }
+
     [nswindow setContentView: contentView];
     [contentView release];
 
--- a/test/testgl2.c	Fri Sep 27 22:09:51 2013 -0700
+++ b/test/testgl2.c	Fri Sep 20 13:43:00 2013 -0400
@@ -180,6 +180,7 @@
     SDL_Event event;
     Uint32 then, now, frames;
     int status;
+    int dw, dh;
 
     /* Enable standard application logging */
     SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
@@ -254,6 +255,10 @@
     SDL_GetCurrentDisplayMode(0, &mode);
     SDL_Log("Screen BPP    : %d\n", SDL_BITSPERPIXEL(mode.format));
     SDL_Log("Swap Interval : %d\n", SDL_GL_GetSwapInterval());
+    SDL_GetWindowSize(state->windows[0], &dw, &dh);
+    SDL_Log("Window Size   : %d,%d\n", dw, dh);
+    SDL_GL_GetDrawableSize(state->windows[0], &dw, &dh);
+    SDL_Log("Draw Size     : %d,%d\n", dw, dh);
     SDL_Log("\n");
     SDL_Log("Vendor        : %s\n", glGetString(GL_VENDOR));
     SDL_Log("Renderer      : %s\n", glGetString(GL_RENDERER));
@@ -322,7 +327,7 @@
     glEnable(GL_DEPTH_TEST);
     glDepthFunc(GL_LESS);
     glShadeModel(GL_SMOOTH);
-
+    
     /* Main render loop */
     frames = 0;
     then = SDL_GetTicks();
@@ -336,7 +341,7 @@
         for (i = 0; i < state->num_windows; ++i) {
             int w, h;
             SDL_GL_MakeCurrent(state->windows[i], context);
-            SDL_GetWindowSize(state->windows[i], &w, &h);
+            SDL_GL_GetDrawableSize(state->windows[i], &w, &h);
             glViewport(0, 0, w, h);
             Render();
             SDL_GL_SwapWindow(state->windows[i]);