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