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 Rectangle start_bounds;
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, Rectangle 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.start_bounds = bounds;
119 		this.start_title = name;
120 
121 		this.AutoFocus = focus;
122 
123 
124 
125 		//Cap info.
126 		if (this.start_bounds is null) this.start_bounds = new Rectangle(WindowPosition.Undefined, WindowPosition.Undefined, 640, 480);
127 		if (this.start_bounds.X == WindowPosition.Center) this.start_bounds.X = SDL_WINDOWPOS_CENTERED;
128 		if (this.start_bounds.Y == WindowPosition.Center) this.start_bounds.Y = SDL_WINDOWPOS_CENTERED;
129 		if (this.start_bounds.X == WindowPosition.Undefined) this.start_bounds.X = SDL_WINDOWPOS_UNDEFINED;
130 		if (this.start_bounds.Y == WindowPosition.Undefined) this.start_bounds.Y = SDL_WINDOWPOS_UNDEFINED;
131 		if (this.start_bounds.Width == WindowPosition.Undefined) this.start_bounds.Width = 640;
132 		if (this.start_bounds.Height == WindowPosition.Undefined) this.start_bounds.Height = 480;
133     }
134 
135 	this (Rectangle bounds, bool focus = true) {
136 		this("My Game", bounds, focus);
137 	}
138 
139 	this(bool focus = true) {
140 		this(new Rectangle(WindowPosition.Undefined, WindowPosition.Undefined, 640, 480), focus);
141 	}
142 
143     ~this() {
144 		if (this.window != null) Close();
145         SDL_Quit();
146     }
147 
148 	override GraphicsContext CreateContext(GraphicsBackend backend) {
149 		ActiveBackend = backend;
150 		SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
151 		SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
152 		SDL_GLContext context = SDL_GL_CreateContext(this.window);
153 		if (context == null) throw new Error(to!string(SDL_GetError()));
154 		ActiveContext = GraphicsContext(context);
155 		return ActiveContext;
156 	}
157 
158 	override void DestroyContext() {
159 		if (ActiveBackend == GraphicsBackend.OpenGL) {
160 			SDL_GL_DeleteContext(ActiveContext.ContextPtr);
161 			Logger.Log("Deleted OpenGL context.");
162 			return;
163 		}
164 		// TODO: Destroy vulkan context.
165 	}
166 
167 	override void SwapBuffers() {
168 		if (ActiveBackend == GraphicsBackend.OpenGL) SDL_GL_SwapWindow(this.window);
169 	}
170 
171 	/**
172 		Triggers an window info update.
173 	*/
174 	override void UpdateState() {
175 		int x, y, width, height;
176 		SDL_GetWindowPosition(this.window, &x, &y);
177 		SDL_GetWindowSize(this.window, &width, &height);
178 		updateBounds(new Rectangle(x, y, width, height));
179 	}
180 
181 	/**
182 		Closes the window.
183 	*/
184 	override void Close() {
185 		SDL_DestroyWindow(this.window);
186 		//Explicitly destroy window.
187 		destroy(this.window);
188 	}
189 
190 	/**
191 		Puts the window in focus (ONLY WORKS ON SOME PLATFORMS!)
192 	*/
193 	override void Focus() {
194 		SDL_RaiseWindow(this.window);
195 	}
196 
197 	/**
198 		TODO
199 		Sets the icon for the window.
200 	*/
201 	override void SetIcon() {
202 		//TODO: When rendering is there, set icon.
203 	}
204 
205 	/**
206 		Shows the window.
207 	*/
208     override void Show() {
209 		Logger.Debug("Spawning window...");
210 		this.window = SDL_CreateWindow(this.start_title.toStringz, this.start_bounds.X, this.start_bounds.Y, this.start_bounds.Width, this.start_bounds.Height, SDL_WINDOW_OPENGL);
211 		Renderer.AssignRenderer(this);
212 		if (this.window == null) {
213 			destroy(this.window);
214 			Logger.Fatal("Window creation error: {0}", SDL_GetError());
215 		}
216 		// Enable VSync by default.
217 		VSync = VSyncState.VSync;
218 
219 		// Focus window
220 		if (AutoFocus) this.Focus();
221     }
222 }