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 }