1 module polyplex.core.windows.sdlwindow;
2 import polyplex.core.window;
3 import polyplex;
4 import polyplex.utils.sdlbool;
5 public import polyplex.core.render;
6 static import polyplex;
7 
8 import bindbc.sdl;
9 import bindbc.opengl;
10 import polyplex.math;
11 import polyplex.utils.logging;
12 import std.stdio;
13 import std.string;
14 import std.conv;
15 
16 
17 public enum WindowPosition {
18 	Center = -1,
19 	Undefined = 0
20 }
21 
22 public class SDLGameWindow : Window {
23 	private string start_title;
24     private SDL_Window* window;
25 	private Rectanglei startBounds;
26 
27 	/*
28 		Gets whenever the window is visible
29 	*/
30     public override @property bool Visible() { return (this.window !is null); }
31 
32 	// Resizing
33 	public override @property bool AllowResizing() {
34 		SDL_WindowFlags flags = SDL_GetWindowFlags(window);
35 		return ((flags & SDL_WINDOW_RESIZABLE) > 0);
36 	}
37 	public override @property void AllowResizing(bool allow) { SDL_SetWindowResizable(this.window, ToSDL(allow)); }
38 
39 	// VSync
40 	public override @property VSyncState VSync() {
41 		if (ActiveBackend == GraphicsBackend.OpenGL) return cast(VSyncState)SDL_GL_GetSwapInterval();
42 		return VSyncState.VSync;
43 	}
44 
45 	public override @property void VSync(VSyncState state) {
46 		if (ActiveBackend == GraphicsBackend.OpenGL) SDL_GL_SetSwapInterval(state);
47 	}
48 
49 	// Borderless
50 	public override @property bool Borderless() {
51 		SDL_WindowFlags flags = SDL_GetWindowFlags(window);
52 		return ((flags & SDL_WINDOW_BORDERLESS) > 0);
53 	}
54 	public override @property void Borderless(bool i) { SDL_SetWindowBordered(this.window, ToSDL(!i)); }
55 
56 	// Title
57 	public override @property string Title() { return to!string(SDL_GetWindowTitle(this.window)); }
58 	public override @property void Title(string value) { return SDL_SetWindowTitle(this.window, value.toStringz); }
59 
60 	//Brightness
61 	public @property float Brightness() { return SDL_GetWindowBrightness(this.window); }
62 	public @property void Brightness(float b) { SDL_SetWindowBrightness(this.window, b); }
63 
64 	//Fullscreen
65 	public override @property bool Fullscreen() {
66 		SDL_WindowFlags flags = SDL_GetWindowFlags(window);
67 		return ((flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_FULLSCREEN_DESKTOP)) > 0);
68 	}
69 
70 	public override @property void Fullscreen(bool i) {
71 		if (!i) {
72 			//Windowed.
73 			SDL_SetWindowFullscreen(this.window, 0);
74 			return;
75 		}
76 		if (Borderless) {
77 			//Fullscreen Borderless
78 			SDL_SetWindowFullscreen(this.window, SDL_WINDOW_FULLSCREEN_DESKTOP);
79 			return;
80 		}
81 		//Fullscreen
82 		SDL_SetWindowFullscreen(this.window, SDL_WINDOW_FULLSCREEN);
83 	}
84 
85 	/**
86 		Returns the position of the window.
87 	*/
88 	public @property Vector2 Position() {
89 		return Vector2(ClientBounds.X, ClientBounds.Y);
90 	}
91 
92 	/**
93 		Allows you to set the position of the window.
94 	*/
95 	public @property void Position(Vector2 pos) {
96 		SDL_SetWindowPosition(this.window, cast(int)pos.X, cast(int)pos.Y);
97 	}
98 
99     this(string name, Rectanglei bounds, bool focus = true) {
100 		super(name);
101         if (!SDL_Init(SDL_INIT_EVERYTHING) < 0) {
102             Logger.Fatal("Initialization of SDL2 failed!...\n{0}", SDL_GetError());
103         }
104 
105 		ClientBounds = new WindowBounds(this, bounds);
106 
107 		ClientBounds.windowResizeRequestEvent ~= (sender, data) {
108 			BoundsEventArgs dat = cast(BoundsEventArgs)data;
109 			SDL_SetWindowSize(this.window, dat.Width, dat.Height);
110 		};
111 
112 		ClientBounds.windowPositionRequestEvent ~= (sender, data) {
113 			BoundsEventArgs dat = cast(BoundsEventArgs)data;
114 			SDL_SetWindowPosition(this.window, dat.X, dat.Y);
115 		};
116 
117 		//Set info.
118         this.startBounds = bounds;
119 		this.start_title = name;
120 
121 		this.AutoFocus = focus;
122 
123 
124 
125 		//Cap info.
126 		if (this.startBounds.X == WindowPosition.Center) this.startBounds.X = SDL_WINDOWPOS_CENTERED;
127 		if (this.startBounds.Y == WindowPosition.Center) this.startBounds.Y = SDL_WINDOWPOS_CENTERED;
128 		if (this.startBounds.X == WindowPosition.Undefined) this.startBounds.X = SDL_WINDOWPOS_UNDEFINED;
129 		if (this.startBounds.Y == WindowPosition.Undefined) this.startBounds.Y = SDL_WINDOWPOS_UNDEFINED;
130 		if (this.startBounds.Width == WindowPosition.Undefined) this.startBounds.Width = 640;
131 		if (this.startBounds.Height == WindowPosition.Undefined) this.startBounds.Height = 480;
132     }
133 
134 	this (Rectanglei bounds, bool focus = true) {
135 		this("My Game", bounds, focus);
136 	}
137 
138 	this(bool focus = true) {
139 		this(Rectanglei(WindowPosition.Undefined, WindowPosition.Undefined, 640, 480), focus);
140 	}
141 
142     ~this() {
143 		if (this.window != null) Close();
144         SDL_Quit();
145     }
146 
147 	override GraphicsContext CreateContext(GraphicsBackend backend) {
148 		ActiveBackend = backend;
149 		version(OpenGL) {
150 			SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
151 			SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
152 			SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
153 		}
154 		version(OpenGL_ES) {
155 			SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
156 			SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
157 			SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
158 			SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
159 			SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
160 			SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
161 		}
162 		SDL_GLContext context = SDL_GL_CreateContext(this.window);
163 		if (context == null) throw new Error(to!string(SDL_GetError()));
164 		ActiveContext = GraphicsContext(context);
165 		return ActiveContext;
166 	}
167 
168 	override void DestroyContext() {
169 		if (ActiveBackend == GraphicsBackend.OpenGL) {
170 			SDL_GL_DeleteContext(ActiveContext.ContextPtr);
171 			Logger.Log("Deleted OpenGL context.");
172 			return;
173 		}
174 		// TODO: Destroy vulkan context.
175 	}
176 
177 	override void SwapBuffers() {
178 		if (ActiveBackend == GraphicsBackend.OpenGL) SDL_GL_SwapWindow(this.window);
179 	}
180 
181 	/**
182 		Triggers an window info update.
183 	*/
184 	override void UpdateState() {
185 		int x, y, width, height;
186 		SDL_GetWindowPosition(this.window, &x, &y);
187 		SDL_GetWindowSize(this.window, &width, &height);
188 		updateBounds(Rectanglei(x, y, width, height));
189 	}
190 
191 	/**
192 		Closes the window.
193 	*/
194 	override void Close() {
195 		SDL_DestroyWindow(this.window);
196 		//Explicitly destroy window.
197 		destroy(this.window);
198 	}
199 
200 	/**
201 		Puts the window in focus (ONLY WORKS ON SOME PLATFORMS!)
202 	*/
203 	override void Focus() {
204 		SDL_RaiseWindow(this.window);
205 	}
206 
207 	/**
208 		TODO
209 		Sets the icon for the window.
210 	*/
211 	override void SetIcon() {
212 		//TODO: When rendering is there, set icon.
213 	}
214 
215 	/**
216 		Shows the window.
217 	*/
218     override void Show() {
219 		Logger.Debug("Spawning window...");
220 		this.window = SDL_CreateWindow(this.start_title.toStringz, this.startBounds.X, this.startBounds.Y, this.startBounds.Width, this.startBounds.Height, SDL_WINDOW_OPENGL);
221 		if (this.window == null) {
222 			destroy(this.window);
223 			Logger.Fatal("Window creation error: {0}", SDL_GetError());
224 		}
225 		// Enable VSync by default.
226 		VSync = VSyncState.VSync;
227 
228 		// Focus window
229 		if (AutoFocus) this.Focus();
230     }
231 }