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 }