From 869a707612ffdff63204f6a350edd65a549a54d4 Mon Sep 17 00:00:00 2001 From: Edward Rudd Date: Fri, 20 Sep 2013 13:43:00 -0400 Subject: [PATCH] add in High DPI support (aka Retina) - based on J?rgen's patch with a few bug fixes --- include/SDL_hints.h | 5 +++++ include/SDL_video.h | 29 +++++++++++++++++++++----- src/render/SDL_render.c | 11 +++++++--- src/render/opengl/SDL_render_gl.c | 10 +++++++++ src/test/SDL_test_common.c | 6 +++++- src/video/SDL_sysvideo.h | 1 + src/video/SDL_video.c | 23 +++++++++++++++++++++ src/video/cocoa/SDL_cocoaopengl.h | 2 ++ src/video/cocoa/SDL_cocoaopengl.m | 34 +++++++++++++++++++++++++++++++ src/video/cocoa/SDL_cocoavideo.m | 1 + src/video/cocoa/SDL_cocoawindow.m | 14 +++++++++++++ test/testgl2.c | 9 ++++++-- 12 files changed, 134 insertions(+), 11 deletions(-) diff --git a/include/SDL_hints.h b/include/SDL_hints.h index 74b63ee09..e90ba2e9d 100644 --- a/include/SDL_hints.h +++ b/include/SDL_hints.h @@ -257,6 +257,11 @@ extern "C" { #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 diff --git a/include/SDL_video.h b/include/SDL_video.h index d4133b026..6822173a5 100644 --- a/include/SDL_video.h +++ b/include/SDL_video.h @@ -107,7 +107,8 @@ typedef enum 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 @@ extern DECLSPEC Uint32 SDLCALL SDL_GetWindowPixelFormat(SDL_Window * window); * \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. * @@ -899,6 +901,23 @@ extern DECLSPEC SDL_Window* SDLCALL SDL_GL_GetCurrentWindow(void); */ 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. * diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c index 1e7f01f62..ef690cf79 100644 --- a/src/render/SDL_render.c +++ b/src/render/SDL_render.c @@ -117,7 +117,12 @@ SDL_RendererEventWatch(void *userdata, SDL_Event *event) /* 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 @@ SDL_GetRendererOutputSize(SDL_Renderer * renderer, int *w, int *h) 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"); diff --git a/src/render/opengl/SDL_render_gl.c b/src/render/opengl/SDL_render_gl.c index 043e5017a..cd0a7085d 100644 --- a/src/render/opengl/SDL_render_gl.c +++ b/src/render/opengl/SDL_render_gl.c @@ -47,6 +47,7 @@ static const float inv255f = 1.0f / 255.0f; 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 @@ GL_CreateRenderer(SDL_Window * window, Uint32 flags) } renderer->WindowEvent = GL_WindowEvent; + renderer->GetOutputSize = GL_GetOutputSize; renderer->CreateTexture = GL_CreateTexture; renderer->UpdateTexture = GL_UpdateTexture; renderer->LockTexture = GL_LockTexture; @@ -539,6 +541,14 @@ GL_WindowEvent(SDL_Renderer * renderer, const SDL_WindowEvent *event) } } +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) { diff --git a/src/test/SDL_test_common.c b/src/test/SDL_test_common.c index 3b2028ab0..e0ff37607 100644 --- a/src/test/SDL_test_common.c +++ b/src/test/SDL_test_common.c @@ -27,7 +27,7 @@ #include #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 @@ SDLTest_CommonArg(SDLTest_CommonState * state, int index) 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])) { diff --git a/src/video/SDL_sysvideo.h b/src/video/SDL_sysvideo.h index 2d36c9d7a..32966997d 100644 --- a/src/video/SDL_sysvideo.h +++ b/src/video/SDL_sysvideo.h @@ -224,6 +224,7 @@ struct SDL_VideoDevice 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); diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c index 88296928f..b5c31324d 100644 --- a/src/video/SDL_video.c +++ b/src/video/SDL_video.c @@ -1187,6 +1187,7 @@ SDL_Window * 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 @@ SDL_CreateWindow(const char *title, int x, int y, int w, int h, Uint32 flags) 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 @@ SDL_GL_GetCurrentContext(void) 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) { diff --git a/src/video/cocoa/SDL_cocoaopengl.h b/src/video/cocoa/SDL_cocoaopengl.h index 2d06700b6..e7ef1f66c 100644 --- a/src/video/cocoa/SDL_cocoaopengl.h +++ b/src/video/cocoa/SDL_cocoaopengl.h @@ -54,6 +54,8 @@ extern void Cocoa_GL_UnloadLibrary(_THIS); 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); diff --git a/src/video/cocoa/SDL_cocoaopengl.m b/src/video/cocoa/SDL_cocoaopengl.m index 93d0b2ad2..13c7777b1 100644 --- a/src/video/cocoa/SDL_cocoaopengl.m +++ b/src/video/cocoa/SDL_cocoaopengl.m @@ -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 @@ Cocoa_GL_MakeCurrent(_THIS, SDL_Window * window, SDL_GLContext context) 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) { diff --git a/src/video/cocoa/SDL_cocoavideo.m b/src/video/cocoa/SDL_cocoavideo.m index da71df03f..d1b958768 100644 --- a/src/video/cocoa/SDL_cocoavideo.m +++ b/src/video/cocoa/SDL_cocoavideo.m @@ -119,6 +119,7 @@ Cocoa_CreateDevice(int devindex) 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; diff --git a/src/video/cocoa/SDL_cocoawindow.m b/src/video/cocoa/SDL_cocoawindow.m index ecfab8204..581590b67 100644 --- a/src/video/cocoa/SDL_cocoawindow.m +++ b/src/video/cocoa/SDL_cocoawindow.m @@ -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 @@ Cocoa_CreateWindow(_THIS, SDL_Window * window) /* 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]; diff --git a/test/testgl2.c b/test/testgl2.c index 571a69d9a..642fe47c6 100644 --- a/test/testgl2.c +++ b/test/testgl2.c @@ -180,6 +180,7 @@ main(int argc, char *argv[]) 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 @@ main(int argc, char *argv[]) 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 @@ main(int argc, char *argv[]) glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); glShadeModel(GL_SMOOTH); - + /* Main render loop */ frames = 0; then = SDL_GetTicks(); @@ -336,7 +341,7 @@ main(int argc, char *argv[]) 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]);