1 /*
2    Copyright (C) 2003 - 2018 by David White <dave@whitevine.net>
3    Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4 
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2 of the License, or
8    (at your option) any later version.
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY.
11 
12    See the COPYING file for more details.
13 */
14 
15 #pragma once
16 
17 #include "events.hpp"
18 #include "exceptions.hpp"
19 #include "lua_jailbreak_exception.hpp"
20 
21 #include <memory>
22 
23 class surface;
24 struct point;
25 
26 namespace sdl
27 {
28 class window;
29 }
30 
31 class CVideo
32 {
33 public:
34 	CVideo(const CVideo&) = delete;
35 	CVideo& operator=(const CVideo&) = delete;
36 
37 	enum FAKE_TYPES { NO_FAKE, FAKE, FAKE_TEST };
38 
39 	CVideo(FAKE_TYPES type = NO_FAKE);
40 
41 	~CVideo();
42 
get_singleton()43 	static CVideo& get_singleton()
44 	{
45 		return *singleton_;
46 	}
47 
48 	static std::string video_settings_report();
49 
50 	/***** ***** ***** ***** Unit test-related functions ***** ***** ****** *****/
51 
52 	void make_fake();
53 
54 	/**
55 	 * Creates a fake frame buffer for the unit tests.
56 	 *
57 	 * @param width               The width of the buffer.
58 	 * @param height              The height of the buffer.
59 	 */
60 	void make_test_fake(const unsigned width = 1024, const unsigned height = 768);
61 
faked() const62 	bool faked() const
63 	{
64 		return fake_screen_;
65 	}
66 
67 	bool non_interactive() const;
68 
69 	/***** ***** ***** ***** Window-related functions ***** ***** ****** *****/
70 
71 	/** Initializes a new SDL window instance, taking into account any preiously saved states. */
72 	void init_window();
73 
74 	/** Returns a pointer to the underlying SDL window. */
75 	sdl::window* get_window();
76 
77 private:
78 	enum MODE_EVENT { TO_RES, TO_FULLSCREEN, TO_WINDOWED, TO_MAXIMIZED_WINDOW };
79 
80 	/**
81 	 * Sets the window's mode - ie, changing it to fullscreen, maximizing, etc.
82 	 *
83 	 * @param mode                The action to perform.
84 	 * @param size                The new window size. Utilized if @a mode is TO_RES.
85 	 */
86 	void set_window_mode(const MODE_EVENT mode, const point& size);
87 
88 public:
89 	void set_fullscreen(bool ison);
90 
91 	void toggle_fullscreen();
92 
93 	bool is_fullscreen() const;
94 
95 	bool set_resolution(const unsigned width, const unsigned height);
96 
97 	/**
98 	 * Set the window resolution.
99 	 *
100 	 * @param resolution          The new width and height.
101 	 *
102 	 * @returns                   Whether the resolution was successfully changed.
103 	 */
104 	bool set_resolution(const point& resolution);
105 
106 	point current_resolution();
107 
108 	/** Returns the list of available screen resolutions. */
109 	std::vector<point> get_available_resolutions(const bool include_current = false);
110 
111 	/**
112 	 * Returns the current window renderer area, either in pixels or screen coordinates.
113 	 *
114 	 * @param as_pixels           Whether to return the area in pixels (default true) or
115 	 *                            DPI-independent (DIP) screen coordinates.
116 	 */
117 	SDL_Rect screen_area(bool as_pixels = true) const;
118 
119 	/** Returns the window renderer width in pixels or screen coordinates. */
120 	int get_width(bool as_pixels = true) const;
121 
122 	/** Returns the window renderer height in pixels or in screen coordinates. */
123 	int get_height(bool as_pixels = true) const;
124 
125 	/** The current scale factor on High-DPI screens. */
126 	std::pair<float, float> get_dpi_scale_factor() const;
127 
128 	/**
129 	 * Tests whether the given flags are currently set on the SDL window.
130 	 *
131 	 * @param flags               The flags to test, OR'd together.
132 	 */
133 	bool window_has_flags(uint32_t flags) const;
134 
135 	/**
136 	 * Sets the title of the main window.
137 	 *
138 	 * @param title               The new title for the window.
139 	 */
140 	void set_window_title(const std::string& title);
141 
142 	/**
143 	 * Sets the icon of the main window.
144 	 *
145 	 * @param icon                The new icon for the window.
146 	 */
147 	void set_window_icon(surface& icon);
148 
current_refresh_rate() const149 	int current_refresh_rate() const
150 	{
151 		return refresh_rate_;
152 	}
153 
154 	/***** ***** ***** ***** Drawing functions ***** ***** ****** *****/
155 
156 	/**
157 	 * Draws a surface directly onto the screen framebuffer.
158 	 *
159 	 * @param x                   The x coordinate at which to draw.
160 	 * @param y                   The y coordinate at which to draw.
161 	 * @param surf                The surface to draw.
162 	 * @param srcrect             The area of the surface to draw. This defaults to nullptr,
163 	 *                            which implies the entire thing.
164 	 * @param clip_rect           The clippin rect. If not null, the surface will only be drawn
165 	 *                            within the bounds of the given rectangle.
166 	 */
167 	void blit_surface(int x, int y, surface surf, SDL_Rect* srcrect = nullptr, SDL_Rect* clip_rect = nullptr);
168 
169 	/** Renders the screen. Should normally not be called directly! */
170 	void flip();
171 
172 	/**
173 	 * Updates and ensures the framebuffer surface is valid.
174 	 * This needs to be invoked immediately after a resize event or the game will crash.
175 	 */
176 	void update_framebuffer();
177 
178 	/** Clear the screen contents */
179 	void clear_screen();
180 
181 	/** Returns a reference to the framebuffer. */
182 	surface& getSurface();
183 
184 	/**
185 	 * Stop the screen being redrawn. Anything that happens while the updates are locked will
186 	 * be hidden from the user's view.
187 	 *
188 	 * Note that this function is re-entrant, meaning that if lock_updates(true) is called twice,
189 	 * lock_updates(false) must be called twice to unlock updates.
190 	 */
191 	void lock_updates(bool value);
192 
193 	/** Whether the screen has been 'locked' or not. */
194 	bool update_locked() const;
195 
196 	void lock_flips(bool);
197 
198 	/***** ***** ***** ***** Help string functions ***** ***** ****** *****/
199 
200 	/**
201 	 * Displays a help string with the given text. A 'help string' is like a tooltip,
202 	 * but appears at the bottom of the screen so as to not be intrusive.
203 	 *
204 	 * @param str                 The text to display.
205 	 *
206 	 * @returns                   The handle id of the new help string.
207 	 */
208 	int set_help_string(const std::string& str);
209 
210 	/** Removes the help string with the given handle. */
211 	void clear_help_string(int handle);
212 
213 	/** Removes all help strings. */
214 	void clear_all_help_strings();
215 
216 	/***** ***** ***** ***** General utils ***** ***** ****** *****/
217 
218 	/** Waits a given number of milliseconds before returning. */
219 	static void delay(unsigned int milliseconds);
220 
221 	struct error : public game::error
222 	{
errorCVideo::error223 		error()
224 			: game::error("Video initialization failed")
225 		{
226 		}
227 	};
228 
229 	/** Type that can be thrown as an exception to quit to desktop. */
230 	class quit : public lua_jailbreak_exception
231 	{
232 	public:
quit()233 		quit()
234 			: lua_jailbreak_exception()
235 		{
236 		}
237 
238 	private:
239 		IMPLEMENT_LUA_JAILBREAK_EXCEPTION(quit)
240 	};
241 
242 private:
243 	static CVideo* singleton_;
244 
245 	/** The SDL window object. */
246 	std::unique_ptr<sdl::window> window;
247 
248 	/** Initializes the SDL video subsystem. */
249 	void initSDL();
250 
251 	// if there is no display at all, but we 'fake' it for clients
252 	bool fake_screen_;
253 
254 	/** Helper class to manage SDL events. */
255 	class video_event_handler : public events::sdl_handler
256 	{
257 	public:
handle_event(const SDL_Event &)258 		virtual void handle_event(const SDL_Event&)
259 		{
260 		}
261 
262 		virtual void handle_window_event(const SDL_Event& event);
263 
video_event_handler()264 		video_event_handler()
265 			: sdl_handler(false)
266 		{
267 		}
268 	};
269 
270 	video_event_handler event_handler_;
271 
272 	/** Curent ID of the help string. */
273 	int help_string_;
274 
275 	int updated_locked_;
276 	int flip_locked_;
277 	int refresh_rate_;
278 };
279 
280 /** An object which will lock the display for the duration of its lifetime. */
281 struct update_locker
282 {
update_lockerupdate_locker283 	update_locker(CVideo& v, bool lock = true)
284 		: video(v)
285 		, unlock(lock)
286 	{
287 		if(lock) {
288 			video.lock_updates(true);
289 		}
290 	}
291 
~update_lockerupdate_locker292 	~update_locker()
293 	{
294 		unlock_update();
295 	}
296 
unlock_updateupdate_locker297 	void unlock_update()
298 	{
299 		if(unlock) {
300 			video.lock_updates(false);
301 			unlock = false;
302 		}
303 	}
304 
305 private:
306 	CVideo& video;
307 	bool unlock;
308 };
309 
310 class flip_locker
311 {
312 public:
flip_locker(CVideo & video)313 	flip_locker(CVideo& video)
314 		: video_(video)
315 	{
316 		video_.lock_flips(true);
317 	}
318 
~flip_locker()319 	~flip_locker()
320 	{
321 		video_.lock_flips(false);
322 	}
323 
324 private:
325 	CVideo& video_;
326 };
327 
328 namespace video2
329 {
330 class draw_layering : public events::sdl_handler
331 {
332 protected:
333 	draw_layering(const bool auto_join = true);
334 	virtual ~draw_layering();
335 };
336 
337 void trigger_full_redraw();
338 }
339