1 module window;
2 
3 /**
4     Contains various helpers, common code, and initialization routines.
5 */
6 
7 import std.algorithm : min;
8 import std.exception : enforce;
9 import std.functional : toDelegate;
10 import std.stdio : stderr;
11 import std.string : format;
12 
13 import deimos.glfw.glfw3;
14 
15 import glad.gl.enums;
16 import glad.gl.ext;
17 import glad.gl.funcs;
18 import glad.gl.loader;
19 import glad.gl.types;
20 
21 import glwtf.input;
22 import glwtf.window;
23 
24 /// init
25 shared static this()
26 {
27     enforce(glfwInit());
28 }
29 
30 /// uninit
31 shared static ~this()
32 {
33     glfwTerminate();
34 }
35 
36 ///
37 enum WindowMode
38 {
39     fullscreen,
40     windowed,
41 }
42 
43 /**
44     Create a window, an OpenGL 3.x context, and set up some other
45     common routines for error handling, window resizing, etc.
46 */
47 Window createWindow(string windowName, WindowMode windowMode = WindowMode.windowed, int width = 1024, int height = 768)
48 {
49     auto vidMode = glfwGetVideoMode(glfwGetPrimaryMonitor());
50 
51     // constrain the window size so it isn't larger than the desktop size.
52     width = min(width, vidMode.width);
53     height = min(height, vidMode.height);
54 
55     // set the window to be initially inivisible since we're repositioning it.
56     glfwWindowHint(GLFW_VISIBLE, 0);
57 
58     // enable debugging
59     glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, 1);
60 
61     Window window = createWindowContext(windowName, WindowMode.windowed, width, height);
62 
63     // center the window on the screen
64     glfwSetWindowPos(window.window, (vidMode.width - width) / 2, (vidMode.height - height) / 2);
65 
66     // glfw-specific error routine (not a generic GL error handler)
67     register_glfw_error_callback(&glfwErrorCallback);
68 
69     // anti-aliasing number of samples.
70     window.samples = 4;
71 
72     // activate an opengl context.
73     window.make_context_current();
74 
75     // load all OpenGL function pointers via glad.
76     enforce(gladLoadGL());
77 
78     enforce(glGenBuffers !is null);
79 
80     // only interested in GL 3.x
81     enforce(GLVersion.major >= 3);
82 
83     // turn v-sync off.
84     glfwSwapInterval(0);
85 
86     version (OSX)
87     {
88         // GL_ARM_debug_output and GL_KHR_debug are not supported under OS X 10.9.3
89     }
90     else
91     {
92         // ensure the debug output extension is supported
93         enforce(GL_ARB_debug_output || GL_KHR_debug);
94 
95         // cast: workaround for 'nothrow' propagation bug (haven't been able to reduce it)
96         auto hookDebugCallback = GL_ARB_debug_output ? glDebugMessageCallbackARB
97                                                      : cast(typeof(glDebugMessageCallbackARB))glDebugMessageCallback;
98 
99 
100         // hook the debug callback
101         // cast: when using derelict it assumes its nothrow
102         hookDebugCallback(cast(GLDEBUGPROCARB)&glErrorCallback, null);
103 
104         // enable proper stack tracing support (otherwise we'd get random failures at runtime)
105         glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB);
106     }
107 
108     // finally show the window
109     glfwShowWindow(window.window);
110 
111     return window;
112 }
113 
114 /** Create a window and an OpenGL context. */
115 Window createWindowContext(string windowName, WindowMode windowMode, int width, int height)
116 {
117     auto window = new Window();
118     auto monitor = windowMode == WindowMode.fullscreen ? glfwGetPrimaryMonitor() : null;
119     auto context = window.create_highest_available_context(width, height, windowName, monitor, null, GLFW_OPENGL_CORE_PROFILE);
120 
121     // ensure we've loaded a proper context
122     enforce(context.major >= 3);
123 
124     return window;
125 }
126 
127 /** Just emit errors to stderr on GLFW errors. */
128 void glfwErrorCallback(int code, string msg)
129 {
130     stderr.writefln("Error (%s): %s", code, msg);
131 }
132 
133 ///
134 class GLException : Exception
135 {
136     @safe pure nothrow this(string msg = "", string file = __FILE__, size_t line = __LINE__, Throwable next = null)
137     {
138         super(msg, file, line, next);
139     }
140 
141     @safe pure nothrow this(string msg, Throwable next, string file = __FILE__, size_t line = __LINE__)
142     {
143         super(msg, file, line, next);
144     }
145 }
146 
147 /**
148     GL_ARB_debug_output or GL_KHR_debug callback.
149 
150     Throwing exceptions across language boundaries is ok as
151     long as $(B GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB) is enabled.
152 */
153 extern (Windows)
154 private void glErrorCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, in GLchar* message, GLvoid* userParam)
155 {
156     string msg = format("source: %s, type: %s, id: %s, severity: %s, length: %s, message: %s, userParam: %s",
157                          source, type, id, severity, length, message.to!string, userParam);
158     throw new GLException(msg);
159 }