1 module evael.system.Window; 2 3 import std.string : toStringz; 4 import std.traits : EnumMembers; 5 6 public import derelict.glfw3.glfw3; 7 import derelict.opengl; 8 import derelict.nanovg.nanovg; 9 10 import evael.system.WindowSettings; 11 import evael.system.GLContextSettings; 12 import evael.system.Cursor; 13 14 /** 15 * Window. 16 * 17 * High-level interface to GLFWWindow. 18 */ 19 class Window 20 { 21 /// GLFW window handle 22 private GLFWwindow* m_glfwWindow; 23 24 /// GLFW cursors 25 private GLFWcursor*[Cursor] m_cursors; 26 27 /// Current cursor 28 private Cursor m_currentCursor; 29 30 mixin(GLFWCallback!("WindowClose", "GLFWwindowclosefun")); 31 mixin(GLFWCallback!("WindowSize", "GLFWwindowsizefun")); 32 mixin(GLFWCallback!("CursorPos", "GLFWcursorposfun")); 33 mixin(GLFWCallback!("MouseButton", "GLFWmousebuttonfun")); 34 mixin(GLFWCallback!("Scroll", "GLFWscrollfun")); 35 mixin(GLFWCallback!("Key", "GLFWkeyfun")); 36 mixin(GLFWCallback!("Char", "GLFWcharfun")); 37 38 /** 39 * Window constructor. 40 * Params: 41 * settings : window settings 42 * contextSettings : gl settings 43 */ 44 public this(in ref WindowSettings settings, in ref GLContextSettings contextSettings) 45 { 46 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, contextSettings.ver / 10); 47 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, contextSettings.ver % 10); 48 glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); 49 glfwWindowHint(GLFW_RESIZABLE, settings.resizable); 50 glfwWindowHint(GLFW_SAMPLES, 4); 51 52 this.m_glfwWindow = glfwCreateWindow(settings.resolution.width, settings.resolution.height, 53 settings.title.toStringz(), settings.fullscreen ? glfwGetPrimaryMonitor() : null, null); 54 55 glfwMakeContextCurrent(this.m_glfwWindow); 56 57 glfwSwapInterval(settings.vsync); 58 59 // We have a context now, we can reload gl3 60 DerelictGL3.reload(); 61 DerelictNANOVG.load(); 62 63 foreach(cursor; [EnumMembers!(Cursor)]) 64 { 65 this.m_cursors[cursor] = glfwCreateStandardCursor(cursor); 66 } 67 68 this.setCursor(Cursor.Arrow); 69 } 70 71 /** 72 * Window destructor. 73 */ 74 public void dispose() 75 { 76 foreach (cursor; [EnumMembers!(Cursor)]) 77 { 78 glfwDestroyCursor(this.m_cursors[cursor]); 79 } 80 81 glfwDestroyWindow(this.m_glfwWindow); 82 } 83 84 /** 85 * Polls window events. 86 */ 87 @nogc 88 public void pollEvents() nothrow 89 { 90 glfwSwapBuffers(this.m_glfwWindow); 91 glfwPollEvents(); 92 } 93 94 /** 95 * Sets window cursor. 96 * Params: 97 * cursor : cursor to set 98 */ 99 @nogc 100 public void setCursor(in Cursor cursor) nothrow 101 { 102 if (this.m_currentCursor == cursor) 103 { 104 return; 105 } 106 107 auto cursorPtr = cursor in this.m_cursors; 108 109 if (cursorPtr !is null) 110 { 111 this.m_currentCursor = cursor; 112 glfwSetCursor(this.m_glfwWindow, *cursorPtr); 113 } 114 } 115 116 /** 117 * Hides cursor. 118 */ 119 @nogc 120 public void hideCursor() nothrow 121 { 122 glfwSetInputMode(this.m_glfwWindow, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); 123 } 124 125 @nogc @safe 126 @property pure nothrow 127 { 128 GLFWwindow* glfwWindow() 129 { 130 return this.m_glfwWindow; 131 } 132 } 133 } 134 135 template GLFWCallback(string name, string glfwCallback) 136 { 137 string GLFWCallbackImpl() 138 { 139 enum callbackName = name ~ "Callback"; 140 return "public void set" ~ callbackName ~ "(" ~ glfwCallback ~ " cb) { glfwSet" ~ callbackName ~ "(this.m_glfwWindow, cb); }"; 141 } 142 143 enum GLFWCallback = GLFWCallbackImpl; 144 }