1 /*
2 Copyright (C) 1999/2000/2001/2004 Alexandre Courbot
3 Copyright (C) 2016 Kai Sterker
4 Part of the Adonthell Project <http://adonthell.nongnu.org>
5
6 Adonthell is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 Adonthell is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with Adonthell. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 /**
21 * @file screen.cc
22 * @author Alexandre Courbot <alexandrecourbot@linuxgames.com>
23 * @author Kai Sterker
24 *
25 * @brief Defines the screen class.
26 *
27 *
28 */
29
30 #include <config.h>
31 #include "screen.h"
32 #include "game.h"
33 #include <iostream>
34 #include <sstream>
35 #include <algorithm>
36
37 using namespace std;
38
39 #if !defined(HAVE_DECL_SDL_WINDOW_ALLOW_HIGHDPI) || HAVE_DECL_SDL_WINDOW_ALLOW_HIGHDPI == 0
40 #define SDL_WINDOW_ALLOW_HIGHDPI 0
41 #endif
42
43 #ifndef HAVE_SDL_GETDISPLAYUSABLEBOUNDS
44 #define SDL_GetDisplayUsableBounds SDL_GetDisplayBounds
45 #endif
46
47 surface screen::display;
48 u_int8 screen::bytes_per_pixel_ = 0;
49 u_int32 screen::trans = 0;
50 SDL_Window *screen::Window = NULL;
51 SDL_Renderer *screen::Renderer = NULL;
52
53 u_int8 screen::mode_ = 0;
54 u_int8 screen::scale_;
55 SDL_Rect screen::clip_rect_ = {};
56 SDL_DisplayMode screen::fullscreen_mode = {};
57
cleanup()58 void screen::cleanup()
59 {
60 if (Renderer) SDL_DestroyRenderer(Renderer);
61 if (Window) SDL_DestroyWindow(Window);
62
63 Renderer = NULL;
64 Window = NULL;
65 }
66
init(u_int16 nl,u_int16 nh,u_int8 depth,const config & myconfig)67 bool screen::init (u_int16 nl, u_int16 nh, u_int8 depth, const config & myconfig)
68 {
69 u_int8 screen = myconfig.display;
70 u_int8 screen_mode = myconfig.screen_mode;
71
72 #if defined(SDL_VIDEO_DRIVER_X11) || defined(SDL_VIDEO_DRIVER_WAYLAND)
73 static std::string wm_class = "SDL_VIDEO_X11_WMCLASS=" + myconfig.game_name;
74 putenv ((char *) wm_class.c_str ());
75 #endif
76
77 #if defined(SDL_HINT_RENDER_BATCHING)
78 SDL_SetHint (SDL_HINT_RENDER_BATCHING, "1");
79 #endif
80
81 if (SDL_Init (SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_GAMECONTROLLER) < 0)
82 {
83 std::cout << "Couldn't init SDL: " << SDL_GetError () << std::endl;
84 return false;
85 }
86
87 SDL_SetHint (SDL_HINT_RENDER_SCALE_QUALITY, "nearest");
88
89 int availableDisplays = SDL_GetNumVideoDisplays();
90 if (availableDisplays < 1)
91 {
92 std::cout << "Couldn't init screen: " << SDL_GetError () << std::endl;
93 return false;
94 }
95
96 if (screen >= availableDisplays)
97 {
98 // if the requested display does not exist, pick the first one
99 screen = 0;
100 }
101
102 if (screen_mode > 2)
103 {
104 // if the requested screen mode is invalid, fallback to window mode
105 screen_mode = 0;
106 }
107
108 mode_ = screen_mode;
109 scale_ = get_scale_for_display(screen, nl, nh);
110
111 // set window flags
112 unsigned int flags = SDL_WINDOW_ALLOW_HIGHDPI;
113 switch (screen_mode)
114 {
115 case 1:
116 {
117 flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
118 break;
119 }
120 case 2:
121 {
122 flags |= SDL_WINDOW_FULLSCREEN;
123 break;
124 }
125 }
126
127 memset(&fullscreen_mode, 0, sizeof(SDL_DisplayMode));
128 fullscreen_mode.format = SDL_PIXELFORMAT_RGB888;
129
130 nl *= scale_;
131 nh *= scale_;
132
133 SDL_ShowCursor(SDL_DISABLE);
134
135 Window = SDL_CreateWindow ("Adonthell", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, nl, nh, flags);
136 if (!Window)
137 {
138 std::cout << "Failed creating window: " << SDL_GetError() << std::endl;
139 return false;
140 }
141 #ifdef WIN32
142 const string icon_name = game::find_file("gfx/icon32.bmp");
143 if (!icon_name.empty())
144 {
145 SDL_Surface* icon = SDL_LoadBMP(icon_name.c_str());
146 if (icon != NULL)
147 {
148 SDL_SetWindowIcon(Window, icon);
149 SDL_FreeSurface(icon);
150 }
151 }
152 #endif
153 if (SDL_SetWindowDisplayMode(Window, &fullscreen_mode) < 0)
154 {
155 std::cout << "Failed setting display mode: " << SDL_GetError() << std::endl;
156 return false;
157 }
158 SDL_ShowWindow(Window);
159
160 Renderer = SDL_CreateRenderer(Window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
161 if (!Renderer)
162 {
163 std::cout << "Failed creating accelerated renderer: " << SDL_GetError() << std::endl;
164 Renderer = SDL_CreateRenderer(Window, -1, SDL_RENDERER_SOFTWARE);
165 if (!Renderer)
166 {
167 std::cout << "Failed creating renderer: " << SDL_GetError() << std::endl;
168 return false;
169 }
170 }
171
172 display.set_length(nl/scale_);
173 display.set_height(nh/scale_);
174
175 // check if we have a HIGH_DPI window, in which case we need to update our scale
176 update_scale();
177
178 // Setting up transparency color
179 trans = display.map_color(255, 0, 255, SDL_ALPHA_OPAQUE);
180
181 return true;
182 }
183
info()184 string screen::info ()
185 {
186 SDL_version version_info;
187 SDL_RendererInfo render_info;
188 std::ostringstream temp;
189
190 SDL_GetVersion(&version_info);
191
192 if (Renderer)
193 {
194 SDL_GetRendererInfo(Renderer, &render_info);
195 }
196 else
197 {
198 render_info.name = "not yet initialized";
199 render_info.flags = 0;
200 }
201
202 temp << "Video information: " << std::endl
203 << "Platform: " << SDL_GetPlatform() << std::endl
204 << "Version: " << "SDL " <<(int) version_info.major << "." << (int) version_info.minor << "." << (int) version_info.patch << " " << SDL_GetRevision() << std::endl
205 << "Video driver used: " << SDL_GetCurrentVideoDriver() << std::endl
206 << "Renderer used: " << render_info.name << std::endl
207 << "HW Accelerated: " << ((render_info.flags & SDL_RENDERER_ACCELERATED) == SDL_RENDERER_ACCELERATED ? "Yes" : "No") << std::endl
208 << "Display Format: " << SDL_GetPixelFormatName (format()) << std::endl
209 << "Screen Size " << (int)length()*scale() << "x" << (int)height()*scale() << std::endl
210 << "Fullscreen: " << (mode() ? "Yes" : "No") << std::endl
211 << std::ends;
212
213 return temp.str ();
214 }
215
216 #ifdef DEBUG
get_mode_str(const u_int8 & m)217 static const char* get_mode_str(const u_int8 & m)
218 {
219 switch(m)
220 {
221 case 0: return "window";
222 case 1: return "letterbox";
223 case 2: return "fullscreen";
224 default: return "unknown";
225 }
226 }
227 #endif
228
set_fullscreen(const u_int8 & m)229 bool screen::set_fullscreen (const u_int8 & m)
230 {
231 bool r = false;
232 if (mode_ != m)
233 {
234 #ifdef DEBUG
235 std::cout << "Switching from " << get_mode_str(mode_) << " to " << get_mode_str(m) << std::endl;
236 #endif
237 if (mode_ != 0)
238 {
239 r = SDL_SetWindowFullscreen(Window, SDL_FALSE) == 0;
240 if (!r)
241 {
242 std::cout << "Failed to leave fullscreen mode: " << SDL_GetError() << std::endl;
243 return false;
244 }
245 }
246
247 // update mode before getting new scale
248 mode_ = m;
249
250 u_int8 screen = SDL_GetWindowDisplayIndex(Window);
251 u_int8 new_scale = get_scale_for_display(screen, length(), height());
252 if (new_scale != scale())
253 {
254 #ifdef DEBUG
255 std::cout << "Scale changed from " << (int)scale_ << " to " << (int)new_scale << std::endl;
256 #endif
257 SDL_SetWindowSize(Window, length()*new_scale, height()*new_scale);
258 }
259
260 switch(mode_)
261 {
262 case 0:
263 {
264 SDL_SetWindowPosition(Window, SDL_WINDOWPOS_CENTERED_DISPLAY(screen), SDL_WINDOWPOS_CENTERED_DISPLAY(screen));
265 break;
266 }
267 case 1:
268 {
269 r = SDL_SetWindowFullscreen(Window, SDL_WINDOW_FULLSCREEN_DESKTOP) == 0;
270 break;
271 }
272 case 2:
273 {
274 SDL_Rect bounds;
275 SDL_GetDisplayBounds(screen, &bounds);
276 SDL_SetWindowPosition(Window, bounds.x, bounds.y);
277 r = SDL_SetWindowFullscreen(Window, SDL_WINDOW_FULLSCREEN) == 0;
278 break;
279 }
280 }
281
282 if (r)
283 {
284 update_scale();
285 }
286 else
287 {
288 std::cout << "Failed to enter fullscreen mode: " << SDL_GetError() << std::endl;
289 }
290
291 return r;
292 }
293
294 return false;
295 }
296
get_scale_for_display(u_int8 screen,u_int16 nl,u_int16 nh)297 u_int8 screen::get_scale_for_display(u_int8 screen, u_int16 nl, u_int16 nh)
298 {
299 SDL_Rect bounds;
300 switch (mode_)
301 {
302 case 0:
303 {
304 // window mode
305 SDL_Delay(250);
306 SDL_GetDisplayUsableBounds(screen, &bounds);
307 break;
308 }
309 case 1:
310 {
311 // letterbox mode with aspect ratio preserved
312 SDL_GetDisplayBounds(screen, &bounds);
313 break;
314 }
315 case 2:
316 {
317 bounds.x = 0;
318 bounds.y = 0;
319 bounds.w = nl * 2;
320 bounds.h = nh * 2;
321
322 // fullscreen mode at highest supported multiple of 320x240
323 const int num_modes = SDL_GetNumDisplayModes(screen);
324 SDL_DisplayMode mode;
325 for (int i = 0; i < num_modes; ++i)
326 {
327 if (SDL_GetDisplayMode(screen, i, &mode) > -1)
328 {
329 if (mode.w % nl == 0 && mode.h % nh == 0)
330 {
331 bounds.w = mode.w;
332 bounds.h = mode.h;
333 break;
334 }
335 }
336 }
337 break;
338 }
339 }
340
341 int scale_x = bounds.w / nl;
342 int scale_y = bounds.h / nh;
343
344 return std::max(1, scale_x > scale_y ? scale_y : scale_x);
345 }
346
update_scale()347 void screen::update_scale()
348 {
349 int w, h;
350
351 if (SDL_GetRendererOutputSize(Renderer, &w, &h) == 0)
352 {
353 int scale_x = w / length();
354 int scale_y = h / height();
355
356 scale_ = scale_x > scale_y ? scale_y : scale_x;
357 }
358
359 if (mode_ == 1)
360 {
361 // center viewport in letterbox mode
362 clip_rect_.x = (w - length() * scale_) / 2;
363 clip_rect_.y = (h - height() * scale_) / 2;
364 clip_rect_.w = length() * scale_;
365 clip_rect_.h = height() * scale_;
366
367 SDL_RenderSetClipRect(Renderer, &clip_rect_);
368 }
369 else
370 {
371 // no rendering offset required when running in window or fullscreen modes
372 clip_rect_.x = 0;
373 clip_rect_.y = 0;
374 clip_rect_.w = length() * scale_;
375 clip_rect_.h = height() * scale_;
376
377 SDL_RenderSetClipRect(Renderer, NULL);
378 }
379
380 #ifdef DEBUG
381 std::cout << "Mode = " << get_mode_str(mode_) << ", X = " << offset_x() << ", Y = " << offset_y()
382 << ", Width = " << w << ", Height = " << h << ", Scale = "
383 << (int) (scale_) << std::endl;
384 #endif
385 }
386
transition(u_int16 i)387 void screen::transition (u_int16 i)
388 {
389 display.fillrect (0, 0, i, screen::height (), 0);
390 display.fillrect (screen::length () - i, 0, i, screen::height (), 0);
391 display.fillrect (0, 0, screen::length (), i, 0);
392 display.fillrect (0, screen::height () - i, screen::length (), i, 0);
393 }
394