Skip to content

Commit 634c12f

Browse files
committed
Improve ext.Window memory safety
1 parent bcf3a51 commit 634c12f

File tree

3 files changed

+56
-12
lines changed

3 files changed

+56
-12
lines changed

doc/news.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ New Features:
1818
* Updated to wrap new functions and constants in SDL2 2.24.0 (PR #246).
1919
* Added the error-handling :func:`sdl2.ext.raise_sdl_err` function to the public
2020
API.
21+
* Added informative exceptions when trying perform operations (e.g. show, hide,
22+
minimize) on a closed :obj:`sdl2.ext.Window`, and generally improved memory
23+
safety of the Window class.
2124

2225
Fixed Bugs:
2326

@@ -29,6 +32,8 @@ API Changes:
2932

3033
* Moved :class:`sdl2.ext.SDLError` and :func:`sdl2.ext.raise_sdl_err`
3134
internally to a new submodule :mod:`sdl2.ext.err`.
35+
* :obj:`sdl2.ext.Window` objects can now be repositioned when closed (would
36+
previously raise an ``SDLError`` exception).
3237

3338

3439
0.9.13

sdl2/ext/window.py

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -95,45 +95,53 @@ def __init__(self, title, size, position=None, flags=None):
9595
if flags is None:
9696
flags = self.DEFAULTFLAGS
9797

98+
self.window = None
9899
self._title = title
99100
self._position = position
100101
self._flags = flags
101102
self._size = size
102103

103-
self.window = None
104104
self.create()
105105

106106
def __del__(self):
107107
"""Releases the resources of the Window, implicitly destroying the
108108
underlying SDL2 window."""
109109
self.close()
110110

111+
def _ensure_window(self, action):
112+
if not self.window:
113+
raise RuntimeError("Cannot {0} a closed window.".format(action))
114+
111115
@property
112116
def position(self):
113117
"""tuple: The (x, y) pixel coordinates of the top-left corner of the
114118
window.
115119
116120
"""
121+
if not self.window:
122+
return self._position
117123
x, y = c_int(), c_int()
118124
video.SDL_GetWindowPosition(self.window, byref(x), byref(y))
119125
return x.value, y.value
120126

121127
@position.setter
122128
def position(self, value):
123-
if not self.window:
124-
raise SDLError("The window is not available")
125-
video.SDL_SetWindowPosition(self.window, value[0], value[1])
129+
if self.window:
130+
video.SDL_SetWindowPosition(self.window, value[0], value[1])
126131
self._position = value[0], value[1]
127132

128133
@property
129134
def title(self):
130135
"""str: The title of the window."""
136+
if not self.window:
137+
return stringify(self._title, "utf-8")
131138
return stringify(video.SDL_GetWindowTitle(self.window), "utf-8")
132139

133140
@title.setter
134141
def title(self, value):
135-
title_bytes = utf8(value).encode('utf-8')
136-
video.SDL_SetWindowTitle(self.window, title_bytes)
142+
if self.window:
143+
title_bytes = utf8(value).encode('utf-8')
144+
video.SDL_SetWindowTitle(self.window, title_bytes)
137145
self._title = value
138146

139147
@property
@@ -142,30 +150,33 @@ def size(self):
142150
``(width, height)``.
143151
144152
"""
153+
if not self.window:
154+
return self._size
145155
w, h = c_int(), c_int()
146156
video.SDL_GetWindowSize(self.window, byref(w), byref(h))
147157
return w.value, h.value
148158

149159
@size.setter
150160
def size(self, value):
151-
video.SDL_SetWindowSize(self.window, value[0], value[1])
152161
self._size = value[0], value[1]
162+
if self.window:
163+
video.SDL_SetWindowSize(self.window, value[0], value[1])
153164

154165
def create(self):
155166
"""Creates the window if it does not already exist."""
156-
if self.window != None:
167+
if self.window:
157168
return
158169
window = video.SDL_CreateWindow(utf8(self._title).encode('utf-8'),
159170
self._position[0], self._position[1],
160171
self._size[0], self._size[1],
161172
self._flags)
162173
if not window:
163-
raise SDLError()
174+
raise_sdl_err("creating the window")
164175
self.window = window.contents
165176

166177
def open(self):
167178
"""Creates and shows the window."""
168-
if self.window is None:
179+
if not self.window:
169180
self.create()
170181
self.show()
171182

@@ -177,7 +188,7 @@ def close(self):
177188
:meth:`create` method.
178189
179190
"""
180-
if hasattr(self, "window") and self.window != None:
191+
if self.window:
181192
video.SDL_DestroyWindow(self.window)
182193
self.window = None
183194

@@ -187,6 +198,7 @@ def show(self):
187198
If the window is already visible, this method does nothing.
188199
189200
"""
201+
self._ensure_window("show")
190202
video.SDL_ShowWindow(self.window)
191203

192204
def hide(self):
@@ -195,14 +207,17 @@ def hide(self):
195207
If the window is already hidden, this method does nothing.
196208
197209
"""
210+
self._ensure_window("hide")
198211
video.SDL_HideWindow(self.window)
199212

200213
def maximize(self):
201214
"""Maximizes the window."""
215+
self._ensure_window("maximize")
202216
video.SDL_MaximizeWindow(self.window)
203217

204218
def minimize(self):
205219
"""Minimizes the window into the system's dock or task bar."""
220+
self._ensure_window("minimize")
206221
video.SDL_MinimizeWindow(self.window)
207222

208223
def restore(self):
@@ -212,6 +227,7 @@ def restore(self):
212227
nothing.
213228
214229
"""
230+
self._ensure_window("restore")
215231
video.SDL_RestoreWindow(self.window)
216232

217233
def refresh(self):
@@ -222,6 +238,7 @@ def refresh(self):
222238
modified using :meth:`get_surface`.
223239
224240
"""
241+
self._ensure_window("refresh")
225242
video.SDL_UpdateWindowSurface(self.window)
226243

227244
def get_surface(self):
@@ -245,7 +262,8 @@ def get_surface(self):
245262
:obj:`~sdl2.SDL_Surface`: The surface associated with the window.
246263
247264
"""
265+
self._ensure_window("get the surface of")
248266
sf = video.SDL_GetWindowSurface(self.window)
249267
if not sf:
250-
raise SDLError()
268+
raise_sdl_err("getting the window surface")
251269
return sf.contents

sdl2/test/sdl2ext_window_test.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,12 @@ def test_title(self, with_sdl):
3535
assert window.title == "Window"
3636
window.title = b"Test1234"
3737
assert window.title == "Test1234"
38+
video.SDL_SetWindowTitle(window.window, b"manual override")
39+
assert window.title == "manual override"
3840
window.close()
41+
assert window.title == "Test1234"
42+
window.title = "set when closed"
43+
assert window.title == "set when closed"
3944

4045
def test_show_hide(self, with_sdl):
4146
get_flags = video.SDL_GetWindowFlags
@@ -46,6 +51,11 @@ def test_show_hide(self, with_sdl):
4651
window.hide()
4752
assert get_flags(window.window) & SDL_WINDOW_SHOWN != SDL_WINDOW_SHOWN
4853
window.close()
54+
# Test informative exceptions for closed window
55+
with pytest.raises(RuntimeError):
56+
window.show()
57+
with pytest.raises(RuntimeError):
58+
window.hide()
4959

5060
@pytest.mark.skipif(SKIP_ANNOYING, reason="Skip unless requested")
5161
def test_maximize(self, with_sdl):
@@ -59,6 +69,9 @@ def test_maximize(self, with_sdl):
5969
if not DRIVER_DUMMY:
6070
assert get_flags(window.window) & max_flag == max_flag
6171
window.close()
72+
# Test informative exception for closed window
73+
with pytest.raises(RuntimeError):
74+
window.maximize()
6275

6376
@pytest.mark.skipif(SKIP_ANNOYING, reason="Skip unless requested")
6477
def test_minimize_restore(self, with_sdl):
@@ -73,6 +86,11 @@ def test_minimize_restore(self, with_sdl):
7386
window.restore()
7487
assert get_flags(window.window) & min_flag != min_flag
7588
window.close()
89+
# Test informative exceptions for closed window
90+
with pytest.raises(RuntimeError):
91+
window.minimize()
92+
with pytest.raises(RuntimeError):
93+
window.restore()
7694

7795
@pytest.mark.skip("not implemented")
7896
def test_refresh(self, with_sdl):
@@ -83,6 +101,9 @@ def test_get_surface(self, with_sdl):
83101
sf = window.get_surface()
84102
assert isinstance(sf, surface.SDL_Surface)
85103
window.close()
104+
# Test informative exception for closed window
105+
with pytest.raises(RuntimeError):
106+
sf = window.get_surface()
86107

87108
def test_open_close(self, with_sdl):
88109
get_flags = video.SDL_GetWindowFlags

0 commit comments

Comments
 (0)