1 module gfm.sdl2.window; 2 3 import std.string; 4 5 import derelict.sdl2.sdl; 6 7 import std.logger; 8 9 import gfm.math.vector, 10 gfm.math.box, 11 gfm.sdl2.sdl, 12 gfm.sdl2.surface; 13 14 /// SDL Window wrapper. 15 /// There is two ways to receive events, either by polling a SDL2 object, 16 /// or by overriding the event callbacks. 17 class SDL2Window 18 { 19 public 20 { 21 /// Initially invisible. 22 /// Accepts the same constants as the SDL2 function. 23 this(SDL2 sdl2, int x, int y, int width, int height, int flags) 24 { 25 _sdl2 = sdl2; 26 _logger = sdl2._logger; 27 _surface = null; 28 _glContext = null; 29 _surfaceMustBeRenewed = false; 30 31 bool OpenGL = (flags & SDL_WINDOW_OPENGL) != 0; 32 33 if (OpenGL) 34 { 35 // put here your desired context profile and version 36 37 //SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); 38 //SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); 39 //SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); 40 //SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG | SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); 41 42 // force debug OpenGL context creation in debug mode 43 debug 44 { 45 SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG); 46 } 47 } 48 49 _window = SDL_CreateWindow(toStringz(""), x, y, width, height, flags); 50 if (_window == null) 51 throw new SDL2Exception("SDL_CreateWindow failed: " ~ _sdl2.getErrorString()); 52 53 _id = SDL_GetWindowID(_window); 54 55 // register window for event dispatch 56 _sdl2.registerWindow(this); 57 58 if (OpenGL) 59 _glContext = new SDL2GLContext(this); 60 } 61 62 /// Releases the SDL resource. 63 final void close() 64 { 65 if (_glContext !is null) 66 { 67 _glContext.close(); 68 _glContext = null; 69 } 70 71 if (_window !is null) 72 { 73 _sdl2.unregisterWindow(this); 74 SDL_DestroyWindow(_window); 75 _window = null; 76 } 77 } 78 79 ~this() 80 { 81 close(); 82 } 83 84 final void setFullscreen(bool activated) 85 { 86 SDL_SetWindowFullscreen(_window, activated ? SDL_WINDOW_FULLSCREEN : 0); 87 } 88 89 final void setPosition(vec2i position) 90 { 91 SDL_SetWindowPosition(_window, position.x, position.y); 92 } 93 94 final void setSize(vec2i size) 95 { 96 SDL_SetWindowSize(_window, size.x, size.y); 97 } 98 99 final vec2i getSize() 100 { 101 int w, h; 102 SDL_GetWindowSize(_window, &w, &h); 103 return vec2i(w, h); 104 } 105 106 final void setTitle(string title) 107 { 108 SDL_SetWindowTitle(_window, toStringz(title)); 109 } 110 111 final void show() 112 { 113 SDL_ShowWindow(_window); 114 } 115 116 final void hide() 117 { 118 SDL_HideWindow(_window); 119 } 120 121 final void minimize() 122 { 123 SDL_MinimizeWindow(_window); 124 } 125 126 final void maximize() 127 { 128 SDL_MaximizeWindow(_window); 129 } 130 131 final SDL2Surface surface() 132 { 133 if (!hasValidSurface()) 134 { 135 SDL_Surface* internalSurface = SDL_GetWindowSurface(_window); 136 if (internalSurface is null) 137 _sdl2.throwSDL2Exception("SDL_GetWindowSurface"); 138 139 // renews surface as needed 140 _surfaceMustBeRenewed = false; 141 _surface = new SDL2Surface(_sdl2, internalSurface, SDL2Surface.Owned.NO); 142 } 143 return _surface; 144 } 145 146 final void updateSurface() 147 { 148 if (!hasValidSurface()) 149 surface(); 150 151 int res = SDL_UpdateWindowSurface(_window); 152 if (res != 0) 153 _sdl2.throwSDL2Exception("SDL_UpdateWindowSurface"); 154 155 } 156 157 final int id() 158 { 159 return _id; 160 } 161 162 SDL_SysWMinfo getWindowInfo() 163 { 164 SDL_SysWMinfo info; 165 int res = SDL_GetWindowWMInfo(_window, &info); 166 if (res != SDL_TRUE) 167 _sdl2.throwSDL2Exception("SDL_GetWindowWMInfo"); 168 return info; 169 } 170 171 // override these function, they are event callbacks 172 173 void onShow() 174 { 175 } 176 177 void onHide() 178 { 179 } 180 181 void onExposed() 182 { 183 _surfaceMustBeRenewed = true; 184 } 185 186 void onMove(int x, int y) 187 { 188 } 189 190 void onResized(int width, int height) 191 { 192 _surfaceMustBeRenewed = true; 193 } 194 195 void onSizeChanged() 196 { 197 _surfaceMustBeRenewed = true; 198 } 199 200 void onMinimized() 201 { 202 _surfaceMustBeRenewed = true; 203 } 204 205 void onMaximized() 206 { 207 _surfaceMustBeRenewed = true; 208 } 209 210 void onRestored() 211 { 212 } 213 214 void onEnter() 215 { 216 } 217 218 void onLeave() 219 { 220 } 221 222 void onFocusGained() 223 { 224 } 225 226 void onFocusLost() 227 { 228 } 229 230 void onClose() 231 { 232 } 233 234 void swapBuffers() 235 { 236 if (_glContext is null) 237 throw new SDL2Exception("swapBuffers failed: not an OpenGL window"); 238 SDL_GL_SwapWindow(_window); 239 } 240 } 241 242 package 243 { 244 SDL2 _sdl2; 245 SDL_Window* _window; 246 } 247 248 private 249 { 250 Logger _logger; 251 SDL2Surface _surface; 252 SDL2GLContext _glContext; 253 uint _id; 254 255 bool _surfaceMustBeRenewed; 256 257 bool hasValidSurface() 258 { 259 return (!_surfaceMustBeRenewed) && (_surface !is null); 260 } 261 } 262 } 263 264 /// SDL OpenGL context wrapper. 265 final class SDL2GLContext 266 { 267 public 268 { 269 this(SDL2Window window) 270 { 271 _window = window; 272 _context = SDL_GL_CreateContext(window._window); 273 _initialized = true; 274 } 275 276 ~this() 277 { 278 close(); 279 } 280 281 void close() 282 { 283 if (_initialized) 284 { 285 // work-around Issue #19 286 // SDL complains with log message "wglMakeCurrent(): The handle is invalid." 287 // in the SDL_DestroyWindow() call if we destroy the OpenGL context before-hand 288 // 289 // SDL_GL_DeleteContext(_context); 290 _initialized = false; 291 } 292 } 293 294 void makeCurrent() 295 { 296 if (0 != SDL_GL_MakeCurrent(_window._window, _context)) 297 _window._sdl2.throwSDL2Exception("SDL_GL_MakeCurrent"); 298 } 299 } 300 301 package 302 { 303 SDL_GLContext _context; 304 SDL2Window _window; 305 } 306 307 private 308 { 309 bool _initialized; 310 } 311 } 312