From 6599aeb0355925f6e211e50dd95304160090ecc2 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Fri, 23 Dec 2016 16:27:01 +0000 Subject: [PATCH] Add support for retina displays on OSX. --- OpenRA.Game/Graphics/PlatformInterfaces.cs | 2 + OpenRA.Game/Renderer.cs | 2 +- .../Sdl2GraphicsDevice.cs | 62 +++++++++++++++++-- OpenRA.Platforms.Default/Sdl2Input.cs | 7 ++- 4 files changed, 67 insertions(+), 6 deletions(-) diff --git a/OpenRA.Game/Graphics/PlatformInterfaces.cs b/OpenRA.Game/Graphics/PlatformInterfaces.cs index 436e445384..4a889ec1a0 100644 --- a/OpenRA.Game/Graphics/PlatformInterfaces.cs +++ b/OpenRA.Game/Graphics/PlatformInterfaces.cs @@ -43,6 +43,8 @@ namespace OpenRA IShader CreateShader(string name); Size WindowSize { get; } + float WindowScale { get; } + event Action OnWindowScaleChanged; void Clear(); void Present(); diff --git a/OpenRA.Game/Renderer.cs b/OpenRA.Game/Renderer.cs index 35296e475a..db29ea8543 100644 --- a/OpenRA.Game/Renderer.cs +++ b/OpenRA.Game/Renderer.cs @@ -86,7 +86,7 @@ namespace OpenRA fontSheetBuilder.Dispose(); fontSheetBuilder = new SheetBuilder(SheetType.BGRA); Fonts = modData.Manifest.Fonts.ToDictionary(x => x.Key, - x => new SpriteFont(x.Value.First, modData.DefaultFileSystem.Open(x.Value.First).ReadAllBytes(), x.Value.Second, fontSheetBuilder)).AsReadOnly(); + x => new SpriteFont(x.Value.First, modData.DefaultFileSystem.Open(x.Value.First).ReadAllBytes(), x.Value.Second, Device.WindowScale, fontSheetBuilder)).AsReadOnly(); } } diff --git a/OpenRA.Platforms.Default/Sdl2GraphicsDevice.cs b/OpenRA.Platforms.Default/Sdl2GraphicsDevice.cs index 19812ccd2f..ae368bb51b 100644 --- a/OpenRA.Platforms.Default/Sdl2GraphicsDevice.cs +++ b/OpenRA.Platforms.Default/Sdl2GraphicsDevice.cs @@ -21,10 +21,15 @@ namespace OpenRA.Platforms.Default sealed class Sdl2GraphicsDevice : ThreadAffine, IGraphicsDevice { readonly Sdl2Input input; + IntPtr context, window; bool disposed; public Size WindowSize { get; private set; } + public float WindowScale { get; private set; } + + internal Size SurfaceSize { get; private set; } + public event Action OnWindowScaleChanged = (before, after) => { }; public Sdl2GraphicsDevice(Size windowSize, WindowMode windowMode) { @@ -50,8 +55,28 @@ namespace OpenRA.Platforms.Default Console.WriteLine("Using resolution: {0}x{1}", WindowSize.Width, WindowSize.Height); + var windowFlags = SDL.SDL_WindowFlags.SDL_WINDOW_OPENGL; + if (Platform.CurrentPlatform == PlatformType.OSX) + windowFlags |= SDL.SDL_WindowFlags.SDL_WINDOW_ALLOW_HIGHDPI; + window = SDL.SDL_CreateWindow("OpenRA", SDL.SDL_WINDOWPOS_CENTERED, SDL.SDL_WINDOWPOS_CENTERED, - WindowSize.Width, WindowSize.Height, SDL.SDL_WindowFlags.SDL_WINDOW_OPENGL); + WindowSize.Width, WindowSize.Height, windowFlags); + + SurfaceSize = WindowSize; + WindowScale = 1; + + // Enable high resolution rendering for Retina displays + if (Platform.CurrentPlatform == PlatformType.OSX) + { + // OSX defines the window size in "points", with a device-dependent number of pixels per point. + // The window scale is simply the ratio of GL pixels / window points. + int width, height; + SDL.SDL_GL_GetDrawableSize(window, out width, out height); + SurfaceSize = new Size(width, height); + WindowScale = width * 1f / WindowSize.Width; + } + + Console.WriteLine("Using window scale {0:F2}", WindowScale); if (Game.Settings.Game.LockMouseWindow) GrabWindowMouseFocus(); @@ -114,6 +139,26 @@ namespace OpenRA.Platforms.Default } } + internal void WindowSizeChanged() + { + // The ratio between pixels and points can change when moving between displays in OSX + // We need to recalculate our scale to account for the potential change in the actual rendered area + if (Platform.CurrentPlatform == PlatformType.OSX) + { + int width, height; + SDL.SDL_GL_GetDrawableSize(window, out width, out height); + + if (width != SurfaceSize.Width || height != SurfaceSize.Height) + { + var oldScale = WindowScale; + SurfaceSize = new Size(width, height); + WindowScale = width * 1f / WindowSize.Width; + + OnWindowScaleChanged(oldScale, WindowScale); + } + } + } + sealed class SDL2HardwareCursor : IHardwareCursor { public IntPtr Cursor { get; private set; } @@ -315,7 +360,16 @@ namespace OpenRA.Platforms.Default if (height < 0) height = 0; - OpenGL.glScissor(left, WindowSize.Height - (top + height), width, height); + var bottom = WindowSize.Height - (top + height); + if (WindowSize != SurfaceSize) + { + left = (int)Math.Round(WindowScale * left); + bottom = (int)Math.Round(WindowScale * bottom); + width = (int)Math.Round(WindowScale * width); + height = (int)Math.Round(WindowScale * height); + } + + OpenGL.glScissor(left, bottom, width, height); OpenGL.CheckGLError(); OpenGL.glEnable(OpenGL.GL_SCISSOR_TEST); OpenGL.CheckGLError(); @@ -330,7 +384,7 @@ namespace OpenRA.Platforms.Default public Bitmap TakeScreenshot() { - var rect = new Rectangle(Point.Empty, WindowSize); + var rect = new Rectangle(Point.Empty, SurfaceSize); var bitmap = new Bitmap(rect.Width, rect.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb); var data = bitmap.LockBits(rect, System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); @@ -362,7 +416,7 @@ namespace OpenRA.Platforms.Default public void PumpInput(IInputHandler inputHandler) { VerifyThreadAffinity(); - input.PumpInput(inputHandler); + input.PumpInput(this, inputHandler); } public string GetClipboardText() diff --git a/OpenRA.Platforms.Default/Sdl2Input.cs b/OpenRA.Platforms.Default/Sdl2Input.cs index 012685892d..dd19d34467 100644 --- a/OpenRA.Platforms.Default/Sdl2Input.cs +++ b/OpenRA.Platforms.Default/Sdl2Input.cs @@ -40,7 +40,7 @@ namespace OpenRA.Platforms.Default | ((raw & (int)SDL.SDL_Keymod.KMOD_SHIFT) != 0 ? Modifiers.Shift : 0); } - public void PumpInput(IInputHandler inputHandler) + public void PumpInput(Sdl2GraphicsDevice device, IInputHandler inputHandler) { var mods = MakeModifiers((int)SDL.SDL_GetModState()); var scrollDelta = 0; @@ -67,6 +67,11 @@ namespace OpenRA.Platforms.Default case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_FOCUS_GAINED: Game.HasInputFocus = true; break; + + // Triggered when moving between displays with different DPI settings + case SDL.SDL_WindowEventID.SDL_WINDOWEVENT_SIZE_CHANGED: + device.WindowSizeChanged(); + break; } break;