1 // Copyright Ferdinand Majerech 2014. 2 // Distributed under the Boost Software License, Version 1.0. 3 // (See accompanying file LICENSE_1_0.txt or copy at 4 // http://www.boost.org/LICENSE_1_0.txt) 5 6 /// Manages windowing and GL and provides access to convenience OpenGL wrappers. 7 module platform.videodevice; 8 9 10 import std.algorithm; 11 import std.exception; 12 import std.experimental.logger; 13 import std.typecons; 14 15 import derelict.opengl3.gl3; 16 import derelict.sdl2.sdl; 17 18 19 import gfmod.opengl.opengl; 20 21 22 /// Manages windowing and GL and provides access to convenience OpenGL wrappers. 23 class VideoDevice 24 { 25 private: 26 // The game log. 27 Logger log_; 28 29 // OpenGL wrapper managing GL versions and information. 30 31 OpenGL gl_; 32 33 // The main game window. 34 SDL_Window* window_; 35 36 // Zero-terminated title of the main game window. 37 char[1024] windowTitle_; 38 39 // Window width. 40 size_t width_; 41 42 // Window height. 43 size_t height_; 44 45 // OpenGL context. 46 SDL_GLContext context_; 47 48 public: 49 /** 50 * Construct a VideoDevice with specified OpenGL drawing to window. 51 * 52 * Note that to fully construct a VideoDevice you also need to call initWindow() 53 * and initGL(). 54 * 55 * Params: 56 * 57 * log = The game log. 58 */ 59 this(Logger log) @safe pure nothrow @nogc 60 { 61 windowTitle_[] = 0; 62 log_ = log; 63 } 64 65 /// Destroy the VideoDevice. Must be called by the user (use e.g. scoped!). 66 ~this() 67 { 68 if(gl_ !is null) { destroy(gl_); } 69 if(context_ != SDL_GLContext.init) { SDL_GL_DeleteContext(context_); } 70 if(window_ !is null) { SDL_DestroyWindow(window_); } 71 } 72 73 import platform.sdl2; 74 /** 75 * Initialize the GL window. 76 * 77 * Must be called before initGL. 78 */ 79 bool initWindow(size_t width, size_t height, Flag!"fullscreen" fullscreen) 80 @trusted nothrow 81 { 82 assert(window_ is null, "Double initialization of the main window"); 83 window_ = createGLWindow(width, height, fullscreen); 84 width_ = width; 85 height_ = height; 86 87 // Exit if window creation fails. 88 if(null is window_) 89 { 90 log_.critical("Failed to create the application window").assumeWontThrow; 91 return false; 92 } 93 log_.infof("Created a%s window with dimensions %s x %s", 94 fullscreen ? " fullscreen" : "", width, height).assumeWontThrow; 95 return true; 96 } 97 98 /** Resize the viewport. 99 * 100 * Should be called after the window is resized. 101 * 102 * See_Also: InputDevice.resized 103 */ 104 void resizeViewport(int width, int height) @system nothrow @nogc 105 { 106 width_ = width; 107 height_ = height; 108 glViewport(0, 0, width, height); 109 } 110 111 /** 112 * Initialize the OpenGL context. 113 * 114 * Returns: true on success, false on failure. 115 */ 116 bool initGL() @trusted nothrow 117 { 118 assert(window_ !is null, "Can't initialize GL without an initialized window"); 119 assert(gl_ is null, "Double initialization of GL"); 120 121 SDL_GLContext context; 122 try 123 { 124 gl_ = new OpenGL(log_); 125 context_ = SDL_GL_CreateContext(window_); 126 if(gl_.reload() < GLVersion.GL30) 127 { 128 log_.critical("Required OpenGL version 3.0 could not be loaded."); 129 return false; 130 } 131 } 132 catch(OpenGLException e) 133 { 134 log_.critical("Failed to initialize OpenGL: ", e).assumeWontThrow; 135 return false; 136 } 137 catch(Exception e) 138 { 139 assert(false, "Unexpected exception in initGL: " ~ e.msg); 140 } 141 return true; 142 } 143 144 /// Get window height. 145 /// 146 /// Signed because screen size is often multiplied by negative numbers. 147 long height() @safe pure nothrow const @nogc 148 { 149 return height_; 150 } 151 152 /// Get window width. 153 /// 154 /// Signed because screen size is often multiplied by negative numbers. 155 long width() @safe pure nothrow const @nogc 156 { 157 return width_; 158 } 159 160 /// Get access to the OpenGL API. 161 OpenGL gl() @safe pure nothrow @nogc 162 { 163 assert(gl_ !is null, "Trying to access GL before it is initialized"); 164 return gl_; 165 } 166 167 /// Set the main window title. Any characters past 1023 will be truncated. 168 void windowTitle(string rhs) @trusted nothrow @nogc 169 { 170 // Zero-terminating in a fixed-size buffer. 171 const titleLength = min(rhs.length, windowTitle_.length - 1); 172 windowTitle_[0 .. titleLength] = rhs[0 .. titleLength]; 173 windowTitle_[titleLength] = 0; 174 SDL_SetWindowTitle(window_, windowTitle_.ptr); 175 } 176 177 /// Swap the front and back buffer at the end of a frame. 178 void swapBuffers() @trusted nothrow 179 { 180 assert(window_ !is null, "Can't swap buffers without an initialized window"); 181 SDL_GL_SwapWindow(window_); 182 } 183 }