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 <vladimir@thecybershadow.net>
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 class SDL2CommonVideo : ThreadedVideo
27 {
28 	override void getScreenSize(out uint width, out uint height)
29 	{
30 		width = screenWidth;
31 		height = screenHeight;
32 	}
33 
34 	override void shutdown()
35 	{
36 		super.shutdown();
37 		if (window)
38 		{
39 			SDL_DestroyWindow(window);
40 			window = null;
41 		}
42 	}
43 
44 	SDL_Window* window;
45 	SDL_Renderer* renderer;
46 
47 protected:
48 	override @property bool initializeVideoInRenderThread()
49 	{
50 		return true;
51 	}
52 
53 	uint getSDLFlags     () { return 0; }
54 	SDL_RendererFlags getRendererFlags() { return cast(SDL_RendererFlags)0; }
55 	void prepare() {}
56 
57 	uint screenWidth, screenHeight;
58 
59 	/// Main thread initialization.
60 	override void initMain(Application application)
61 	{
62 		SDL_WindowFlags flags = SDL_WINDOW_SHOWN;
63 		flags |= getSDLFlags();
64 
65 		auto settings = application.getShellSettings();
66 		screenWidth = screenHeight = 0;
67 		uint windowPosX = SDL_WINDOWPOS_UNDEFINED, windowPosY = SDL_WINDOWPOS_UNDEFINED;
68 
69 		final switch (settings.screenMode)
70 		{
71 			case ScreenMode.windowed:
72 				screenWidth  = settings.windowSizeX;
73 				screenHeight = settings.windowSizeY;
74 				windowPosX = settings.windowPosX == int.min ? SDL_WINDOWPOS_CENTERED : settings.windowPosX;
75 				windowPosY = settings.windowPosY == int.min ? SDL_WINDOWPOS_CENTERED : settings.windowPosY;
76 				break;
77 			case ScreenMode.maximized:
78 				flags |= SDL_WINDOW_MAXIMIZED;
79 				break;
80 			case ScreenMode.fullscreen:
81 				screenWidth  = settings.fullScreenX;
82 				screenHeight = settings.fullScreenY;
83 				flags |= SDL_WINDOW_FULLSCREEN;
84 				break;
85 			case ScreenMode.windowedFullscreen:
86 			{
87 				SDL_DisplayMode dm;
88 				sdlEnforce(SDL_GetDesktopDisplayMode(0, &dm)==0, "Can't get desktop display mode");
89 				windowPosX = 0;
90 				windowPosY = 0;
91 				screenWidth  = dm.w;
92 				screenHeight = dm.h;
93 				flags |= SDL_WINDOW_BORDERLESS;
94 				break;
95 			}
96 		}
97 
98 		if (application.isResizable())
99 			flags |= SDL_WINDOW_RESIZABLE;
100 
101 		if (window)
102 		{
103 			// We need to recreate the window if renderer flags,
104 			// such as SDL_WINDOW_OPENGL, have changed.
105 			// Also recreate when switching fullscreen modes.
106 			enum recreateMask =
107 				SDL_WINDOW_OPENGL |
108 				SDL_WINDOW_FULLSCREEN |
109 				SDL_WINDOW_BORDERLESS |
110 				SDL_WINDOW_RESIZABLE;
111 			if ((currentFlags & recreateMask) != (flags & recreateMask)
112 			 || (flags & SDL_WINDOW_FULLSCREEN))
113 			{
114 				SDL_DestroyWindow(window);
115 				window = null;
116 			}
117 		}
118 
119 		if (window)
120 		{
121 			// Adjust parameters of existing window.
122 
123 			if (windowPosX != SDL_WINDOWPOS_UNDEFINED && windowPosY != SDL_WINDOWPOS_UNDEFINED)
124 			{
125 				int currentX, currentY;
126 				SDL_GetWindowPosition(window, &currentX, &currentY);
127 				if (currentX != windowPosX || currentY != windowPosY)
128 					SDL_SetWindowPosition(window, windowPosX, windowPosY);
129 			}
130 			if (screenWidth && screenHeight)
131 			{
132 				int currentW, currentH;
133 				SDL_GetWindowSize(window, &currentW, &currentH);
134 				if (currentW != screenWidth || currentH != screenHeight)
135 					SDL_SetWindowSize(window, screenWidth, screenHeight);
136 			}
137 		}
138 		else
139 		{
140 			// Create a new window.
141 
142 			// Window must always be created in the main (SDL event) thread,
143 			// otherwise we get Win32 deadlocks due to messages being sent
144 			// to the render thread.
145 			// As a result, if the event thread does something that results
146 			// in a Windows message, the message gets put on the render thread
147 			// message queue. However, while waiting for the message to be
148 			// processed, the event thread holds the application global lock,
149 			// and the render thread is waiting on it - thus resulting in a
150 			// deadlock.
151 
152 			window = sdlEnforce(
153 				SDL_CreateWindow(
154 					toStringz(application.getName()),
155 					windowPosX, windowPosY,
156 					screenWidth, screenHeight,
157 					flags),
158 				"Can't create window");
159 		}
160 
161 		currentFlags = flags;
162 	}
163 
164 	/// Main/render thread initialization (depends on InitializeVideoInRenderThread).
165 	override void initVary()
166 	{
167 		prepare();
168 		renderer = sdlEnforce(SDL_CreateRenderer(window, -1, getRendererFlags()), "Can't create renderer");
169 	}
170 
171 	/// Main/render thread finalization (depends on InitializeVideoInRenderThread).
172 	override void doneVary()
173 	{
174 		SDL_DestroyRenderer(renderer); renderer = null;
175 	}
176 
177 	/// Main thread finalization.
178 	override void doneMain() {}
179 
180 private:
181 	uint currentFlags;
182 }