1 module gfm.sdl2.window;
2 
3 import std..string;
4 
5 import bindbc.sdl;
6 
7 import std.experimental.logger;
8 
9 import gfm.sdl2.sdl,
10        gfm.sdl2.surface,
11        gfm.sdl2.mouse,
12        gfm.sdl2.keyboard;
13 
14 
15 /// SDL Window wrapper.
16 /// There is two ways to receive events, either by polling a SDL2 object,
17 /// or by overriding the event callbacks.
18 final class SDL2Window
19 {
20     public
21     {
22         /// Creates a SDL window which targets a window.
23         /// See_also: $(LINK http://wiki.libsdl.org/SDL_CreateWindow)
24         /// Throws: $(D SDL2Exception) on error.
25         this(SDL2 sdl2, int x, int y, int width, int height, SDL_WindowFlags flags)
26         {
27             _sdl2 = sdl2;
28             _logger = sdl2._logger;
29             _surface = null;
30             _glContext = null;
31             _surfaceMustBeRenewed = false;
32 
33             bool OpenGL = (flags & SDL_WINDOW_OPENGL) != 0;
34             _window = SDL_CreateWindow(toStringz(""), x, y, width, height, flags);
35             if (_window == null)
36 			{
37 				string message = "SDL_CreateWindow failed: " ~ _sdl2.getErrorString().idup;
38                 throw new SDL2Exception(message);
39 			}
40 
41             _id = SDL_GetWindowID(_window);
42 
43             if (OpenGL)
44                 _glContext = new SDL2GLContext(this);
45         }
46 
47         /// Creates a SDL window from anexisting handle.
48         /// See_also: $(LINK http://wiki.libsdl.org/SDL_CreateWindowFrom)
49         /// Throws: $(D SDL2Exception) on error.
50         this(SDL2 sdl2, void* windowData)
51         {
52             _sdl2 = sdl2;
53             _logger = sdl2._logger;
54             _surface = null;
55             _glContext = null;
56             _surfaceMustBeRenewed = false;
57              _window = SDL_CreateWindowFrom(windowData);
58              if (_window == null)
59              {
60                  string message = "SDL_CreateWindowFrom failed: " ~ _sdl2.getErrorString().idup;
61                  throw new SDL2Exception(message);
62              }
63 
64             _id = SDL_GetWindowID(_window);
65         }
66 
67 
68         /// Releases the SDL resource.
69         /// See_also: $(LINK http://wiki.libsdl.org/SDL_DestroyWindow)
70         ~this()
71         {
72             if (_glContext !is null)
73             {
74                 debug ensureNotInGC("SDL2Window");
75                 _glContext.destroy();
76                 _glContext = null;
77             }
78 
79             if (_window !is null)
80             {
81                 debug ensureNotInGC("SDL2Window");
82                 SDL_DestroyWindow(_window);
83                 _window = null;
84             }
85         }
86 
87         /// See_also: $(LINK http://wiki.libsdl.org/SDL_SetWindowFullscreen)
88         /// Throws: $(D SDL2Exception) on error.
89         final void setFullscreenSetting(uint flags)
90         {
91             if (SDL_SetWindowFullscreen(_window, flags) != 0)
92                 _sdl2.throwSDL2Exception("SDL_SetWindowFullscreen");
93         }
94 
95         /// Returns: The flags associated with the window.
96         /// See_also: $(LINK https://wiki.libsdl.org/SDL_GetWindowFlags)
97         final uint getWindowFlags()
98         {
99             return SDL_GetWindowFlags(_window);
100         }
101 
102         /// Returns: X window coordinate.
103         /// See_also: $(LINK http://wiki.libsdl.org/SDL_GetWindowPosition)
104         final int getX()
105         {
106             return getPosition().x;
107         }
108 
109         /// Returns: Y window coordinate.
110         /// See_also: $(LINK http://wiki.libsdl.org/SDL_GetWindowPosition)
111         final int getY()
112         {
113             return getPosition().y;
114         }
115 
116         /// Gets information about the window's display mode
117         /// See_also: $(LINK https://wiki.libsdl.org/SDL_GetWindowDisplayMode)
118         final SDL_DisplayMode getWindowDisplayMode()
119         {
120             SDL_DisplayMode mode;
121             if (0 != SDL_GetWindowDisplayMode(_window, &mode))
122                 _sdl2.throwSDL2Exception("SDL_GetWindowDisplayMode");
123             return mode;
124         }
125 
126         /// Returns: Window coordinates.
127         /// See_also: $(LINK http://wiki.libsdl.org/SDL_GetWindowPosition)
128         final SDL_Point getPosition()
129         {
130             int x, y;
131             SDL_GetWindowPosition(_window, &x, &y);
132             return SDL_Point(x, y);
133         }
134 
135         /// See_also: $(LINK http://wiki.libsdl.org/SDL_SetWindowPosition)
136         final void setPosition(int positionX, int positionY)
137         {
138             SDL_SetWindowPosition(_window, positionX, positionY);
139         }
140 
141         /// See_also: $(LINK http://wiki.libsdl.org/SDL_SetWindowSize)
142         final void setSize(int width, int height)
143         {
144             SDL_SetWindowSize(_window, width, height);
145         }
146 
147         /// Get the minimum size setting for the window
148         /// See_also: $(LINK https://wiki.libsdl.org/SDL_GetWindowMinimumSize)
149         final SDL_Point getMinimumSize()
150         {
151             SDL_Point p;
152             SDL_GetWindowMinimumSize(_window, &p.x, &p.y);
153             return p;
154         }
155 
156         /// Get the minimum size setting for the window
157         /// See_also: $(LINK https://wiki.libsdl.org/SDL_SetWindowMinimumSize)
158         final void setMinimumSize(int width, int height)
159         {
160             SDL_SetWindowMinimumSize(_window, width, height);
161         }
162 
163         /// Get the minimum size setting for the window
164         /// See_also: $(LINK https://wiki.libsdl.org/SDL_GetWindowMaximumSize)
165         final SDL_Point getMaximumSize()
166         {
167             SDL_Point p;
168             SDL_GetWindowMaximumSize(_window, &p.x, &p.y);
169             return p;
170         }
171 
172         /// Get the minimum size setting for the window
173         /// See_also: $(LINK https://wiki.libsdl.org/SDL_SetWindowMaximumSize)
174         final void setMaximumSize(int width, int height)
175         {
176             SDL_SetWindowMaximumSize(_window, width, height);
177         }
178 
179         /// See_also: $(LINK http://wiki.libsdl.org/SDL_GetWindowSize)
180         /// Returns: Window size in pixels.
181         final SDL_Point getSize()
182         {
183             int w, h;
184             SDL_GetWindowSize(_window, &w, &h);
185             return SDL_Point(w, h);
186         }
187 
188         /// See_also: $(LINK http://wiki.libsdl.org/SDL_SetWindowIcon)
189         final void setIcon(SDL2Surface icon)
190         {
191             SDL_SetWindowIcon(_window, icon.handle());
192         }
193 
194         /// See_also: $(LINK http://wiki.libsdl.org/SDL_SetWindowBordered)
195         final void setBordered(bool bordered)
196         {
197             SDL_SetWindowBordered(_window, bordered ? SDL_TRUE : SDL_FALSE);
198         }
199 
200         /// See_also: $(LINK http://wiki.libsdl.org/SDL_GetWindowSize)
201         /// Returns: Window width in pixels.
202         final int getWidth()
203         {
204             int w, h;
205             SDL_GetWindowSize(_window, &w, &h);
206             return w;
207         }
208 
209         /// See_also: $(LINK http://wiki.libsdl.org/SDL_GetWindowSize)
210         /// Returns: Window height in pixels.
211         final int getHeight()
212         {
213             int w, h;
214             SDL_GetWindowSize(_window, &w, &h);
215             return h;
216         }
217 
218         /// See_also: $(LINK http://wiki.libsdl.org/SDL_SetWindowTitle)
219         final void setTitle(string title)
220         {
221             SDL_SetWindowTitle(_window, toStringz(title));
222         }
223 
224         /// See_also: $(LINK http://wiki.libsdl.org/SDL_ShowWindow)
225         final void show()
226         {
227             SDL_ShowWindow(_window);
228         }
229 
230         /// See_also: $(LINK http://wiki.libsdl.org/SDL_HideWindow)
231         final void hide()
232         {
233             SDL_HideWindow(_window);
234         }
235 
236         /// See_also: $(LINK http://wiki.libsdl.org/SDL_MinimizeWindow)
237         final void minimize()
238         {
239             SDL_MinimizeWindow(_window);
240         }
241 
242         /// See_also: $(LINK http://wiki.libsdl.org/SDL_MaximizeWindow)
243         final void maximize()
244         {
245             SDL_MaximizeWindow(_window);
246         }
247 
248         /// Returns: Window surface.
249         /// See_also: $(LINK http://wiki.libsdl.org/SDL_GetWindowSurface)
250         /// Throws: $(D SDL2Exception) on error.
251         final SDL2Surface surface()
252         {
253             if (!hasValidSurface())
254             {
255                 SDL_Surface* internalSurface = SDL_GetWindowSurface(_window);
256                 if (internalSurface is null)
257                     _sdl2.throwSDL2Exception("SDL_GetWindowSurface");
258 
259                 // renews surface as needed
260                 _surfaceMustBeRenewed = false;
261                 _surface = new SDL2Surface(_sdl2, internalSurface,  SDL2Surface.Owned.NO);
262             }
263             return _surface;
264         }
265 
266         /// Submit changes to the window surface.
267         /// See_also: $(LINK http://wiki.libsdl.org/SDL_UpdateWindowSurface)
268         /// Throws: $(D SDL2Exception) on error.
269         final void updateSurface()
270         {
271             if (!hasValidSurface())
272                 surface();
273 
274             int res = SDL_UpdateWindowSurface(_window);
275             if (res != 0)
276                 _sdl2.throwSDL2Exception("SDL_UpdateWindowSurface");
277 
278         }
279 
280         /// Returns: Window ID.
281         /// See_also: $(LINK http://wiki.libsdl.org/SDL_GetWindowID)
282         final int id()
283         {
284             return _id;
285         }
286 
287         /// Returns: System-specific window information, useful to use a third-party rendering library.
288         /// See_also: $(LINK http://wiki.libsdl.org/SDL_GetWindowWMInfo)
289         /// Throws: $(D SDL2Exception) on error.
290         SDL_SysWMinfo getWindowInfo()
291         {
292             SDL_SysWMinfo info;
293             SDL_VERSION(&info.version_);
294             int res = SDL_GetWindowWMInfo(_window, &info);
295             if (res != SDL_TRUE)
296                 _sdl2.throwSDL2Exception("SDL_GetWindowWMInfo");
297             return info;
298         }
299 
300         /// Swap OpenGL buffers.
301         /// See_also: $(LINK http://wiki.libsdl.org/SDL_GL_SwapWindow)
302         /// Throws: $(D SDL2Exception) on error.
303         void swapBuffers()
304         {
305             if (_glContext is null)
306                 throw new SDL2Exception("swapBuffers failed: not an OpenGL window");
307             SDL_GL_SwapWindow(_window);
308         }
309 
310         /// Returns: Native SDL window context
311         SDL_Window* nativeWindow() nothrow @nogc pure @safe
312         {
313             return _window;
314         }
315     }
316 
317     package
318     {
319         SDL2 _sdl2;
320         SDL_Window* _window;
321     }
322 
323     private
324     {
325         Logger _logger;
326         SDL2Surface _surface;
327         SDL2GLContext _glContext;
328         uint _id;
329 
330         bool _surfaceMustBeRenewed;
331 
332         bool hasValidSurface()
333         {
334             return (!_surfaceMustBeRenewed) && (_surface !is null);
335         }
336     }
337 }
338 
339 /// SDL OpenGL context wrapper. You probably don't need to use it directly.
340 final class SDL2GLContext
341 {
342     public
343     {
344         /// Creates an OpenGL context for a given SDL window.
345         this(SDL2Window window)
346         {
347             _window = window;
348             _context = SDL_GL_CreateContext(window._window);
349             _initialized = true;
350         }
351 
352         ~this()
353         {
354             close();
355         }
356 
357         /// Release the associated SDL ressource.
358         void close()
359         {
360             if (_initialized)
361             {
362                 // work-around Issue #19
363                 // SDL complains with log message "wglMakeCurrent(): The handle is invalid."
364                 // in the SDL_DestroyWindow() call if we destroy the OpenGL context before-hand
365                 //
366                 // SDL_GL_DeleteContext(_context);
367                 _initialized = false;
368             }
369         }
370 
371         /// Makes this OpenGL context current.
372         /// Throws: $(D SDL2Exception) on error.
373         void makeCurrent()
374         {
375             if (0 != SDL_GL_MakeCurrent(_window._window, _context))
376                 _window._sdl2.throwSDL2Exception("SDL_GL_MakeCurrent");
377         }
378     }
379 
380     package
381     {
382         SDL_GLContext _context;
383         SDL2Window _window;
384     }
385 
386     private
387     {
388         bool _initialized;
389     }
390 }
391