1 /* 2 Copyright (c) 2017-2018 Timur Gafarov 3 4 Boost Software License - Version 1.0 - August 17th, 2003 5 Permission is hereby granted, free of charge, to any person or organization 6 obtaining a copy of the software and accompanying documentation covered by 7 this license (the "Software") to use, reproduce, display, distribute, 8 execute, and transmit the Software, and to prepare derivative works of the 9 Software, and to permit third-parties to whom the Software is furnished to 10 do so, all subject to the following: 11 12 The copyright notices in the Software and this entire statement, including 13 the above license grant, this restriction and the following disclaimer, 14 must be included in all copies of the Software, in whole or in part, and 15 all derivative works of the Software, unless such copies or derivative 16 works are solely in the form of machine-executable object code generated by 17 a source language processor. 18 19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 22 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 23 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 24 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 DEALINGS IN THE SOFTWARE. 26 */ 27 28 module dagon.core.application; 29 30 import std.stdio; 31 import std.conv; 32 import std.getopt; 33 import std.string; 34 import std.file; 35 import core.stdc.stdlib; 36 37 import derelict.util.exception; 38 import derelict.sdl2.sdl; 39 import derelict.opengl; 40 import derelict.freetype.ft; 41 42 import dagon.core.event; 43 44 void exitWithError(string message) 45 { 46 writeln(message); 47 core.stdc.stdlib.exit(1); 48 } 49 50 enum DagonEvent 51 { 52 Exit = -1 53 } 54 55 ShouldThrow ftOnMissingSymbol(string symbolName) 56 { 57 writefln("Warning: failed to load Freetype function \"%s\"", symbolName); 58 return ShouldThrow.No; 59 } 60 61 ShouldThrow sdlOnMissingSymbol(string symbolName) 62 { 63 writefln("Warning: failed to load SDL2 function \"%s\"", symbolName); 64 return ShouldThrow.No; 65 } 66 67 class Application: EventListener 68 { 69 uint width; 70 uint height; 71 SDL_Window* window = null; 72 SDL_GLContext glcontext; 73 string libdir; 74 75 this(uint w, uint h, bool fullscreen, string windowTitle, string[] args) 76 { 77 try 78 { 79 getopt( 80 args, 81 "libdir", &libdir, 82 ); 83 } 84 catch(Exception) 85 { 86 } 87 88 DerelictSDL2.missingSymbolCallback = &sdlOnMissingSymbol; 89 DerelictFT.missingSymbolCallback = &ftOnMissingSymbol; 90 91 DerelictGL3.load(); 92 if (libdir.length) 93 { 94 version(linux) 95 { 96 DerelictSDL2.load(format("%s/libSDL2-2.0.so", libdir)); 97 DerelictFT.load(format("%s/libfreetype.so", libdir)); 98 } 99 version(Windows) 100 { 101 version(X86) 102 { 103 DerelictSDL2.load(format("%s/SDL2.dll", libdir)); 104 DerelictFT.load(format("%s/freetype281.dll", libdir)); 105 } 106 version(X86_64) 107 { 108 DerelictSDL2.load(format("%s/SDL2.dll", libdir)); 109 DerelictFT.load(format("%s/freetype281.dll", libdir)); 110 } 111 } 112 } 113 else 114 { 115 version(linux) 116 { 117 version(X86) 118 { 119 if (exists("lib/x86/libSDL2-2.0.so")) 120 DerelictSDL2.load("lib/x86/libSDL2-2.0.so"); 121 else 122 DerelictSDL2.load(); 123 124 if (exists("lib/x86/libfreetype.so")) 125 DerelictFT.load("lib/x86/libfreetype.so"); 126 else 127 DerelictFT.load(); 128 } 129 version(X86_64) 130 { 131 if (exists("lib/x64/libSDL2-2.0.so")) 132 DerelictSDL2.load("lib/x64/libSDL2-2.0.so"); 133 else 134 DerelictSDL2.load(); 135 136 if (exists("lib/x64/libfreetype.so")) 137 DerelictFT.load("lib/x64/libfreetype.so"); 138 else 139 DerelictFT.load(); 140 } 141 } 142 version(Windows) 143 { 144 version(X86) 145 { 146 if (exists("lib/x86/SDL2.dll")) 147 DerelictSDL2.load("lib/x86/SDL2.dll"); 148 else 149 DerelictSDL2.load(); 150 151 if (exists("lib/x86/freetype281.dll")) 152 DerelictFT.load("lib/x86/freetype281.dll"); 153 else 154 DerelictFT.load(); 155 } 156 version(X86_64) 157 { 158 if (exists("lib/x64/SDL2.dll")) 159 DerelictSDL2.load("lib/x64/SDL2.dll"); 160 else 161 DerelictSDL2.load(); 162 163 if (exists("lib/x64/freetype281.dll")) 164 DerelictFT.load("lib/x64/freetype281.dll"); 165 else 166 DerelictFT.load(); 167 } 168 } 169 } 170 171 version(FreeBSD) 172 { 173 DerelictSDL2.load(); 174 DerelictFT.load(); 175 } 176 177 version(OSX) 178 { 179 DerelictSDL2.load(); 180 DerelictFT.load(); 181 } 182 183 if (SDL_Init(SDL_INIT_EVERYTHING) == -1) 184 exitWithError("Failed to init SDL: " ~ to!string(SDL_GetError())); 185 186 width = w; 187 height = h; 188 189 SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1); 190 191 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); 192 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); 193 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); 194 SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); 195 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); 196 SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); 197 SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); 198 199 window = SDL_CreateWindow(toStringz(windowTitle), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL); 200 if (window is null) 201 exitWithError("Failed to create window: " ~ to!string(SDL_GetError())); 202 203 SDL_GL_SetSwapInterval(1); 204 205 glcontext = SDL_GL_CreateContext(window); 206 if (glcontext is null) 207 exitWithError("Failed to create GL context: " ~ to!string(SDL_GetError())); 208 209 SDL_GL_MakeCurrent(window, glcontext); 210 211 GLVersion loadedVersion = DerelictGL3.reload(); 212 writeln("OpenGL version loaded: ", loadedVersion); 213 if (loadedVersion < GLVersion.gl40) 214 { 215 exitWithError("Sorry, Dagon requires OpenGL 4.0!"); 216 } 217 218 if (fullscreen) 219 SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN); 220 221 EventManager eventManager = new EventManager(window, width, height); 222 super(eventManager, null); 223 224 // Initialize OpenGL 225 glClearColor(0.0f, 0.0f, 0.0f, 1.0f); 226 glClearDepth(1.0); 227 glDepthFunc(GL_LESS); 228 glEnable(GL_DEPTH_TEST); 229 glEnable(GL_POLYGON_OFFSET_FILL); 230 231 checkGLError(); 232 } 233 234 override void onUserEvent(int code) 235 { 236 if (code == DagonEvent.Exit) 237 { 238 exit(); 239 } 240 } 241 242 void onUpdate(double dt) 243 { 244 // Override me 245 } 246 247 void onRender() 248 { 249 // Override me 250 } 251 252 void checkGLError() 253 { 254 GLenum error = GL_NO_ERROR; 255 error = glGetError(); 256 if (error != GL_NO_ERROR) 257 { 258 writeln("OpenGL error: ", error); 259 eventManager.running = false; 260 } 261 } 262 263 void run() 264 { 265 while(eventManager.running) 266 { 267 beginRender(); 268 onUpdate(eventManager.deltaTime); 269 onRender(); 270 endRender(); 271 } 272 } 273 274 void beginRender() 275 { 276 eventManager.update(); 277 processEvents(); 278 } 279 280 void endRender() 281 { 282 debug checkGLError(); 283 SDL_GL_SwapWindow(window); 284 } 285 286 void exit() 287 { 288 eventManager.running = false; 289 } 290 291 ~this() 292 { 293 SDL_GL_DeleteContext(glcontext); 294 SDL_DestroyWindow(window); 295 SDL_Quit(); 296 } 297 }