1 module gfm.sdl2.window; 2 3 import std..string; 4 5 import derelict.sdl2.sdl; 6 7 import std.experimental.logger; 8 9 import gfm.sdl2.sdl, 10 gfm.sdl2.surface, 11 gfm.sdl2.mouse, 12 gfm.sdl2.keyboard; 13 14 15 /// An interface for mouse events. 16 interface KeyboardListener 17 { 18 /// Called whenever a keyboard button is pressed. 19 void onKeyDown(uint timestamp, SDL2Keyboard keyboard, SDL_Keycode key); 20 21 /// Called whenever a keyboard button is released. 22 void onKeyUp(uint timestamp, SDL2Keyboard keyboard, SDL_Keycode key); 23 } 24 25 /// An interface for mouse events. 26 interface MouseListener 27 { 28 /// Called whenever the mouse moves. 29 void onMouseMove(uint timestamp, SDL2Mouse mouseState); 30 31 /// Called whenever a mouse button is pressed. 32 void onMouseButtonPressed(uint timestamp, SDL2Mouse mouseState, int button, bool isDoubleClick); 33 34 /// Called whenever a mouse button is released. 35 void onMouseButtonReleased(uint timestamp, SDL2Mouse mouseState, int button); 36 37 /// Called whenever the mouse wheel is scrolled. 38 void onMouseWheel(uint timestamp, SDL2Mouse mouseState, int wheelDeltaX, int wheelDeltaY); 39 } 40 41 /// SDL Window wrapper. 42 /// There is two ways to receive events, either by polling a SDL2 object, 43 /// or by overriding the event callbacks. 44 class SDL2Window : KeyboardListener, MouseListener 45 { 46 public 47 { 48 /// Creates a SDL window which targets a window. 49 /// See_also: $(LINK http://wiki.libsdl.org/SDL_CreateWindow) 50 /// Throws: $(D SDL2Exception) on error. 51 this(SDL2 sdl2, int x, int y, int width, int height, int flags) 52 { 53 _sdl2 = sdl2; 54 _logger = sdl2._logger; 55 _surface = null; 56 _glContext = null; 57 _surfaceMustBeRenewed = false; 58 59 bool OpenGL = (flags & SDL_WINDOW_OPENGL) != 0; 60 61 if (OpenGL) 62 { 63 // put here your desired context profile and version 64 65 //SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); 66 //SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); 67 //SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); 68 //SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG | SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); 69 70 // force debug OpenGL context creation in debug mode 71 debug 72 { 73 SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG); 74 } 75 } 76 77 _window = SDL_CreateWindow(toStringz(""), x, y, width, height, flags); 78 if (_window == null) 79 throw new SDL2Exception("SDL_CreateWindow failed: " ~ _sdl2.getErrorString()); 80 81 _id = SDL_GetWindowID(_window); 82 83 // register window for event dispatch 84 _sdl2.registerWindow(this); 85 86 if (OpenGL) 87 _glContext = new SDL2GLContext(this); 88 } 89 90 /// Releases the SDL resource. 91 /// See_also: $(LINK http://wiki.libsdl.org/SDL_DestroyWindow) 92 final void close() 93 { 94 if (_glContext !is null) 95 { 96 _glContext.close(); 97 _glContext = null; 98 } 99 100 if (_window !is null) 101 { 102 _sdl2.unregisterWindow(this); 103 SDL_DestroyWindow(_window); 104 _window = null; 105 } 106 } 107 108 /// 109 ~this() 110 { 111 close(); 112 } 113 114 /// See_also: $(LINK http://wiki.libsdl.org/SDL_SetWindowFullscreen) 115 /// Throws: $(D SDL2Exception) on error. 116 deprecated("Use setFullscreenSetting instead") final void setFullscreen(bool fullscreen) 117 { 118 SDL_SetWindowFullscreen(_window, fullscreen ? SDL_WINDOW_FULLSCREEN : 0); 119 } 120 121 /// See_also: $(LINK http://wiki.libsdl.org/SDL_SetWindowFullscreen) 122 /// Throws: $(D SDL2Exception) on error. 123 final void setFullscreenSetting(uint flags) 124 { 125 if (SDL_SetWindowFullscreen(_window, flags) != 0) 126 _sdl2.throwSDL2Exception("SDL_SetWindowFullscreen"); 127 } 128 129 /// Returns: X window coordinate. 130 /// See_also: $(LINK http://wiki.libsdl.org/SDL_GetWindowPosition) 131 final int getX() 132 { 133 return getPosition().x; 134 } 135 136 /// Returns: Y window coordinate. 137 /// See_also: $(LINK http://wiki.libsdl.org/SDL_GetWindowPosition) 138 final int getY() 139 { 140 return getPosition().y; 141 } 142 143 /// Returns: Window coordinates. 144 /// See_also: $(LINK http://wiki.libsdl.org/SDL_GetWindowPosition) 145 final SDL_Point getPosition() 146 { 147 int x, y; 148 SDL_GetWindowPosition(_window, &x, &y); 149 return SDL_Point(x, y); 150 } 151 152 /// See_also: $(LINK http://wiki.libsdl.org/SDL_SetWindowPosition) 153 final void setPosition(int positionX, int positionY) 154 { 155 SDL_SetWindowPosition(_window, positionX, positionY); 156 } 157 158 /// See_also: $(LINK http://wiki.libsdl.org/SDL_SetWindowSize) 159 final void setSize(int width, int height) 160 { 161 SDL_SetWindowSize(_window, width, height); 162 } 163 164 /// See_also: $(LINK http://wiki.libsdl.org/SDL_GetWindowSize) 165 /// Returns: Window size in pixels. 166 final SDL_Point getSize() 167 { 168 int w, h; 169 SDL_GetWindowSize(_window, &w, &h); 170 return SDL_Point(w, h); 171 } 172 173 /// See_also: $(LINK https://wiki.libsdl.org/SDL_SetWindowIcon) 174 final void setIcon(SDL2Surface icon) 175 { 176 SDL_SetWindowIcon( _window, icon.handle() ); 177 } 178 179 /// See_also: $(LINK http://wiki.libsdl.org/SDL_GetWindowSize) 180 /// Returns: Window width in pixels. 181 final int getWidth() 182 { 183 int w, h; 184 SDL_GetWindowSize(_window, &w, &h); 185 return w; 186 } 187 188 /// See_also: $(LINK http://wiki.libsdl.org/SDL_GetWindowSize) 189 /// Returns: Window height in pixels. 190 final int getHeight() 191 { 192 int w, h; 193 SDL_GetWindowSize(_window, &w, &h); 194 return h; 195 } 196 197 /// See_also: $(LINK http://wiki.libsdl.org/SDL_SetWindowTitle) 198 final void setTitle(string title) 199 { 200 SDL_SetWindowTitle(_window, toStringz(title)); 201 } 202 203 /// See_also: $(LINK http://wiki.libsdl.org/SDL_ShowWindow) 204 final void show() 205 { 206 SDL_ShowWindow(_window); 207 } 208 209 /// See_also: $(LINK http://wiki.libsdl.org/SDL_HideWindow) 210 final void hide() 211 { 212 SDL_HideWindow(_window); 213 } 214 215 /// See_also: $(LINK http://wiki.libsdl.org/SDL_MinimizeWindow) 216 final void minimize() 217 { 218 SDL_MinimizeWindow(_window); 219 } 220 221 /// See_also: $(LINK http://wiki.libsdl.org/SDL_MaximizeWindow) 222 final void maximize() 223 { 224 SDL_MaximizeWindow(_window); 225 } 226 227 /// Returns: Window surface. 228 /// See_also: $(LINK http://wiki.libsdl.org/SDL_GetWindowSurface) 229 /// Throws: $(D SDL2Exception) on error. 230 final SDL2Surface surface() 231 { 232 if (!hasValidSurface()) 233 { 234 SDL_Surface* internalSurface = SDL_GetWindowSurface(_window); 235 if (internalSurface is null) 236 _sdl2.throwSDL2Exception("SDL_GetWindowSurface"); 237 238 // renews surface as needed 239 _surfaceMustBeRenewed = false; 240 _surface = new SDL2Surface(_sdl2, internalSurface, SDL2Surface.Owned.NO); 241 } 242 return _surface; 243 } 244 245 /// Submit changes to the window surface. 246 /// See_also: $(LINK http://wiki.libsdl.org/SDL_UpdateWindowSurface) 247 /// Throws: $(D SDL2Exception) on error. 248 final void updateSurface() 249 { 250 if (!hasValidSurface()) 251 surface(); 252 253 int res = SDL_UpdateWindowSurface(_window); 254 if (res != 0) 255 _sdl2.throwSDL2Exception("SDL_UpdateWindowSurface"); 256 257 } 258 259 /// Returns: Window ID. 260 /// See_also: $(LINK http://wiki.libsdl.org/SDL_GetWindowID) 261 final int id() 262 { 263 return _id; 264 } 265 266 /// Returns: System-specific window information, useful to use a third-party rendering library. 267 /// See_also: $(LINK http://wiki.libsdl.org/SDL_GetWindowWMInfo) 268 /// Throws: $(D SDL2Exception) on error. 269 SDL_SysWMinfo getWindowInfo() 270 { 271 SDL_SysWMinfo info; 272 SDL_VERSION(&info.version_); 273 int res = SDL_GetWindowWMInfo(_window, &info); 274 if (res != SDL_TRUE) 275 _sdl2.throwSDL2Exception("SDL_GetWindowWMInfo"); 276 return info; 277 } 278 279 // override these function, they are event callbacks 280 281 /// 282 void onShow() 283 { 284 } 285 286 /// 287 void onHide() 288 { 289 } 290 291 /// 292 void onExposed() 293 { 294 _surfaceMustBeRenewed = true; 295 } 296 297 /// 298 void onMove(int x, int y) 299 { 300 } 301 302 /// 303 void onResized(int width, int height) 304 { 305 _surfaceMustBeRenewed = true; 306 } 307 308 /// 309 void onSizeChanged() 310 { 311 _surfaceMustBeRenewed = true; 312 } 313 314 /// 315 void onMinimized() 316 { 317 _surfaceMustBeRenewed = true; 318 } 319 320 /// 321 void onMaximized() 322 { 323 _surfaceMustBeRenewed = true; 324 } 325 326 /// 327 void onRestored() 328 { 329 } 330 331 /// 332 void onEnter() 333 { 334 } 335 336 /// 337 void onLeave() 338 { 339 } 340 341 /// 342 void onFocusGained() 343 { 344 } 345 346 /// 347 void onFocusLost() 348 { 349 } 350 351 /// 352 void onClose() 353 { 354 } 355 356 // Mouse event callbacks 357 358 override void onMouseMove(uint timestamp, SDL2Mouse mouseState) 359 { 360 // do nothing by default 361 } 362 363 override void onMouseButtonPressed(uint timestamp, SDL2Mouse mouseState, int button, bool isDoubleClick) 364 { 365 // do nothing by default 366 } 367 368 override void onMouseButtonReleased(uint timestamp, SDL2Mouse mouseState, int button) 369 { 370 // do nothing by default 371 } 372 373 override void onMouseWheel(uint timestamp, SDL2Mouse mouseState, int wheelDeltaX, int wheelDeltaY) 374 { 375 // do nothing by default 376 } 377 378 // Keybard event callbacks 379 380 override void onKeyDown(uint timestamp, SDL2Keyboard keyboard, SDL_Keycode key) 381 { 382 // do nothing by default 383 } 384 385 override void onKeyUp(uint timestamp, SDL2Keyboard keyboard, SDL_Keycode key) 386 { 387 // do nothing by default 388 } 389 390 /// Swap OpenGL buffers. 391 /// See_also: $(LINK http://wiki.libsdl.org/SDL_GL_SwapWindow) 392 /// Throws: $(D SDL2Exception) on error. 393 void swapBuffers() 394 { 395 if (_glContext is null) 396 throw new SDL2Exception("swapBuffers failed: not an OpenGL window"); 397 SDL_GL_SwapWindow(_window); 398 } 399 } 400 401 package 402 { 403 SDL2 _sdl2; 404 SDL_Window* _window; 405 } 406 407 private 408 { 409 Logger _logger; 410 SDL2Surface _surface; 411 SDL2GLContext _glContext; 412 uint _id; 413 414 bool _surfaceMustBeRenewed; 415 416 bool hasValidSurface() 417 { 418 return (!_surfaceMustBeRenewed) && (_surface !is null); 419 } 420 } 421 } 422 423 /// SDL OpenGL context wrapper. You probably don't need to use it directly. 424 final class SDL2GLContext 425 { 426 public 427 { 428 /// Creates for a given SDL window. 429 this(SDL2Window window) 430 { 431 _window = window; 432 _context = SDL_GL_CreateContext(window._window); 433 _initialized = true; 434 } 435 436 ~this() 437 { 438 close(); 439 } 440 441 /// Release the associated SDL ressource. 442 void close() 443 { 444 if (_initialized) 445 { 446 // work-around Issue #19 447 // SDL complains with log message "wglMakeCurrent(): The handle is invalid." 448 // in the SDL_DestroyWindow() call if we destroy the OpenGL context before-hand 449 // 450 // SDL_GL_DeleteContext(_context); 451 _initialized = false; 452 } 453 } 454 455 /// Makes this OpenGL context current. 456 /// Throws: $(D SDL2Exception) on error. 457 void makeCurrent() 458 { 459 if (0 != SDL_GL_MakeCurrent(_window._window, _context)) 460 _window._sdl2.throwSDL2Exception("SDL_GL_MakeCurrent"); 461 } 462 } 463 464 package 465 { 466 SDL_GLContext _context; 467 SDL2Window _window; 468 } 469 470 private 471 { 472 bool _initialized; 473 } 474 } 475