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