1 module d2d.window.window;
2 
3 import d2d;
4 
5 /// Single-Window class wrapping SDL_Window.
6 class Window : IVerifiable, IDisposable, IRenderTarget
7 {
8 private:
9 	SDL_Window* _handle;
10 	int _id;
11 	uint _fbo, _drb;
12 	Texture _texture;
13 	RectangleShape _displayPlane;
14 	bool _direct = false;
15 	mat4 _postMatrix;
16 
17 public:
18 	/// Static variable to a SDL GL Context.
19 	static SDL_GLContext glContext = null;
20 
21 	/// Creates a new centered window with specified title and flags on a 800x480 resolution.
22 	this(string title = "D2DGame", uint flags = WindowFlags.Default) { this(800, 480, title, flags); }
23 
24 	/// Creates a new centered window with specified dimensions, title and flags.
25 	this(int width, int height, string title = "D2DGame", uint flags = WindowFlags.Default)
26 	{
27 		this(SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, title, flags);
28 	}
29 
30 	/// Creates a new window with specified parameters.
31 	this(int x, int y, int width, int height, string title, uint flags = WindowFlags.Default)
32 	{
33 		DerelictSDL2.load();
34 		DerelictSDL2Image.load();
35 		DerelictSDL2Mixer.load();
36 		DerelictSDL2ttf.load();
37 		DerelictGL3.load();
38 
39 		SDL_Init(SDL_INIT_EVERYTHING);
40 
41 		if (TTF_Init() == -1)
42 		{
43 			std.stdio.writeln("Error Initializing SDL_TTF: ", TTF_GetError());
44 		}
45 
46 		_handle = SDL_CreateWindow(title.toStringz(), x, y, width, height, flags | SDL_WINDOW_OPENGL);
47 		if (!valid)
48 			throw new Exception("Couldn't create window!");
49 		_id = SDL_GetWindowID(_handle);
50 
51 		SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
52 		SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 0);
53 
54 		SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
55 		SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
56 		SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
57 
58 		glContext = SDL_GL_CreateContext(_handle);
59 
60 		SDL_GL_SetSwapInterval(0);
61 
62 		DerelictGL3.reload();
63 
64 		if (SDL_GL_MakeCurrent(_handle, glContext) < 0)
65 			throw new Exception(cast(string) fromStringz(SDL_GetError()));
66 
67 		glEnable(GL_BLEND);
68 		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
69 
70 		ShaderProgram.load();
71 		Texture.load();
72 		if (!Music.load())
73 			throw new Exception(Music.error);
74 
75 		create(width, height);
76 
77 		_displayPlane = new RectangleShape();
78 		_displayPlane.size = vec2(1, 1);
79 		_displayPlane.create();
80 		_displayPlane.texture = _texture;
81 
82 		_postMatrix = mat4.orthographic(0, 1, 0, 1, -1, 1);
83 	}
84 
85 	~this()
86 	{
87 		if (valid)
88 			dispose();
89 	}
90 
91 	/// Polls a event from the stack and returns `true` if one was found.
92 	bool pollEvent(ref WindowEvent event)
93 	{
94 		SDL_Event evt;
95 		if (SDL_PollEvent(&evt))
96 		{
97 			event = WindowEvent();
98 			event.fromSDL(evt);
99 			return true;
100 		}
101 		return false;
102 	}
103 
104 	/// Pushes `WindowEvent.Quit` to the event stack.
105 	void quit()
106 	{
107 		SDL_Event sdlevent;
108 		sdlevent.type = SDL_QUIT;
109 		SDL_PushEvent(&sdlevent);
110 	}
111 
112 	void bind()
113 	{
114 		if (!_direct)
115 		{
116 			glBindFramebuffer(GL_FRAMEBUFFER, _fbo);
117 			glViewport(0, 0, _texture.width, _texture.height);
118 		}
119 	}
120 
121 	void resize(int width, int height)
122 	{
123 		glDeleteFramebuffers(1, &_fbo);
124 		_texture.dispose();
125 		create(width, height);
126 		_displayPlane.texture = _texture;
127 		projectionStack.set(mat4.orthographic(0, width, height, 0, -1, 1));
128 	}
129 
130 	void create(int width, int height)
131 	{
132 		glGenFramebuffers(1, &_fbo);
133 		glBindFramebuffer(GL_FRAMEBUFFER, _fbo);
134 
135 		_texture = new Texture();
136 		_texture.minFilter = TextureFilterMode.Nearest;
137 		_texture.magFilter = TextureFilterMode.Nearest;
138 		_texture.create(width, height, GL_RGB, null);
139 
140 		glGenRenderbuffers(1, &_drb);
141 		glBindRenderbuffer(GL_RENDERBUFFER, _drb);
142 		glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height);
143 		glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _drb);
144 
145 		glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, _texture.id, 0);
146 
147 		glDrawBuffers(1, [GL_COLOR_ATTACHMENT0].ptr);
148 		projectionStack.set(mat4.orthographic(0, width, height, 0, -1, 1));
149 		if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
150 			throw new Exception("Invalid Framebuffer");
151 	}
152 
153 	/// Texture containing rendered content.
154 	@property Texture texture()
155 	{
156 		return _texture;
157 	}
158 
159 	/// Displays rendered content to the window.
160 	void display(ShaderProgram post = null)
161 	{
162 		glBindFramebuffer(GL_FRAMEBUFFER, 0);
163 		int x, y;
164 		SDL_GetWindowSize(_handle, &x, &y);
165 		glViewport(0, 0, x, y);
166 		glClearColor(Color3.BlueViolet, 1);
167 		glClear(GL_COLOR_BUFFER_BIT);
168 		_direct = true;
169 		projectionStack.push();
170 		projectionStack.set(_postMatrix);
171 		matrixStack.push();
172 		matrixStack.set(mat4.identity);
173 		draw(_displayPlane, post);
174 		matrixStack.pop();
175 		projectionStack.pop();
176 		_direct = false;
177 		SDL_GL_SwapWindow(_handle);
178 	}
179 
180 	/// Dynamically sets the title of the window.
181 	@property void title(string title)
182 	{
183 		SDL_SetWindowTitle(_handle, title.toStringz());
184 	}
185 
186 	/// Dynamically gets the title of the window.
187 	@property string title()
188 	{
189 		string title = SDL_GetWindowTitle(_handle).fromStringz().dup;
190 		return title;
191 	}
192 
193 	/// Dynamically sets the width of the window.
194 	@property void width(int width)
195 	{
196 		SDL_SetWindowSize(_handle, width, height);
197 		resize(width, height);
198 	}
199 
200 	/// Dynamically gets the width of the window.
201 	@property int width()
202 	{
203 		int x, y;
204 		SDL_GetWindowSize(_handle, &x, &y);
205 		return x;
206 	}
207 
208 	/// Dynamically sets the height of the window.
209 	@property void height(int height)
210 	{
211 		SDL_SetWindowSize(_handle, width, height);
212 		resize(width, height);
213 	}
214 
215 	/// Dynamically gets the height of the window.
216 	@property int height()
217 	{
218 		int x, y;
219 		SDL_GetWindowSize(_handle, &x, &y);
220 		return y;
221 	}
222 
223 	/// Dynamically sets the maximum width of the window.
224 	@property void maxWidth(int maxWidth)
225 	{
226 		SDL_SetWindowMaximumSize(_handle, maxWidth, maxHeight);
227 	}
228 
229 	/// Dynamically gets the maximum width of the window.
230 	@property int maxWidth()
231 	{
232 		int x, y;
233 		SDL_GetWindowMaximumSize(_handle, &x, &y);
234 		return x;
235 	}
236 
237 	/// Dynamically sets the maximum height of the window.
238 	@property void maxHeight(int maxHeight)
239 	{
240 		SDL_SetWindowMaximumSize(_handle, maxWidth, maxHeight);
241 	}
242 
243 	/// Dynamically gets the maximum height of the window.
244 	@property int maxHeight()
245 	{
246 		int x, y;
247 		SDL_GetWindowMaximumSize(_handle, &x, &y);
248 		return y;
249 	}
250 
251 	/// Dynamically sets the minimum width of the window.
252 	@property void minWidth(int minWidth)
253 	{
254 		SDL_SetWindowMinimumSize(_handle, minWidth, minHeight);
255 	}
256 
257 	/// Dynamically gets the minimum width of the window.
258 	@property int minWidth()
259 	{
260 		int x, y;
261 		SDL_GetWindowMinimumSize(_handle, &x, &y);
262 		return x;
263 	}
264 
265 	/// Dynamically sets the minimum height of the window.
266 	@property void minHeight(int minHeight)
267 	{
268 		SDL_SetWindowMinimumSize(_handle, minWidth, minHeight);
269 	}
270 
271 	/// Dynamically gets the minimum height of the window.
272 	@property int minHeight()
273 	{
274 		int x, y;
275 		SDL_GetWindowMinimumSize(_handle, &x, &y);
276 		return y;
277 	}
278 
279 	/// Dynamically sets the x position of the window.
280 	@property void x(int x)
281 	{
282 		SDL_SetWindowPosition(_handle, x, y);
283 	}
284 
285 	/// Dynamically gets the x position of the window.
286 	@property int x()
287 	{
288 		int x, y;
289 		SDL_GetWindowPosition(_handle, &x, &y);
290 		return x;
291 	}
292 
293 	/// Dynamically sets the y position of the window.
294 	@property void y(int y)
295 	{
296 		SDL_SetWindowPosition(_handle, x, y);
297 	}
298 
299 	/// Dynamically gets the y position of the window.
300 	@property int y()
301 	{
302 		int x, y;
303 		SDL_GetWindowPosition(_handle, &x, &y);
304 		return y;
305 	}
306 
307 	/// Shows the window if hidden.
308 	void show()
309 	{
310 		SDL_ShowWindow(_handle);
311 	}
312 
313 	/// Hides the window.
314 	void hide()
315 	{
316 		SDL_HideWindow(_handle);
317 	}
318 
319 	/// Minimizes the window.
320 	void minimize()
321 	{
322 		SDL_MinimizeWindow(_handle);
323 	}
324 
325 	/// Maximizes the window.
326 	void maximize()
327 	{
328 		SDL_MaximizeWindow(_handle);
329 	}
330 
331 	/// Restores the window state from minimized or maximized.
332 	void restore()
333 	{
334 		SDL_RestoreWindow(_handle);
335 	}
336 
337 	/// Raises the window to top and focuses it for input.
338 	void focus()
339 	{
340 		SDL_RaiseWindow(_handle);
341 	}
342 
343 	/// Sets the icon to a Btimap.
344 	void setIcon(Bitmap icon)
345 	{
346 		SDL_SetWindowIcon(_handle, icon.surface);
347 	}
348 
349 	/// Closes the window and invalidates it.
350 	/// See_Also: Window.close
351 	void dispose()
352 	{
353 		glDeleteFramebuffers(1, &_fbo);
354 		_texture.dispose();
355 		if (valid)
356 		{
357 			SDL_DestroyWindow(_handle);
358 			_handle = null;
359 		}
360 	}
361 
362 	/// Closes the window and invalidates it.
363 	/// See_Also: Window.dispose
364 	void close()
365 	{
366 		dispose();
367 	}
368 
369 	/// Returns if the is still open.
370 	/// See_Also: Window.valid
371 	@property bool open()
372 	{
373 		return valid;
374 	}
375 
376 	/// Returns if the window is still open.
377 	/// See_Also: Window.open
378 	@property bool valid()
379 	{
380 		return _handle !is null;
381 	}
382 }
383 
384 ///
385 unittest
386 {
387 	Window window = new Window(800, 600, "Unittest");
388 
389 	assert(window.valid);
390 
391 	assert(window.title == "Unittest");
392 	window.title = "Window Title";
393 	assert(window.title == "Window Title");
394 	// Automatic conversion from c-strings to D-strings
395 
396 	assert(window.width == 800);
397 
398 	window.close();
399 	assert(!window.valid);
400 }