1 module unecht.glfw.glfwwindow;
2 
3 import std.stdio;
4 
5 import derelict.glfw3.glfw3;
6 
7 import unecht.ue;
8 import unecht.core.types;
9 import unecht.core.window;
10 import unecht.core.events;
11 
12 ///
13 class GlfwWindow : UEWindow
14 {
15 private:
16 	/// framebuffer size
17 	UESize size;
18 	/// actual window size
19 	UESize _windowSize;
20 	///
21 	UEEvents _events;
22 	///
23 	double _lastMousePosX = 0;
24 	///
25 	double _lastMousePosY = 0;
26 
27 package:
28 
29 	///
30 	public @property bool isRetina() const { return size.width > _windowSize.width && size.height > _windowSize.height; }
31 	///
32 	public @property bool shouldClose() { return glfwWindowShouldClose(glfwWindow)!=0; }
33 	///
34 	public @property GLFWwindow* window() { return glfwWindow; }
35 	///
36 	public @property void* windowPtr() { return glfwWindow; }
37 	///
38 	public @property UESize windowSize() const { return _windowSize; }
39 	///
40 	public @property UESize framebufferSize() const { return size; }
41 
42 	///
43 	bool create(UESize _size, string _title, UEEvents _evs)
44 	{
45 		import std.string:toStringz;
46 
47 		_events = _evs;
48 
49 		//TODO: support multisampling
50 		//glfwWindowHint(GLFW_SAMPLES, 4);
51 		glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, true);
52 		glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
53 		glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
54 		glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
55 
56 		glfwWindow = glfwCreateWindow(
57 			_size.width,
58 			_size.height,
59 			toStringz(_title), null, null);
60 
61 		if (!glfwWindow)
62 			return false;
63 
64 		glfwSetWindowUserPointer(glfwWindow, cast(void*)this);
65 		glfwMakeContextCurrent(glfwWindow);
66 
67 		int w,h;
68 		glfwGetFramebufferSize(glfwWindow, &w, &h);
69 		size = UESize(w,h);
70 
71 		glfwGetWindowSize(glfwWindow, &w, &h);
72 		_windowSize = UESize(w,h);
73 
74 		//TODO: support for fixed updates befor disabling vsync
75 		//glfwSwapInterval(0);
76 		glfwSwapInterval(1);
77 
78 		glfwSetCursorPosCallback(glfwWindow, &cursor_pos_callback);
79 		glfwSetMouseButtonCallback(glfwWindow, &mouse_button_callback);
80 		glfwSetKeyCallback(glfwWindow, &key_callback);
81 		glfwSetWindowSizeCallback(glfwWindow, &wnd_size_callback);
82 		glfwSetCharCallback(glfwWindow, &character_callback);
83 		glfwSetFramebufferSizeCallback(glfwWindow, &framebuffer_size_callback);
84 		glfwSetWindowFocusCallback(glfwWindow, &window_focus_callback);
85 		glfwSetScrollCallback(window, &mouse_scroll_callback);
86 
87 		return true;
88 	}
89 
90 	///
91 	public bool isCursorPosInside(float x, float y) pure const
92 	{
93 		static immutable border = 1;
94 
95 		return x > border &&
96 			y > border &&
97 			x < _windowSize.width - border &&
98 			y < _windowSize.height - border;
99 	}
100 
101 	///
102 	public void wrapAroundCursorPos(float x, float y)
103 	{
104 		static immutable border = 3;
105 
106 		auto newx = cast(int)x % (_windowSize.width - border);
107 		auto newy = cast(int)y % (_windowSize.height - border);
108 
109 		if(x < border)
110 			newx = _windowSize.width - border + cast(int)x;
111 		if(y < border)
112 			newy = _windowSize.height - border + cast(int)y;
113 
114 		glfwSetCursorPos(glfwWindow, newx, newy);
115 	}
116 
117 	///
118 	void showCursor(bool _show)
119 	{
120 		glfwSetInputMode(glfwWindow, GLFW_CURSOR, _show?GLFW_CURSOR_NORMAL:GLFW_CURSOR_HIDDEN);
121 	}
122 
123 	///
124 	void destroy()
125 	{
126 		glfwDestroyWindow(glfwWindow);
127 	}
128 
129 	///
130 	void close()
131 	{
132 		glfwSetWindowShouldClose(glfwWindow, true);
133 	}
134 
135 	///
136 	void swapBuffers()
137 	{
138 		glfwSwapBuffers(glfwWindow);
139 	}
140 
141 	///
142 	void onResize(UESize size)
143 	{
144 		_windowSize = size;
145 	}
146 
147 	///
148 	private void glfwOnKey(int key, int scancode, int action, int mods) nothrow
149 	{
150 		UEEvent ev;
151 		ev.eventType = UEEventType.key;
152 		ev.keyEvent.key = cast(UEKey)key;
153 		ev.keyEvent.action = UEEvent.KeyEvent.Action.Down;
154 
155 		if(action == GLFW_RELEASE)
156 			ev.keyEvent.action = UEEvent.KeyEvent.Action.Up;
157 		else if(action == GLFW_REPEAT)
158 			ev.keyEvent.action = UEEvent.KeyEvent.Action.Repeat;
159 
160 		ev.keyEvent.mods.setFromBitMaskGLFW(mods);
161 
162    		_events.trigger(ev);
163 	}
164 
165 	///
166 	private void glfwOnChar(uint codepoint) nothrow
167 	{
168 		UEEvent ev;
169 		ev.eventType = UEEventType.text;
170 		ev.textEvent.character = cast(dchar)codepoint;
171 
172 		populateCurrentKeyMods(ev.textEvent.mods);
173 
174 		_events.trigger(ev);
175 	}
176 
177 	///
178 	private void glfwOnMouseMove(double x, double y) nothrow
179 	{
180 		UEEvent ev;
181 		ev.eventType = UEEventType.mousePos;
182 		ev.mousePosEvent.x = _lastMousePosX = x;
183 		ev.mousePosEvent.y = _lastMousePosY = y;
184 
185 		populateCurrentKeyMods(ev.mousePosEvent.mods);
186 
187 		_events.trigger(ev);
188 	}
189 
190 	///
191 	private void glfwOnMouseButton(int button, int action, int mods) nothrow
192 	{
193 		UEEvent ev;
194 		ev.eventType = UEEventType.mouseButton;
195 		ev.mouseButtonEvent.button = button;
196 		ev.mouseButtonEvent.action = (action == GLFW_PRESS) ? UEEvent.MouseButtonEvent.Action.down : UEEvent.MouseButtonEvent.Action.up;
197 
198 		//TODO: click detection here instaed of in mouseControls.d
199 		ev.mouseButtonEvent.pos.x = _lastMousePosX;
200 		ev.mouseButtonEvent.pos.y = _lastMousePosY;
201 
202 		ev.mouseButtonEvent.pos.mods.setFromBitMaskGLFW(mods);
203 
204 		_events.trigger(ev);
205 	}
206 
207 	///
208 	private void glfwOnMouseScroll(double xoffset, double yoffset) nothrow
209 	{
210 		UEEvent ev;
211 		ev.eventType = UEEventType.mouseScroll;
212 		ev.mouseScrollEvent.xoffset = xoffset;
213 		ev.mouseScrollEvent.yoffset = yoffset;
214 
215 		populateCurrentKeyMods(ev.mousePosEvent.mods);
216 
217 		_events.trigger(ev);
218 	}
219 
220 	///
221 	private void glfwOnWndSize(int width, int height) nothrow
222 	{
223 		UEEvent ev;
224 		ev.eventType = UEEventType.windowSize;
225 		ev.windowSizeEvent.size = UESize(width,height);
226 
227 		_windowSize = ev.windowSizeEvent.size;
228 
229 		_events.trigger(ev);
230 	}
231 
232 	///
233 	private void glfwOnFramebufferSize(int width, int height) nothrow
234 	{
235 		UEEvent ev;
236 		ev.eventType = UEEventType.framebufferSize;
237 		ev.framebufferSizeEvent.size = UESize(width,height);
238 
239 		size = ev.framebufferSizeEvent.size;
240 
241 		_events.trigger(ev);
242 	}
243 
244 	///
245 	private void glfwOnWindowFocus(bool gainedFocus) nothrow
246 	{
247 		UEEvent ev;
248 		ev.eventType = UEEventType.windowFocus;
249 		ev.focusEvent.gainedFocus = gainedFocus;
250 
251 		_events.trigger(ev);
252 	}
253 
254 	///
255 	private void populateCurrentKeyMods(ref EventModKeys mods) nothrow
256 	{
257 		mods.set(glfwGetKey(glfwWindow, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS,
258 			glfwGetKey(glfwWindow, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS,
259 			glfwGetKey(glfwWindow, GLFW_KEY_LEFT_ALT) == GLFW_PRESS,
260 			glfwGetKey(glfwWindow, GLFW_KEY_LEFT_SUPER) == GLFW_PRESS);
261 	}
262 
263 private:
264 	GLFWwindow* glfwWindow;
265 }
266 
267 private nothrow extern(C)
268 {
269 	void character_callback(GLFWwindow* window, uint codepoint)
270 	{
271 		GlfwWindow wnd = cast(GlfwWindow)glfwGetWindowUserPointer(window);
272 		wnd.glfwOnChar(codepoint);
273 	}
274 
275 	void cursor_pos_callback(GLFWwindow* window, double xpos, double ypos)
276 	{
277 		GlfwWindow wnd = cast(GlfwWindow)glfwGetWindowUserPointer(window);
278 		wnd.glfwOnMouseMove(xpos, ypos);
279 	}
280 
281 	void mouse_button_callback(GLFWwindow* window, int button, int action, int mods)
282 	{
283 		GlfwWindow wnd = cast(GlfwWindow)glfwGetWindowUserPointer(window);
284 		wnd.glfwOnMouseButton(button, action, mods);
285 	}
286 
287 	void mouse_scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
288 	{
289 		GlfwWindow wnd = cast(GlfwWindow)glfwGetWindowUserPointer(window);
290 		wnd.glfwOnMouseScroll(xoffset, yoffset);
291 	}
292 
293 	void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
294 	{
295 		GlfwWindow wnd = cast(GlfwWindow)glfwGetWindowUserPointer(window);
296 		wnd.glfwOnKey(key,scancode,action,mods);
297 	}
298 
299 	void wnd_size_callback(GLFWwindow* window, int width, int height) nothrow
300 	{
301 		GlfwWindow wnd = cast(GlfwWindow)glfwGetWindowUserPointer(window);
302 		wnd.glfwOnWndSize(width,height);
303 	}
304 
305 	void framebuffer_size_callback(GLFWwindow* window, int width, int height)
306 	{
307 		GlfwWindow wnd = cast(GlfwWindow)glfwGetWindowUserPointer(window);
308 		wnd.glfwOnFramebufferSize(width,height);
309 	}
310 
311 	void window_focus_callback(GLFWwindow* window, int gainedFocus)
312 	{
313 		GlfwWindow wnd = cast(GlfwWindow)glfwGetWindowUserPointer(window);
314 		wnd.glfwOnWindowFocus(gainedFocus!=0);
315 	}
316 }