Adonthell  0.4
screen.cc
Go to the documentation of this file.
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 
36 using namespace std;
37 
38 #if !defined(HAVE_DECL_SDL_WINDOW_ALLOW_HIGHDPI) || HAVE_DECL_SDL_WINDOW_ALLOW_HIGHDPI == 0
39 #define SDL_WINDOW_ALLOW_HIGHDPI 0
40 #endif
41 
42 #ifndef HAVE_SDL_GETDISPLAYUSABLEBOUNDS
43 #define SDL_GetDisplayUsableBounds SDL_GetDisplayBounds
44 #endif
45 
47 u_int8 screen::bytes_per_pixel_ = 0;
48 u_int32 screen::trans = 0;
49 SDL_Window *screen::Window = NULL;
50 SDL_Renderer *screen::Renderer = NULL;
51 
52 u_int8 screen::mode_ = 0;
53 u_int8 screen::scale_;
54 SDL_Rect screen::clip_rect_ = {};
55 
56 void screen::cleanup()
57 {
58  if (Renderer) SDL_DestroyRenderer(Renderer);
59  if (Window) SDL_DestroyWindow(Window);
60 
61  Renderer = NULL;
62  Window = NULL;
63 }
64 
65 bool screen::init (u_int16 nl, u_int16 nh, u_int8 depth, const config & myconfig)
66 {
67  u_int8 screen = myconfig.display;
68  u_int8 screen_mode = myconfig.screen_mode;
69 
70 #if defined(SDL_VIDEO_DRIVER_X11) || defined(SDL_VIDEO_DRIVER_WAYLAND)
71  std::string wm_class = "SDL_VIDEO_X11_WMCLASS=" + myconfig.game_name;
72  putenv ((char *) wm_class.c_str ());
73 #endif
74 
75  if (SDL_Init (SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0)
76  {
77  std::cout << "Couldn't init SDL: " << SDL_GetError () << std::endl;
78  return false;
79  }
80 
81  SDL_SetHint (SDL_HINT_RENDER_SCALE_QUALITY, "nearest");
82 
83  int availableDisplays = SDL_GetNumVideoDisplays();
84  if (availableDisplays < 1)
85  {
86  std::cout << "Couldn't init screen: " << SDL_GetError () << std::endl;
87  return false;
88  }
89 
90  if (screen >= availableDisplays)
91  {
92  // if the requested display does not exist, pick the first one
93  screen = 0;
94  }
95 
96  if (screen_mode > 2)
97  {
98  // if the requested screen mode is invalid, fallback to window mode
99  screen_mode = 0;
100  }
101 
102  mode_ = screen_mode;
103  scale_ = get_scale_for_display(screen, nl, nh);
104 
105  // set window flags
106  unsigned int flags = SDL_WINDOW_ALLOW_HIGHDPI;
107  switch (screen_mode)
108  {
109  case 1:
110  {
111  flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
112  break;
113  }
114  case 2:
115  {
116  flags |= SDL_WINDOW_FULLSCREEN;
117  break;
118  }
119  }
120 
121  SDL_DisplayMode fullscreen_mode;
122  memset(&fullscreen_mode, 0, sizeof(SDL_DisplayMode));
123  fullscreen_mode.format = SDL_PIXELFORMAT_RGB888;
124 
125  nl *= scale_;
126  nh *= scale_;
127 
128  SDL_ShowCursor(SDL_DISABLE);
129 
130  Window = SDL_CreateWindow ("Adonthell", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, nl, nh, flags);
131  if (!Window)
132  {
133  std::cout << "Failed creating window: " << SDL_GetError() << std::endl;
134  return false;
135  }
136 #ifdef WIN32
137  const string icon_name = game::find_file("gfx/icon32.bmp");
138  if (!icon_name.empty())
139  {
140  SDL_Surface* icon = SDL_LoadBMP(icon_name.c_str());
141  if (icon != NULL)
142  {
143  SDL_SetWindowIcon(Window, icon);
144  SDL_FreeSurface(icon);
145  }
146  }
147 #endif
148  if (SDL_SetWindowDisplayMode(Window, &fullscreen_mode) < 0)
149  {
150  std::cout << "Failed setting display mode: " << SDL_GetError() << std::endl;
151  return false;
152  }
153  SDL_ShowWindow(Window);
154 
155  Renderer = SDL_CreateRenderer(Window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
156  if (!Renderer)
157  {
158  std::cout << "Failed creating accelerated renderer: " << SDL_GetError() << std::endl;
159  Renderer = SDL_CreateRenderer(Window, -1, SDL_RENDERER_SOFTWARE);
160  if (!Renderer)
161  {
162  std::cout << "Failed creating renderer: " << SDL_GetError() << std::endl;
163  return false;
164  }
165  }
166 
167  display.set_length(nl/scale_);
168  display.set_height(nh/scale_);
169 
170  // check if we have a HIGH_DPI window, in which case we need to update our scale
171  update_scale();
172 
173  // Setting up transparency color
174  trans = display.map_color(255, 0, 255, SDL_ALPHA_OPAQUE);
175 
176  return true;
177 }
178 
179 string screen::info ()
180 {
181  SDL_version version_info;
182  SDL_RendererInfo render_info;
183  std::ostringstream temp;
184 
185  SDL_GetVersion(&version_info);
186 
187  if (Renderer)
188  {
189  SDL_GetRendererInfo(Renderer, &render_info);
190  }
191  else
192  {
193  render_info.name = "not yet initialized";
194  render_info.flags = 0;
195  }
196 
197  temp << "Video information: " << std::endl
198  << "Platform: " << SDL_GetPlatform() << std::endl
199  << "Version: " << "SDL " <<(int) version_info.major << "." << (int) version_info.minor << "." << (int) version_info.patch << " " << SDL_GetRevision() << std::endl
200  << "Video driver used: " << SDL_GetCurrentVideoDriver() << std::endl
201  << "Renderer used: " << render_info.name << std::endl
202  << "HW Accelerated: " << ((render_info.flags & SDL_RENDERER_ACCELERATED) == SDL_RENDERER_ACCELERATED ? "Yes" : "No") << std::endl
203  << "Display Format: " << SDL_GetPixelFormatName (format()) << std::endl
204  << "Screen Size " << (int)length()*scale() << "x" << (int)height()*scale() << std::endl
205  << "Fullscreen: " << (mode() ? "Yes" : "No") << std::endl
206  << std::ends;
207 
208  return temp.str ();
209 }
210 
212 {
213  bool r = false;
214  if (mode_ != m)
215  {
216 #ifdef DEBUG
217  std::cout << "Switching from " << (int)mode_ << " to " << (int)m << std::endl;
218 #endif
219  if (mode_ != 0)
220  {
221  r = SDL_SetWindowFullscreen(Window, SDL_FALSE) == 0;
222  if (!r)
223  {
224  return false;
225  }
226  }
227 
228  // update mode before getting new scale
229  mode_ = m;
230 
231  u_int8 screen = SDL_GetWindowDisplayIndex(Window);
232  u_int8 new_scale = get_scale_for_display(screen, length(), height());
233  if (new_scale != scale())
234  {
235 #ifdef DEBUG
236  std::cout << "Scale changed from " << (int)scale_ << " to " << (int)new_scale << std::endl;
237 #endif
238  SDL_SetWindowSize(Window, length()*new_scale, height()*new_scale);
239  }
240 
241  switch(mode_)
242  {
243  case 0:
244  {
245  SDL_SetWindowPosition(Window, SDL_WINDOWPOS_CENTERED_DISPLAY(screen), SDL_WINDOWPOS_CENTERED_DISPLAY(screen));
246  break;
247  }
248  case 1:
249  {
250  r = SDL_SetWindowFullscreen(Window, SDL_WINDOW_FULLSCREEN_DESKTOP) == 0;
251  break;
252  }
253  case 2:
254  {
255  SDL_Rect bounds;
256  SDL_GetDisplayBounds(screen, &bounds);
257  SDL_SetWindowPosition(Window, bounds.x, bounds.y);
258  r = SDL_SetWindowFullscreen(Window, SDL_WINDOW_FULLSCREEN) == 0;
259  break;
260  }
261  }
262 
263  if (r)
264  {
265  update_scale();
266  }
267 
268  return r;
269  }
270  return 0;
271 }
272 
273 u_int8 screen::get_scale_for_display(u_int8 screen, u_int16 nl, u_int16 nh)
274 {
275  SDL_Rect bounds;
276  switch (mode_)
277  {
278  case 0:
279  {
280  // window mode
281  SDL_Delay(250);
282  SDL_GetDisplayUsableBounds(screen, &bounds);
283  break;
284  }
285  case 1:
286  {
287  // letterbox mode with aspect ratio preserved
288  SDL_GetDisplayBounds(screen, &bounds);
289  break;
290  }
291  case 2:
292  {
293  // fullscreen mode at 640x480
294  bounds.x = 0;
295  bounds.y = 0;
296  bounds.w = nl * 2;
297  bounds.h = nh * 2;
298  break;
299  }
300  }
301 
302  int scale_x = (bounds.w - bounds.x) / nl;
303  int scale_y = (bounds.h - bounds.y) / nh;
304 
305  return scale_x > scale_y ? scale_y : scale_x;
306 }
307 
308 void screen::update_scale()
309 {
310  int w, h;
311 
312  SDL_GetRendererOutputSize(Renderer, &w, &h);
313 
314  int scale_x = w / length();
315  int scale_y = h / height();
316 
317  scale_ = scale_x > scale_y ? scale_y : scale_x;
318 
319  if (mode_ == 1)
320  {
321  // center viewport in letterbox mode
322  clip_rect_.x = (w - length() * scale_) / 2;
323  clip_rect_.y = (h - height() * scale_) / 2;
324  clip_rect_.w = length() * scale_;
325  clip_rect_.h = height() * scale_;
326 
327  SDL_RenderSetClipRect(Renderer, &clip_rect_);
328  }
329  else
330  {
331  // no rendering offset required when running in window or fullscreen modes
332  clip_rect_.x = 0;
333  clip_rect_.y = 0;
334  clip_rect_.w = length() * scale_;
335  clip_rect_.h = height() * scale_;
336 
337  SDL_RenderSetClipRect(Renderer, NULL);
338  }
339 
340 #ifdef DEBUG
341  std::cout << "Mode = " << (int) mode_ << ", X = " << offset_x_ << ", Y = " << offset_y_
342  << ", Width = " << w << ", Height = " << h << ", Scale = "
343  << (int) (scale_) << std::endl;
344 #endif
345 }
346 
348 {
349  display.fillrect (0, 0, i, screen::height (), 0);
350  display.fillrect (screen::length () - i, 0, i, screen::height (), 0);
351  display.fillrect (0, 0, screen::length (), i, 0);
352  display.fillrect (0, screen::height () - i, screen::length (), i, 0);
353 }
#define u_int16
16 bits long unsigned integer
Definition: types.h:38
Class where drawables can actually be drawn to.
Definition: surface.h:85
Declares the screen class.
Definition: str_hash.h:71
Declares the game class.
static u_int16 length()
Returns the length of the screen.
Definition: screen.h:84
static string info()
Returns information about the current screen settings, suitable for being displayed to the user...
Definition: screen.cc:179
#define u_int32
32 bits long unsigned integer
Definition: types.h:41
#define u_int8
8 bits long unsigned integer
Definition: types.h:35
static bool init(u_int16 nl, u_int16 nh, u_int8 depth, const config &myconfig)
Initializes the video subsystem and creates the required resources.
Definition: screen.cc:65
static string find_file(const string &fname)
Finds a file in the directories hierarchy, starting searching from game_data_dir(), then global_data_dir() and finally user_data_dir().
Definition: game.cc:130
static bool set_fullscreen(const u_int8 &m)
Sets fullscreen/windowed mode.
Definition: screen.cc:211
Screen access is made through this class.
Definition: screen.h:48
u_int8 display
Index of the display to use for fullscreen mode.
Definition: prefs.h:147
string game_name
Name of the game that is running at present.
Definition: prefs.h:135
static void transition(u_int16 i)
Make a nice transition effect.
Definition: screen.cc:347
static surface display
The actual screen surface.
Definition: screen.h:70
This class contains the engine&#39;s configuration read either from the config file or from the command l...
Definition: prefs.h:74
static u_int16 height()
Returns the height of the screen.
Definition: screen.h:92
u_int8 screen_mode
Whether the engine shall run in window (0) or fullscreen (1) mode.
Definition: prefs.h:143