1 /** 2 * Common code shared by SDL-based video drivers. 3 * 4 * License: 5 * This Source Code Form is subject to the terms of 6 * the Mozilla Public License, v. 2.0. If a copy of 7 * the MPL was not distributed with this file, You 8 * can obtain one at http://mozilla.org/MPL/2.0/. 9 * 10 * Authors: 11 * Vladimir Panteleev <ae@cy.md> 12 */ 13 14 module ae.ui.video.sdl2common.video; 15 16 import std.process : environment; 17 import std.string; 18 19 import derelict.sdl2.sdl; 20 21 import ae.ui.video.threadedvideo; 22 import ae.ui.app.application; 23 import ae.ui.shell.shell; 24 import ae.ui.shell.sdl2.shell; 25 26 /// `Video` implementation for SDL2. 27 class SDL2CommonVideo : ThreadedVideo 28 { 29 override void getScreenSize(out uint width, out uint height) 30 { 31 width = screenWidth; 32 height = screenHeight; 33 } /// 34 35 override void shutdown() 36 { 37 super.shutdown(); 38 if (window) 39 { 40 SDL_DestroyWindow(window); 41 window = null; 42 } 43 } /// 44 45 SDL_Window* window; /// SDL window object. 46 SDL_Renderer* renderer; /// SDL renderer object. 47 48 protected: 49 override @property bool initializeVideoInRenderThread() 50 { 51 return true; 52 } 53 54 uint getSDLFlags () { return 0; } 55 SDL_RendererFlags getRendererFlags() { return SDL_RENDERER_PRESENTVSYNC; } 56 void prepare() {} 57 58 uint screenWidth, screenHeight; 59 60 /// Main thread initialization. 61 override void initMain(Application application) 62 { 63 SDL_WindowFlags flags = SDL_WINDOW_SHOWN; 64 flags |= getSDLFlags(); 65 66 auto settings = application.getShellSettings(); 67 screenWidth = screenHeight = 0; 68 uint windowPosX = SDL_WINDOWPOS_UNDEFINED, windowPosY = SDL_WINDOWPOS_UNDEFINED; 69 70 final switch (settings.screenMode) 71 { 72 case ScreenMode.windowed: 73 screenWidth = settings.windowSizeX; 74 screenHeight = settings.windowSizeY; 75 windowPosX = settings.windowPosX == int.min ? SDL_WINDOWPOS_CENTERED : settings.windowPosX; 76 windowPosY = settings.windowPosY == int.min ? SDL_WINDOWPOS_CENTERED : settings.windowPosY; 77 break; 78 case ScreenMode.maximized: 79 flags |= SDL_WINDOW_MAXIMIZED; 80 break; 81 case ScreenMode.fullscreen: 82 screenWidth = settings.fullScreenX; 83 screenHeight = settings.fullScreenY; 84 flags |= SDL_WINDOW_FULLSCREEN; 85 break; 86 case ScreenMode.windowedFullscreen: 87 { 88 SDL_DisplayMode dm; 89 sdlEnforce(SDL_GetDesktopDisplayMode(0, &dm)==0, "Can't get desktop display mode"); 90 windowPosX = 0; 91 windowPosY = 0; 92 screenWidth = dm.w; 93 screenHeight = dm.h; 94 flags |= SDL_WINDOW_BORDERLESS; 95 break; 96 } 97 } 98 99 if (application.isResizable()) 100 flags |= SDL_WINDOW_RESIZABLE; 101 102 if (window) 103 { 104 // We need to recreate the window if renderer flags, 105 // such as SDL_WINDOW_OPENGL, have changed. 106 // Also recreate when switching fullscreen modes. 107 enum recreateMask = 108 SDL_WINDOW_OPENGL | 109 SDL_WINDOW_FULLSCREEN | 110 SDL_WINDOW_BORDERLESS | 111 SDL_WINDOW_RESIZABLE; 112 if ((currentFlags & recreateMask) != (flags & recreateMask) 113 || (flags & SDL_WINDOW_FULLSCREEN)) 114 { 115 SDL_DestroyWindow(window); 116 window = null; 117 } 118 } 119 120 if (window) 121 { 122 // Adjust parameters of existing window. 123 124 if (windowPosX != SDL_WINDOWPOS_UNDEFINED && windowPosY != SDL_WINDOWPOS_UNDEFINED) 125 { 126 int currentX, currentY; 127 SDL_GetWindowPosition(window, ¤tX, ¤tY); 128 if (currentX != windowPosX || currentY != windowPosY) 129 SDL_SetWindowPosition(window, windowPosX, windowPosY); 130 } 131 if (screenWidth && screenHeight) 132 { 133 int currentW, currentH; 134 SDL_GetWindowSize(window, ¤tW, ¤tH); 135 if (currentW != screenWidth || currentH != screenHeight) 136 SDL_SetWindowSize(window, screenWidth, screenHeight); 137 } 138 } 139 else 140 { 141 // Create a new window. 142 143 // Window must always be created in the main (SDL event) thread, 144 // otherwise we get Win32 deadlocks due to messages being sent 145 // to the render thread. 146 // As a result, if the event thread does something that results 147 // in a Windows message, the message gets put on the render thread 148 // message queue. However, while waiting for the message to be 149 // processed, the event thread holds the application global lock, 150 // and the render thread is waiting on it - thus resulting in a 151 // deadlock. 152 153 window = sdlEnforce( 154 SDL_CreateWindow( 155 toStringz(application.getName()), 156 windowPosX, windowPosY, 157 screenWidth, screenHeight, 158 flags), 159 "Can't create window"); 160 } 161 162 currentFlags = flags; 163 } 164 165 /// Main/render thread initialization (depends on InitializeVideoInRenderThread). 166 override void initVary() 167 { 168 prepare(); 169 renderer = sdlEnforce(SDL_CreateRenderer(window, -1, getRendererFlags()), "Can't create renderer"); 170 } 171 172 /// Main/render thread finalization (depends on InitializeVideoInRenderThread). 173 override void doneVary() 174 { 175 SDL_DestroyRenderer(renderer); renderer = null; 176 } 177 178 /// Main thread finalization. 179 override void doneMain() {} 180 181 private: 182 uint currentFlags; 183 }