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