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