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 }