1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #define FORBIDDEN_SYMBOL_ALLOW_ALL
24
25 #include "backends/platform/sdl/sdl-window.h"
26
27 #include "common/textconsole.h"
28
29 #include "icons/residualvm.xpm"
30
31 #if SDL_VERSION_ATLEAST(2, 0, 0)
32 static const uint32 fullscreenMask = SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_FULLSCREEN;
33 #endif
34
SdlWindow()35 SdlWindow::SdlWindow()
36 #if SDL_VERSION_ATLEAST(2, 0, 0)
37 : _window(nullptr), _inputGrabState(false), _windowCaption("ResidualVM"),
38 _lastFlags(0), _lastX(SDL_WINDOWPOS_UNDEFINED), _lastY(SDL_WINDOWPOS_UNDEFINED)
39 #endif
40 {
41 }
42
~SdlWindow()43 SdlWindow::~SdlWindow() {
44 #if SDL_VERSION_ATLEAST(2, 0, 0)
45 destroyWindow();
46 #endif
47 }
48
setupIcon()49 void SdlWindow::setupIcon() {
50 int x, y, w, h, ncols, nbytes, i;
51 unsigned int rgba[256];
52 unsigned int *icon;
53
54 if (sscanf(scummvm_icon[0], "%d %d %d %d", &w, &h, &ncols, &nbytes) != 4) {
55 warning("Wrong format of scummvm_icon[0] (%s)", scummvm_icon[0]);
56
57 return;
58 }
59 if ((w > 512) || (h > 512) || (ncols > 255) || (nbytes > 1)) {
60 warning("Could not load the built-in icon (%d %d %d %d)", w, h, ncols, nbytes);
61 return;
62 }
63 icon = (unsigned int*)malloc(w*h*sizeof(unsigned int));
64 if (!icon) {
65 warning("Could not allocate temp storage for the built-in icon");
66 return;
67 }
68
69 for (i = 0; i < ncols; i++) {
70 unsigned char code;
71 char color[32];
72 memset(color, 0, sizeof(color));
73 unsigned int col;
74 if (sscanf(scummvm_icon[1 + i], "%c c %s", &code, color) != 2) {
75 warning("Wrong format of scummvm_icon[%d] (%s)", 1 + i, scummvm_icon[1 + i]);
76 }
77 if (!strcmp(color, "None"))
78 col = 0x00000000;
79 else if (!strcmp(color, "black"))
80 col = 0xFF000000;
81 else if (color[0] == '#') {
82 if (sscanf(color + 1, "%06x", &col) != 1) {
83 warning("Wrong format of color (%s)", color + 1);
84 }
85 col |= 0xFF000000;
86 } else {
87 warning("Could not load the built-in icon (%d %s - %s) ", code, color, scummvm_icon[1 + i]);
88 free(icon);
89 return;
90 }
91
92 rgba[code] = col;
93 }
94 for (y = 0; y < h; y++) {
95 const char *line = scummvm_icon[1 + ncols + y];
96 for (x = 0; x < w; x++) {
97 icon[x + w * y] = rgba[(int)line[x]];
98 }
99 }
100
101 SDL_Surface *sdl_surf = SDL_CreateRGBSurfaceFrom(icon, w, h, 32, w * 4, 0xFF0000, 0x00FF00, 0x0000FF, 0xFF000000);
102 if (!sdl_surf) {
103 warning("SDL_CreateRGBSurfaceFrom(icon) failed");
104 }
105
106 #if SDL_VERSION_ATLEAST(2, 0, 0)
107 if (_window) {
108 SDL_SetWindowIcon(_window, sdl_surf);
109 }
110 #else
111 SDL_WM_SetIcon(sdl_surf, NULL);
112 #endif
113
114 SDL_FreeSurface(sdl_surf);
115 free(icon);
116 }
117
setWindowCaption(const Common::String & caption)118 void SdlWindow::setWindowCaption(const Common::String &caption) {
119 #if SDL_VERSION_ATLEAST(2, 0, 0)
120 _windowCaption = caption;
121 if (_window) {
122 SDL_SetWindowTitle(_window, caption.c_str());
123 }
124 #else
125 SDL_WM_SetCaption(caption.c_str(), caption.c_str());
126 #endif
127 }
128
toggleMouseGrab()129 void SdlWindow::toggleMouseGrab() {
130 #if SDL_VERSION_ATLEAST(2, 0, 0)
131 if (_window) {
132 _inputGrabState = !(SDL_GetWindowGrab(_window) == SDL_TRUE);
133 SDL_SetWindowGrab(_window, _inputGrabState ? SDL_TRUE : SDL_FALSE);
134 }
135 #else
136 if (SDL_WM_GrabInput(SDL_GRAB_QUERY) == SDL_GRAB_OFF) {
137 SDL_WM_GrabInput(SDL_GRAB_ON);
138 } else {
139 SDL_WM_GrabInput(SDL_GRAB_OFF);
140 }
141 #endif
142 }
143
hasMouseFocus() const144 bool SdlWindow::hasMouseFocus() const {
145 #if SDL_VERSION_ATLEAST(2, 0, 0)
146 if (_window) {
147 return (SDL_GetWindowFlags(_window) & SDL_WINDOW_MOUSE_FOCUS);
148 } else {
149 return false;
150 }
151 #else
152 return (SDL_GetAppState() & SDL_APPMOUSEFOCUS);
153 #endif
154 }
155
warpMouseInWindow(uint x,uint y)156 void SdlWindow::warpMouseInWindow(uint x, uint y) {
157 #if SDL_VERSION_ATLEAST(2, 0, 0)
158 if (_window && hasMouseFocus()) {
159 SDL_WarpMouseInWindow(_window, x, y);
160 }
161 #else
162 SDL_WarpMouse(x, y);
163 #endif
164 }
165
iconifyWindow()166 void SdlWindow::iconifyWindow() {
167 #if SDL_VERSION_ATLEAST(2, 0, 0)
168 if (_window) {
169 SDL_MinimizeWindow(_window);
170 }
171 #else
172 SDL_WM_IconifyWindow();
173 #endif
174 }
175
getSDLWMInformation(SDL_SysWMinfo * info) const176 bool SdlWindow::getSDLWMInformation(SDL_SysWMinfo *info) const {
177 SDL_VERSION(&info->version);
178 #if SDL_VERSION_ATLEAST(2, 0, 0)
179 return SDL_GetWindowWMInfo(_window, info);
180 #else
181 return SDL_GetWMInfo(info);
182 #endif
183 }
184
185 #if SDL_VERSION_ATLEAST(2, 0, 0)
copySDLSurface(SDL_Surface * src)186 SDL_Surface *copySDLSurface(SDL_Surface *src) {
187 const bool locked = SDL_MUSTLOCK(src) == SDL_TRUE;
188
189 if (locked) {
190 if (SDL_LockSurface(src) != 0) {
191 return nullptr;
192 }
193 }
194
195 SDL_Surface *res = SDL_CreateRGBSurfaceFrom(src->pixels,
196 src->w, src->h, src->format->BitsPerPixel,
197 src->pitch, src->format->Rmask, src->format->Gmask,
198 src->format->Bmask, src->format->Amask);
199
200 if (locked) {
201 SDL_UnlockSurface(src);
202 }
203
204 return res;
205 }
206
createOrUpdateWindow(int width,int height,uint32 flags)207 bool SdlWindow::createOrUpdateWindow(int width, int height, uint32 flags) {
208 if (_inputGrabState) {
209 flags |= SDL_WINDOW_INPUT_GRABBED;
210 }
211
212 // SDL_WINDOW_RESIZABLE can also be updated without recreating the window
213 // starting with SDL 2.0.5, but it is not treated as updateable here
214 // because:
215 // 1. It is currently only changed in conjunction with the SDL_WINDOW_OPENGL
216 // flag, so the window will always be recreated anyway when changing
217 // resizability; and
218 // 2. Users (particularly on Windows) will sometimes swap older SDL DLLs
219 // to avoid bugs, which would be impossible if the feature was enabled
220 // at compile time using SDL_VERSION_ATLEAST.
221 const uint32 updateableFlagsMask = fullscreenMask | SDL_WINDOW_INPUT_GRABBED;
222
223 const uint32 oldNonUpdateableFlags = _lastFlags & ~updateableFlagsMask;
224 const uint32 newNonUpdateableFlags = flags & ~updateableFlagsMask;
225
226 if (!_window || oldNonUpdateableFlags != newNonUpdateableFlags) {
227 destroyWindow();
228 _window = SDL_CreateWindow(_windowCaption.c_str(), _lastX,
229 _lastY, width, height, flags);
230 if (_window) {
231 setupIcon();
232 }
233 } else {
234 const uint32 fullscreenFlags = flags & fullscreenMask;
235
236 if (fullscreenFlags) {
237 SDL_DisplayMode fullscreenMode;
238 fullscreenMode.w = width;
239 fullscreenMode.h = height;
240 fullscreenMode.driverdata = nullptr;
241 fullscreenMode.format = 0;
242 fullscreenMode.refresh_rate = 0;
243 SDL_SetWindowDisplayMode(_window, &fullscreenMode);
244 } else {
245 SDL_SetWindowSize(_window, width, height);
246 }
247
248 SDL_SetWindowFullscreen(_window, fullscreenFlags);
249 SDL_SetWindowGrab(_window, (flags & SDL_WINDOW_INPUT_GRABBED) ? SDL_TRUE : SDL_FALSE);
250 }
251
252 if (!_window) {
253 return false;
254 }
255
256 #if defined(MACOSX)
257 // macOS windows with the flag SDL_WINDOW_FULLSCREEN_DESKTOP exiting their fullscreen space
258 // ignore the size set by SDL_SetWindowSize while they were in fullscreen mode.
259 // Instead, they revert back to their previous windowed mode size.
260 // This is a bug in SDL2: https://bugzilla.libsdl.org/show_bug.cgi?id=3719.
261 // TODO: Remove the call to SDL_SetWindowSize below once the SDL bug is fixed.
262
263 // In some cases at this point there may be a pending SDL resize event with the old size.
264 // This happens for example if we destroyed the window, or when switching between windowed
265 // and fullscreen modes. If we changed the window size here, this pending event will have the
266 // old (and incorrect) size. To avoid any issue we call SDL_SetWindowSize() to generate another
267 // resize event (SDL_WINDOWEVENT_SIZE_CHANGED) so that the last resize event we receive has
268 // the correct size. This fixes for exmample bug #9971: SDL2: Fullscreen to RTL launcher resolution
269 SDL_SetWindowSize(_window, width, height);
270 #endif
271
272 _lastFlags = flags;
273
274 return true;
275 }
276
destroyWindow()277 void SdlWindow::destroyWindow() {
278 if (_window) {
279 if (!(_lastFlags & fullscreenMask)) {
280 SDL_GetWindowPosition(_window, &_lastX, &_lastY);
281 }
282 SDL_DestroyWindow(_window);
283 _window = nullptr;
284 }
285 }
286 #endif
287