foxygit / doom Log in
commit 5d838b88a73c3299fc3472c202718e868fc3c9bd
Author:     Simon Howard <fraggle@soulsphere.org>
AuthorDate: Sat Feb 27 21:59:28 2016 -0500
Commit:     Simon Howard <fraggle@soulsphere.org>
CommitDate: Sat Feb 27 21:59:28 2016 -0500

    textscreen: Add support for OS X high-DPI rendering.

    When using a Mac with a retina display, we want to use the large
    textscreen font, albeit in a normal-sized window. The HIGHDPI flag is
    the way to do this properly with SDL2; try to set it and if we get the
    output screen size we're hoping for, switch to the large font.

    As part of this, bump the SDL2 dependency to 2.0.1, since the
    SDL_WINDOW_ALLOW_HIGHDPI flag was only added in this version.
---
 configure.ac          |   2 +-
 pkg/osx/Info.plist.in |   2 +
 textscreen/txt_sdl.c  | 118 ++++++++++++++++++++++++++++++++++++--------------
 3 files changed, 89 insertions(+), 33 deletions(-)

diff --git a/configure.ac b/configure.ac
index 4d8dd355..7837b64b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -34,7 +34,7 @@ then
         CFLAGS="-O$OPT_LEVEL -g $WARNINGS $orig_CFLAGS"
 fi

-PKG_CHECK_MODULES(SDL, [sdl2 >= 2.0.0])
+PKG_CHECK_MODULES(SDL, [sdl2 >= 2.0.1])
 PKG_CHECK_MODULES(SDLMIXER, [SDL2_mixer >= 2.0.0])
 PKG_CHECK_MODULES(SDLNET, [SDL2_net >= 2.0.0])

diff --git a/pkg/osx/Info.plist.in b/pkg/osx/Info.plist.in
index a648ed8b..b838d757 100644
--- a/pkg/osx/Info.plist.in
+++ b/pkg/osx/Info.plist.in
@@ -24,6 +24,8 @@
 	<string>@PACKAGE_VERSION@</string>
 	<key>CFBundleVersion</key>
 	<string>@PACKAGE_VERSION@</string>
+        <key>NSHighResolutionCapable</key>
+        <string>true</string>
 	<key>NSPrincipalClass</key>
 	<string>NSApplication</string>
 	<key>NSMainNibFile</key>
diff --git a/textscreen/txt_sdl.c b/textscreen/txt_sdl.c
index e0938ad6..5fad0c7e 100644
--- a/textscreen/txt_sdl.c
+++ b/textscreen/txt_sdl.c
@@ -51,6 +51,7 @@ typedef struct
 SDL_Window *TXT_SDLWindow;
 static SDL_Surface *screenbuffer;
 static unsigned char *screendata;
+static SDL_Renderer *renderer;
 static int key_mapping = 1;

 static TxtSDLEventCallbackFunc event_callback;
@@ -59,8 +60,11 @@ static void *event_callback_data;
 static int modifier_state[TXT_NUM_MODIFIERS];

 // Font we are using:
+static const txt_font_t *font;

-static txt_font_t *font;
+// Dummy "font" that means to try highdpi rendering, or fallback to
+// main_font otherwise.
+static const txt_font_t highdpi_font = { NULL, 8, 16 };

 //#define TANGO

@@ -142,24 +146,28 @@ static int Win32_UseLargeFont(void)

 #endif

-static txt_font_t *FontForName(char *name)
+static const txt_font_t *FontForName(char *name)
 {
-    if (!strcmp(name, "small"))
-    {
-        return &small_font;
-    }
-    else if (!strcmp(name, "normal"))
-    {
-        return &main_font;
-    }
-    else if (!strcmp(name, "large"))
-    {
-        return &large_font;
-    }
-    else
-    {
-        return NULL;
+    int i;
+    struct {
+        char *name;
+        const txt_font_t *font;
+    } fonts[] = {
+        { "small", &small_font },
+        { "normal", &main_font },
+        { "large", &large_font },
+        { "normal-highdpi", &highdpi_font },
+        { NULL },
+    };
+
+    for (i = 0; fonts[i].name != NULL; ++i)
+    {
+        if (!strcmp(fonts[i].name, name))
+        {
+            return fonts[i].font;
+        }
     }
+    return NULL;
 }

 //
@@ -175,9 +183,7 @@ static void ChooseFont(void)
     char *env;

     // Allow normal selection to be overridden from an environment variable:
-
     env = getenv("TEXTSCREEN_FONT");
-
     if (env != NULL)
     {
         font = FontForName(env);
@@ -191,10 +197,9 @@ static void ChooseFont(void)
     // Get desktop resolution.
     // If in doubt and we can't get a list, always prefer to
     // fall back to the normal font:
-
     if (!SDL_GetCurrentDisplayMode(0, &desktop_info))
     {
-        font = &main_font;
+        font = &highdpi_font;
         return;
     }

@@ -223,7 +228,10 @@ static void ChooseFont(void)
     // and using large_font if the result is >= 2.
     else
     {
-        font = &main_font;
+        // highdpi_font usually means main_font (the normal resolution
+        // version), but actually means "set the HIGHDPI flag and try
+        // to use large_font if we initialize successfully".
+        font = &highdpi_font;
     }
 }

@@ -235,6 +243,9 @@ static void ChooseFont(void)

 int TXT_Init(void)
 {
+    int window_w, window_h;
+    int flags = 0;
+
     if (SDL_Init(SDL_INIT_VIDEO) < 0)
     {
         return 0;
@@ -242,17 +253,47 @@ int TXT_Init(void)

     ChooseFont();

-    // Always create the screen at the native screen depth (bpp=0);
-    // some systems nowadays don't seem to support true 8-bit palettized
-    // screen modes very well and we end up with screwed up colors.
+    window_w = TXT_SCREEN_W * font->w;
+    window_h = TXT_SCREEN_H * font->h;
+
+    // If highdpi_font is selected, try to initialize high dpi rendering.
+    if (font == &highdpi_font)
+    {
+        flags |= SDL_WINDOW_ALLOW_HIGHDPI;
+    }
+
     TXT_SDLWindow =
         SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
-                         TXT_SCREEN_W * font->w, TXT_SCREEN_H * font->h,
-                         0);
+                         window_w, window_h, flags);

     if (TXT_SDLWindow == NULL)
         return 0;

+    renderer = SDL_CreateRenderer(TXT_SDLWindow, -1, 0);
+
+    // Special handling for OS X retina display. If we successfully set the
+    // highdpi flag, check the output size for the screen renderer. If we get
+    // the 2x doubled size we expect from a retina display, use the large font
+    // for drawing the screen.
+    if ((SDL_GetWindowFlags(TXT_SDLWindow) & SDL_WINDOW_ALLOW_HIGHDPI) != 0)
+    {
+        int render_w, render_h;
+
+        if (SDL_GetRendererOutputSize(renderer, &render_w, &render_h) == 0
+         && render_w == TXT_SCREEN_W * large_font.w
+         && render_h == TXT_SCREEN_H * large_font.h)
+        {
+            font = &large_font;
+        }
+    }
+
+    // Failed to initialize for high dpi (retina display) rendering? If so
+    // then use the normal resolution font instead.
+    if (font == &highdpi_font)
+    {
+        font = &main_font;
+    }
+
     // Instead, we draw everything into an intermediate 8-bit surface
     // the same dimensions as the screen. SDL then takes care of all the
     // 8->32 bit (or whatever depth) color conversions for us.
@@ -380,6 +421,7 @@ static int LimitToRange(int val, int min, int max)

 void TXT_UpdateScreenArea(int x, int y, int w, int h)
 {
+    SDL_Texture *screentx;
     SDL_Rect rect;
     int x1, y1;
     int x_end;
@@ -407,9 +449,15 @@ void TXT_UpdateScreenArea(int x, int y, int w, int h)

     SDL_UnlockSurface(screenbuffer);

-    SDL_BlitSurface(screenbuffer, &rect,
-                    SDL_GetWindowSurface(TXT_SDLWindow), &rect);
-    SDL_UpdateWindowSurfaceRects(TXT_SDLWindow, &rect, 1);
+    // TODO: This is currently creating a new texture every time we render
+    // the screen; find a more efficient way to do it.
+    screentx = SDL_CreateTextureFromSurface(renderer, screenbuffer);
+
+    SDL_RenderClear(renderer);
+    SDL_RenderCopy(renderer, screentx, NULL, NULL);
+    SDL_RenderPresent(renderer);
+
+    SDL_DestroyTexture(screentx);
 }

 void TXT_UpdateScreen(void)
@@ -419,10 +467,16 @@ void TXT_UpdateScreen(void)

 void TXT_GetMousePosition(int *x, int *y)
 {
+    int window_w, window_h;
+
     SDL_GetMouseState(x, y);

-    *x /= font->w;
-    *y /= font->h;
+    // Translate mouse position from 'pixel' position into character position.
+    // Note that font->{w,h} are deliberately not used in this calculation, as
+    // that would break when using highdpi (OS X retina display).
+    SDL_GetWindowSize(TXT_SDLWindow, &window_w, &window_h);
+    *x = (*x * TXT_SCREEN_W) / window_w;
+    *y = (*y * TXT_SCREEN_H) / window_h;
 }

 //