1 module gfm.sdl2.window;
2 
3 import std.string;
4 
5 import derelict.sdl2.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, int 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 
311     package
312     {
313         SDL2 _sdl2;
314         SDL_Window* _window;
315     }
316 
317     private
318     {
319         Logger _logger;
320         SDL2Surface _surface;
321         SDL2GLContext _glContext;
322         uint _id;
323 
324         bool _surfaceMustBeRenewed;
325 
326         bool hasValidSurface()
327         {
328             return (!_surfaceMustBeRenewed) && (_surface !is null);
329         }
330     }
331 }
332 
333 /// SDL OpenGL context wrapper. You probably don't need to use it directly.
334 final class SDL2GLContext
335 {
336     public
337     {
338         /// Creates an OpenGL context for a given SDL window.
339         this(SDL2Window window)
340         {
341             _window = window;
342             _context = SDL_GL_CreateContext(window._window);
343             _initialized = true;
344         }
345 
346         ~this()
347         {
348             close();
349         }
350 
351         /// Release the associated SDL ressource.
352         void close()
353         {
354             if (_initialized)
355             {
356                 // work-around Issue #19
357                 // SDL complains with log message "wglMakeCurrent(): The handle is invalid."
358                 // in the SDL_DestroyWindow() call if we destroy the OpenGL context before-hand
359                 //
360                 // SDL_GL_DeleteContext(_context);
361                 _initialized = false;
362             }
363         }
364 
365         /// Makes this OpenGL context current.
366         /// Throws: $(D SDL2Exception) on error.
367         void makeCurrent()
368         {
369             if (0 != SDL_GL_MakeCurrent(_window._window, _context))
370                 _window._sdl2.throwSDL2Exception("SDL_GL_MakeCurrent");
371         }
372     }
373 
374     package
375     {
376         SDL_GLContext _context;
377         SDL2Window _window;
378     }
379 
380     private
381     {
382         bool _initialized;
383     }
384 }
385