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 }