1 /**
2 Copyright: Copyright (c) 2013-2016 Andrey Penechko.
3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
4 Authors: Andrey Penechko.
5 */
6 
7 module anchovy.glfwwindow;
8 
9 import std.conv : to;
10 import std.string : toStringz, fromStringz, format;
11 
12 import derelict.glfw3.glfw3;
13 import derelict.opengl3.gl3;
14 import dlib.math.vector;
15 import anchovy.iwindow : IWindow;
16 
17 class GlfwWindow : IWindow
18 {
19 private:
20 	GLFWwindow*	glfwWindowPtr;
21 	static bool	glfwInited = false;
22 	bool isProcessingEvents = false;
23 
24 public:
25 	override void init(uvec2 size, in string caption)
26 	{
27 		if (!glfwInited)
28 			initGlfw();
29 
30 		scope(failure) glfwTerminate();
31 
32 		//BUG: sometimes fails in Windows 8. Maybe because of old drivers.
33 		glfwWindowPtr = glfwCreateWindow(size.x, size.y, toStringz(caption), null,  null);
34 
35 		if (glfwWindowPtr is null)
36 		{
37 			throw new Error("Error creating GLFW3 window");
38 		}
39 
40 		glfwMakeContextCurrent(glfwWindowPtr);
41 		glfwSwapInterval(0);
42 
43 		glClearColor(1.0, 1.0, 1.0, 1.0);
44 		glViewport(0, 0, size.x, size.y);
45 
46 		DerelictGL3.reload();
47 
48 		glfwSetWindowUserPointer(glfwWindowPtr, cast(void*)this);
49 		glfwSetWindowPosCallback(glfwWindowPtr, &windowposfun);
50 		glfwSetFramebufferSizeCallback(glfwWindowPtr, &windowsizefun);
51 		glfwSetWindowCloseCallback(glfwWindowPtr, &windowclosefun);
52 		//glfwSetWindowRefreshCallback(glfwWindowPtr, &windowrefreshfun);
53 		glfwSetWindowFocusCallback(glfwWindowPtr, &windowfocusfun);
54 		glfwSetWindowIconifyCallback(glfwWindowPtr, &windowiconifyfun);
55 		glfwSetMouseButtonCallback(glfwWindowPtr, &mousebuttonfun);
56 		glfwSetCursorPosCallback(glfwWindowPtr, &cursorposfun);
57 		glfwSetScrollCallback(glfwWindowPtr, &scrollfun);
58 		glfwSetKeyCallback(glfwWindowPtr, &keyfun);
59 		glfwSetCharCallback(glfwWindowPtr, &charfun);
60 		//glfwSetCursorEnterCallback(window, GLFWcursorenterfun cbfun);
61 		glfwSetWindowRefreshCallback(glfwWindowPtr, &refreshfun);
62 	}
63 
64 	override void processEvents()
65 	{
66 		// prevent calling glfwPollEvents when inside window_refresh callback
67 		if (!isProcessingEvents)
68 		{
69 			isProcessingEvents = true;
70 			glfwPollEvents();
71 			isProcessingEvents = false;
72 		}
73 	}
74 
75 	override double elapsedTime() @property //in seconds
76 	{
77 		return glfwGetTime();
78 	}
79 
80 	override void reshape(uvec2 viewportSize)
81 	{
82 		glViewport(0, 0, cast(int)viewportSize.x, cast(int)viewportSize.y);
83 	}
84 
85 	override void releaseWindow()
86 	{
87 		glfwDestroyWindow(glfwWindowPtr);
88 		glfwTerminate();
89 	}
90 
91 	override void mousePosition(ivec2 newPosition) @property
92 	{
93 		glfwSetCursorPos(glfwWindowPtr, newPosition.x, newPosition.y);
94 	}
95 
96 	override ivec2 mousePosition() @property
97 	{
98 		double x, y;
99 		glfwGetCursorPos(glfwWindowPtr, &x, &y);
100 		return ivec2(cast(int)x, cast(int)y);
101 	}
102 
103 	override void swapBuffers()
104 	{
105 		glfwSwapBuffers(glfwWindowPtr);
106 	}
107 
108 	override void size(uvec2 newSize) @property
109 	{
110 		glfwSetWindowSize(glfwWindowPtr, cast(int)newSize.x, cast(int)newSize.y);
111 	}
112 
113 	override uvec2 size() @property
114 	{
115 		int width, height;
116 		glfwGetWindowSize(glfwWindowPtr, &width, &height);
117 		return uvec2(cast(uint)width, cast(uint)height);
118 	}
119 
120 	override uvec2 framebufferSize() @property
121 	{
122 		int width, height;
123 		glfwGetFramebufferSize(glfwWindowPtr, &width, &height);
124 		return uvec2(cast(uint)width, cast(uint)height);
125 	}
126 
127 	override string clipboardString() @property
128 	{
129 		const(char*) data = glfwGetClipboardString(glfwWindowPtr);
130 		if (data is null) return "";
131 		return cast(string)fromStringz(data);
132 	}
133 
134 	override void clipboardString(string newClipboardString) @property
135 	{
136 		glfwSetClipboardString(glfwWindowPtr, toStringz(newClipboardString));
137 	}
138 
139 	override bool isKeyPressed(uint key)
140 	{
141 		return glfwGetKey(glfwWindowPtr, key) == GLFW_PRESS;
142 	}
143 
144 	GLFWwindow* handle() @property
145 	{
146 		return glfwWindowPtr;
147 	}
148 
149 private:
150 	static void initGlfw()
151 	{
152 		glfwSetErrorCallback(&errorfun);
153 
154 		if (glfwInit() == 0)
155 		{
156 			throw new Error("Error initializing GLFW3"); //TODO: add proper error handling
157 		}
158 
159 		glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
160 		glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
161 		//glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
162 
163 		glfwInited = true;
164 	}
165 }
166 
167 GlfwWindow getWinFromUP(GLFWwindow* w) nothrow
168 {
169 	GlfwWindow win;
170 	win = cast(GlfwWindow) glfwGetWindowUserPointer(w);
171 	return win;
172 }
173 
174 extern(C) nothrow
175 {
176 	void errorfun(int errorCode, const(char)* msg)
177 	{
178 		throw new Error(format("GLFW error [%s] : %s", errorCode, fromStringz(msg)));
179 	}
180 	void windowposfun(GLFWwindow* w, int nx, int ny)
181 	{
182 		try getWinFromUP(w).windowMoved.emit(ivec2(nx, ny));
183 		catch(Exception e) throw new Error(to!string(e));
184 	}
185 	void windowsizefun(GLFWwindow* w, int newWidth, int newHeight)
186 	{
187 		try getWinFromUP(w).windowResized.emit(uvec2(cast(uint)newWidth, cast(uint)newHeight));
188 		catch(Exception e) throw new Error(to!string(e));
189 	}
190 	void windowclosefun(GLFWwindow* w)
191 	{
192 		try getWinFromUP(w).closePressed.emit();
193 		catch(Exception e) throw new Error(to!string(e));
194 	}
195 	void windowrefreshfun(GLFWwindow* w)
196 	{
197 
198 	}
199 	void windowfocusfun(GLFWwindow* w, int focus)
200 	{
201 		try getWinFromUP(w).focusChanged.emit(cast(bool)focus);
202 		catch(Exception e) throw new Error(to!string(e));
203 	}
204 	void windowiconifyfun(GLFWwindow* w, int iconified)
205 	{
206 		try getWinFromUP(w).windowIconified.emit(cast(bool)iconified);
207 		catch(Exception e) throw new Error(to!string(e));
208 	}
209 	void mousebuttonfun(GLFWwindow* w, int mouseButton, int action, int)
210 	{
211 		try
212 		{
213 			if(action == GLFW_RELEASE)
214 			{
215 				getWinFromUP(w).mouseReleased.emit(mouseButton);
216 			}
217 			else
218 			{
219 				getWinFromUP(w).mousePressed.emit(mouseButton);
220 			}
221 		}
222 		catch(Exception e) throw new Error(to!string(e));
223 	}
224 	void cursorposfun(GLFWwindow* w, double nx, double ny)
225 	{
226 		try getWinFromUP(w).mouseMoved.emit(ivec2(cast(int)nx, cast(int)ny));
227 		catch(Exception e) throw new Error(to!string(e));
228 	}
229 	void scrollfun(GLFWwindow* w, double x, double y)
230 	{
231 		try getWinFromUP(w).wheelScrolled.emit(dvec2(x, y));
232 		catch(Exception e) throw new Error(to!string(e));
233 	}
234 	void cursorenterfun(GLFWwindow* w, int)
235 	{
236 
237 	}
238 	void keyfun(GLFWwindow* w, int key, int, int action, int)
239 	{
240 		try
241 		{
242 			if (action == GLFW_RELEASE)
243 			{
244 				getWinFromUP(w).keyReleased.emit(key);
245 			}
246 			else
247 			{
248 				getWinFromUP(w).keyPressed.emit(key);
249 			}
250 		}
251 		catch(Exception e) throw new Error(to!string(e));
252 	}
253 	void charfun(GLFWwindow* w, uint unicode)
254 	{
255 		try getWinFromUP(w).charEntered.emit(cast(dchar)unicode);
256 		catch(Exception e) throw new Error(to!string(e));
257 	}
258 	void refreshfun(GLFWwindow* w)
259 	{
260 		try getWinFromUP(w).refresh.emit();
261 		catch(Exception e) throw new Error(to!string(e));
262 	}
263 }