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 }