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