1 /**
2 Copyright: Copyright (c) 2013-2017 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.log;
15 import voxelman.math;
16 import anchovy.iwindow : IWindow;
17 import anchovy.isharedcontext;
18 
19 class GlfwSharedContext : ISharedContext
20 {
21 	GLFWwindow* sharedContext;
22 
23 	this(GLFWwindow* _sharedContext)
24 	{
25 		this.sharedContext = _sharedContext;
26 	}
27 
28 	void makeCurrent()
29 	{
30 		glfwMakeContextCurrent(sharedContext);
31 	}
32 }
33 
34 class GlfwWindow : IWindow
35 {
36 private:
37 	GLFWwindow*	glfwWindowPtr;
38 	static bool	glfwInited = false;
39 	bool isProcessingEvents = false;
40 
41 public:
42 	override void init(ivec2 size, in string caption, bool center = false)
43 	{
44 		if (!glfwInited)
45 			initGlfw();
46 
47 		scope(failure) glfwTerminate();
48 
49 		glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
50 		glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
51 		//glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
52 		glfwWindowHint(GLFW_VISIBLE, false);
53 
54 		//BUG: sometimes fails in Windows 8. Maybe because of old drivers.
55 		glfwWindowPtr = glfwCreateWindow(size.x, size.y, toStringz(caption), null,  null);
56 
57 		if (glfwWindowPtr is null)
58 		{
59 			throw new Error("Error creating GLFW3 window");
60 		}
61 
62 		if (center) moveToCenter();
63 
64 		glfwShowWindow(glfwWindowPtr);
65 
66 		glfwMakeContextCurrent(glfwWindowPtr);
67 		glfwSwapInterval(0);
68 
69 		glClearColor(1.0, 1.0, 1.0, 1.0);
70 		glViewport(0, 0, size.x, size.y);
71 
72 		DerelictGL3.reload();
73 
74 		glfwSetWindowUserPointer(glfwWindowPtr, cast(void*)this);
75 		glfwSetWindowPosCallback(glfwWindowPtr, &windowposfun);
76 		glfwSetFramebufferSizeCallback(glfwWindowPtr, &windowsizefun);
77 		glfwSetWindowCloseCallback(glfwWindowPtr, &windowclosefun);
78 		//glfwSetWindowRefreshCallback(glfwWindowPtr, &windowrefreshfun);
79 		glfwSetWindowFocusCallback(glfwWindowPtr, &windowfocusfun);
80 		glfwSetWindowIconifyCallback(glfwWindowPtr, &windowiconifyfun);
81 		glfwSetMouseButtonCallback(glfwWindowPtr, &mousebuttonfun);
82 		glfwSetCursorPosCallback(glfwWindowPtr, &cursorposfun);
83 		glfwSetScrollCallback(glfwWindowPtr, &scrollfun);
84 		glfwSetKeyCallback(glfwWindowPtr, &keyfun);
85 		glfwSetCharCallback(glfwWindowPtr, &charfun);
86 		//glfwSetCursorEnterCallback(window, GLFWcursorenterfun cbfun);
87 		glfwSetWindowRefreshCallback(glfwWindowPtr, &refreshfun);
88 	}
89 
90 	override ISharedContext createSharedContext()
91 	{
92 		assert(glfwInited);
93 		assert(glfwWindowPtr);
94 		glfwWindowHint(GLFW_VISIBLE, false);
95 		GLFWwindow* sharedContext = glfwCreateWindow(100, 100, "", null, glfwWindowPtr);
96 		return new GlfwSharedContext(sharedContext);
97 	}
98 
99 	override void moveToCenter()
100 	{
101 		GLFWmonitor* primaryMonitor = glfwGetPrimaryMonitor();
102 		ivec2 currentSize = size();
103 		ivec2 primPos;
104 		glfwGetMonitorPos(primaryMonitor, &primPos.x, &primPos.y);
105 		const GLFWvidmode* primMode = glfwGetVideoMode(primaryMonitor);
106 		ivec2 primSize = ivec2(primMode.width, primMode.height);
107 		ivec2 newPos = primPos + primSize/2 - currentSize/2;
108 		glfwSetWindowPos(glfwWindowPtr, newPos.x, newPos.y);
109 	}
110 
111 	override void processEvents()
112 	{
113 		// prevent calling glfwPollEvents when inside window_refresh callback
114 		if (!isProcessingEvents)
115 		{
116 			isProcessingEvents = true;
117 			glfwPollEvents();
118 			isProcessingEvents = false;
119 		}
120 	}
121 
122 	override double elapsedTime() @property //in seconds
123 	{
124 		return glfwGetTime();
125 	}
126 
127 	override void reshape(ivec2 viewportSize)
128 	{
129 		glViewport(0, 0, viewportSize.x, viewportSize.y);
130 	}
131 
132 	override void releaseWindow()
133 	{
134 		glfwDestroyWindow(glfwWindowPtr);
135 		glfwTerminate();
136 	}
137 
138 	override void mousePosition(ivec2 newPosition) @property
139 	{
140 		glfwSetCursorPos(glfwWindowPtr, newPosition.x, newPosition.y);
141 	}
142 
143 	override ivec2 mousePosition() @property
144 	{
145 		double x, y;
146 		glfwGetCursorPos(glfwWindowPtr, &x, &y);
147 		return ivec2(x, y);
148 	}
149 
150 	override void swapBuffers()
151 	{
152 		glfwSwapBuffers(glfwWindowPtr);
153 	}
154 
155 	override void size(ivec2 newSize) @property
156 	{
157 		glfwSetWindowSize(glfwWindowPtr, newSize.x, newSize.y);
158 	}
159 
160 	override ivec2 size() @property
161 	{
162 		int width, height;
163 		glfwGetWindowSize(glfwWindowPtr, &width, &height);
164 		return ivec2(width, height);
165 	}
166 
167 	override ivec2 framebufferSize() @property
168 	{
169 		int width, height;
170 		glfwGetFramebufferSize(glfwWindowPtr, &width, &height);
171 		return ivec2(width, height);
172 	}
173 
174 	override string clipboardString() @property
175 	{
176 		const(char*) data = glfwGetClipboardString(glfwWindowPtr);
177 		if (data is null) return "";
178 		return cast(string)fromStringz(data);
179 	}
180 
181 	override void clipboardString(string newClipboardString) @property
182 	{
183 		glfwSetClipboardString(glfwWindowPtr, toStringz(newClipboardString));
184 	}
185 
186 	override bool isKeyPressed(uint key)
187 	{
188 		return glfwGetKey(glfwWindowPtr, key) == GLFW_PRESS;
189 	}
190 
191 	override void setMouseLock(bool isMouseLocked)
192 	{
193 		if (isMouseLocked)
194 			glfwSetInputMode(glfwWindowPtr, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
195 		else
196 			glfwSetInputMode(glfwWindowPtr, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
197 	}
198 
199 	GLFWwindow* handle() @property
200 	{
201 		return glfwWindowPtr;
202 	}
203 
204 private:
205 	static void initGlfw()
206 	{
207 		glfwSetErrorCallback(&errorfun);
208 
209 		if (glfwInit() == 0)
210 		{
211 			throw new Error("Error initializing GLFW3"); //TODO: add proper error handling
212 		}
213 		glfwInited = true;
214 	}
215 }
216 
217 GlfwWindow getWinFromUP(GLFWwindow* w) nothrow
218 {
219 	GlfwWindow win;
220 	win = cast(GlfwWindow) glfwGetWindowUserPointer(w);
221 	return win;
222 }
223 
224 extern(C) nothrow
225 {
226 	void errorfun(int errorCode, const(char)* msg)
227 	{
228 		throw new Error(format("GLFW error [%s] : %s", errorCode, fromStringz(msg)));
229 	}
230 	void windowposfun(GLFWwindow* w, int nx, int ny)
231 	{
232 		try getWinFromUP(w).windowMoved.emit(ivec2(nx, ny));
233 		catch(Exception e) throw new Error(to!string(e));
234 	}
235 	void windowsizefun(GLFWwindow* w, int newWidth, int newHeight)
236 	{
237 		try getWinFromUP(w).windowResized.emit(ivec2(newWidth, newHeight));
238 		catch(Exception e) throw new Error(to!string(e));
239 	}
240 	void windowclosefun(GLFWwindow* w)
241 	{
242 		try getWinFromUP(w).closePressed.emit();
243 		catch(Exception e) throw new Error(to!string(e));
244 	}
245 	void windowrefreshfun(GLFWwindow* w)
246 	{
247 
248 	}
249 	void windowfocusfun(GLFWwindow* w, int focus)
250 	{
251 		try getWinFromUP(w).focusChanged.emit(cast(bool)focus);
252 		catch(Exception e) throw new Error(to!string(e));
253 	}
254 	void windowiconifyfun(GLFWwindow* w, int iconified)
255 	{
256 		try getWinFromUP(w).windowIconified.emit(cast(bool)iconified);
257 		catch(Exception e) throw new Error(to!string(e));
258 	}
259 	void mousebuttonfun(GLFWwindow* w, int mouseButton, int action, int)
260 	{
261 		try
262 		{
263 			if(action == GLFW_RELEASE)
264 			{
265 				getWinFromUP(w).mouseReleased.emit(mouseButton);
266 			}
267 			else
268 			{
269 				getWinFromUP(w).mousePressed.emit(mouseButton);
270 			}
271 		}
272 		catch(Exception e) throw new Error(to!string(e));
273 	}
274 	void cursorposfun(GLFWwindow* w, double nx, double ny)
275 	{
276 		try getWinFromUP(w).mouseMoved.emit(ivec2(cast(int)nx, cast(int)ny));
277 		catch(Exception e) throw new Error(to!string(e));
278 	}
279 	void scrollfun(GLFWwindow* w, double x, double y)
280 	{
281 		try getWinFromUP(w).wheelScrolled.emit(dvec2(x, y));
282 		catch(Exception e) throw new Error(to!string(e));
283 	}
284 	void cursorenterfun(GLFWwindow* w, int)
285 	{
286 
287 	}
288 	void keyfun(GLFWwindow* w, int key, int, int action, int)
289 	{
290 		if (key < 0) return;
291 		try
292 		{
293 			if (action == GLFW_RELEASE)
294 			{
295 				getWinFromUP(w).keyReleased.emit(key);
296 			}
297 			else
298 			{
299 				getWinFromUP(w).keyPressed.emit(key);
300 			}
301 		}
302 		catch(Exception e) throw new Error(to!string(e));
303 	}
304 	void charfun(GLFWwindow* w, uint unicode)
305 	{
306 	    if (unicode > 0 && unicode < 0x10000) {
307 			try getWinFromUP(w).charEntered.emit(cast(dchar)unicode);
308 			catch(Exception e) throw new Error(to!string(e));
309 		}
310 	}
311 	void refreshfun(GLFWwindow* w)
312 	{
313 		try getWinFromUP(w).refresh.emit();
314 		catch(Exception e) throw new Error(to!string(e));
315 	}
316 }