1//============================================================================
2//
3// SSSS tt lll lll
4// SS SS tt ll ll
5// SS tttttt eeee ll ll aaaa
6// SSSS tt ee ee ll ll aa
7// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
8// SS SS tt ee ll ll aa aa
9// SSSS ttt eeeee llll llll aaaaa
10//
11// Copyright (c) 1995-2019 by Bradford W. Mott, Stephen Anthony
12// and the Stella Team
13//
14// See the file "License.txt" for information on usage and redistribution of
15// this file, and for a DISCLAIMER OF ALL WARRANTIES.
16//============================================================================
17
18#ifndef FRAMEBUFFER_HXX
19#define FRAMEBUFFER_HXX
20
21#include <map>
22
23class OSystem;
24class Console;
25class Settings;
26class FBSurface;
27class TIASurface;
28
29namespace GUI {
30 class Font;
31}
32
33#include "Rect.hxx"
34#include "Variant.hxx"
35#include "TIAConstants.hxx"
36#include "FrameBufferConstants.hxx"
37#include "EventHandlerConstants.hxx"
38#include "bspf.hxx"
39
40/**
41 This class encapsulates all video buffers and is the basis for the video
42 display in Stella. All graphics ports should derive from this class for
43 platform-specific video stuff.
44
45 The TIA is drawn here, and all GUI elements (ala ScummVM, which are drawn
46 into FBSurfaces), are in turn drawn here as well.
47
48 @author Stephen Anthony
49*/
50class FrameBuffer
51{
52 public:
53 // Contains all relevant info for the dimensions of a video screen
54 // Also takes care of the case when the image should be 'centered'
55 // within the given screen:
56 // 'image' is the image dimensions into the screen
57 // 'screen' are the dimensions of the screen itself
58 struct VideoMode
59 {
60 enum class Stretch { Preserve, Fill, None };
61
62 Common::Rect image;
63 Common::Size screen;
64 Stretch stretch;
65 string description;
66 float zoom;
67 Int32 fsIndex;
68
69 VideoMode();
70 VideoMode(uInt32 iw, uInt32 ih, uInt32 sw, uInt32 sh, Stretch smode, float overscan = 1.0,
71 const string& desc = "", float zoomLevel = 1, Int32 fsindex = -1);
72
73 friend ostream& operator<<(ostream& os, const VideoMode& vm)
74 {
75 os << "image=" << vm.image << " screen=" << vm.screen
76 << " stretch=" << (vm.stretch == Stretch::Preserve ? "preserve" :
77 vm.stretch == Stretch::Fill ? "fill" : "none")
78 << " desc=" << vm.description << " zoom=" << vm.zoom
79 << " fsIndex= " << vm.fsIndex;
80 return os;
81 }
82 };
83
84 // Zoom level step interval
85 static constexpr float ZOOM_STEPS = 0.25;
86
87 public:
88 /**
89 Creates a new Frame Buffer
90 */
91 FrameBuffer(OSystem& osystem);
92 virtual ~FrameBuffer();
93
94 /**
95 Initialize the framebuffer object (set up the underlying hardware)
96 */
97 bool initialize();
98
99 /**
100 Set palette for user interface
101 */
102 void setUIPalette();
103
104 /**
105 (Re)creates the framebuffer display. This must be called before any
106 calls are made to derived methods.
107
108 @param title The title of the application / window
109 @param width The width of the framebuffer
110 @param height The height of the framebuffer
111 @param honourHiDPI If true, consult the 'hidpi' setting and enlarge
112 the display size accordingly; if false, use the
113 exact dimensions as given
114
115 @return Status of initialization (see FBInitStatus 'enum')
116 */
117 FBInitStatus createDisplay(const string& title, uInt32 width, uInt32 height,
118 bool honourHiDPI = true);
119
120 /**
121 Updates the display, which depending on the current mode could mean
122 drawing the TIA, any pending menus, etc.
123 */
124 void update(bool force = false);
125
126 /**
127 There is a dedicated update method for emulation mode.
128 */
129 void updateInEmulationMode(float framesPerSecond);
130
131 /**
132 Shows a message onscreen.
133
134 @param message The message to be shown
135 @param position Onscreen position for the message
136 @param force Force showing this message, even if messages are disabled
137 */
138 void showMessage(const string& message,
139 MessagePosition position = MessagePosition::BottomCenter,
140 bool force = false);
141
142 /**
143 Toggles showing or hiding framerate statistics.
144 */
145 void toggleFrameStats();
146
147 /**
148 Shows a message containing frame statistics for the current frame.
149 */
150 void showFrameStats(bool enable);
151
152 /**
153 Enable/disable any pending messages. Disabled messages aren't removed
154 from the message queue; they're just not redrawn into the framebuffer.
155 */
156 void enableMessages(bool enable);
157
158 /**
159 Reset 'Paused' display delay counter
160 */
161 void setPauseDelay();
162
163 /**
164 Allocate a new surface. The FrameBuffer class takes all responsibility
165 for freeing this surface (ie, other classes must not delete it directly).
166
167 @param w The requested width of the new surface.
168 @param h The requested height of the new surface.
169 @param data If non-null, use the given data values as a static surface
170
171 @return A pointer to a valid surface object, or nullptr.
172 */
173 shared_ptr<FBSurface> allocateSurface(int w, int h, const uInt32* data = nullptr);
174
175 /**
176 Returns the current dimensions of the framebuffer image.
177 Note that this will take into account the current scaling (if any)
178 as well as image 'centering'.
179 */
180 const Common::Rect& imageRect() const { return myImageRect; }
181
182 /**
183 Returns the current dimensions of the framebuffer window.
184 This is the entire area containing the framebuffer image as well as any
185 'unusable' area.
186 */
187 const Common::Size& screenSize() const { return myScreenSize; }
188 const Common::Rect& screenRect() const { return myScreenRect; }
189
190 /**
191 Returns the current dimensions of the users' desktop.
192 */
193 const Common::Size& desktopSize() const { return myDesktopSize; }
194
195 /**
196 Get the supported renderers for the video hardware.
197
198 @return An array of supported renderers
199 */
200 const VariantList& supportedRenderers() const { return myRenderers; }
201
202 /**
203 Get the minimum/maximum supported TIA zoom level (windowed mode)
204 for the framebuffer.
205 */
206 float supportedTIAMinZoom() const { return 2 * hidpiScaleFactor(); }
207 float supportedTIAMaxZoom() const { return myTIAMaxZoom; }
208
209 /**
210 Get the TIA surface associated with the framebuffer.
211 */
212 TIASurface& tiaSurface() const { return *myTIASurface; }
213
214 /**
215 Enables/disables fullscreen mode.
216 */
217 void setFullscreen(bool enable);
218
219 /**
220 Toggles between fullscreen and window mode.
221 */
222 void toggleFullscreen();
223
224 /**
225 Changes the fullscreen overscan.
226 direction = -1 means less overscan
227 direction = +1 means more overscan
228 */
229 void changeOverscan(int direction);
230
231 /**
232 This method is called when the user wants to switch to the next
233 available video mode. In windowed mode, this typically means going to
234 the next/previous zoom level. In fullscreen mode, this typically means
235 switching between normal aspect and fully filling the screen.
236 direction = -1 means go to the next lower video mode
237 direction = +1 means go to the next higher video mode
238
239 @param direction Described above
240 */
241 bool changeVidMode(int direction);
242
243 /**
244 Sets the state of the cursor (hidden or grabbed) based on the
245 current mode.
246 */
247 void setCursorState();
248
249 /**
250 Sets the use of grabmouse.
251 */
252 void enableGrabMouse(bool enable);
253
254 /**
255 Sets the use of grabmouse.
256 */
257 bool grabMouseEnabled() const { return myGrabMouse; }
258
259 /**
260 Toggles the use of grabmouse (only has effect in emulation mode).
261 */
262 void toggleGrabMouse();
263
264 /**
265 Set up the TIA/emulation palette for a screen of any depth > 8.
266
267 @param raw_palette The array of colors in R/G/B format
268 */
269 void setPalette(const uInt32* raw_palette);
270
271 /**
272 Informs the Framebuffer of a change in EventHandler state.
273 */
274 void stateChanged(EventHandlerState state);
275
276 /**
277 Answer whether hidpi mode is allowed. In this mode, all FBSurfaces
278 are scaled to 2x normal size.
279 */
280 bool hidpiAllowed() const { return myHiDPIAllowed; }
281
282 /**
283 Answer whether hidpi mode is enabled. In this mode, all FBSurfaces
284 are scaled to 2x normal size.
285 */
286 bool hidpiEnabled() const { return myHiDPIEnabled; }
287 uInt32 hidpiScaleFactor() const { return myHiDPIEnabled ? 2 : 1; }
288
289 #ifdef GUI_SUPPORT
290 /**
291 Get the font object(s) of the framebuffer
292 */
293 const GUI::Font& font() const { return *myFont; }
294 const GUI::Font& infoFont() const { return *myInfoFont; }
295 const GUI::Font& smallFont() const { return *mySmallFont; }
296 const GUI::Font& launcherFont() const { return *myLauncherFont; }
297 #endif
298
299 //////////////////////////////////////////////////////////////////////
300 // The following methods are system-specific and can/must be
301 // implemented in derived classes.
302 //////////////////////////////////////////////////////////////////////
303 public:
304 /**
305 Updates window title
306
307 @param title The title of the application / window
308 */
309 virtual void setTitle(const string& title) = 0;
310
311 /**
312 Shows or hides the cursor based on the given boolean value.
313 */
314 virtual void showCursor(bool show) = 0;
315
316 /**
317 Answers if the display is currently in fullscreen mode.
318 */
319 virtual bool fullScreen() const = 0;
320
321 /**
322 This method is called to retrieve the R/G/B data from the given pixel.
323
324 @param pixel The pixel containing R/G/B data
325 @param r The red component of the color
326 @param g The green component of the color
327 @param b The blue component of the color
328 */
329 virtual void getRGB(uInt32 pixel, uInt8* r, uInt8* g, uInt8* b) const = 0;
330
331 /**
332 This method is called to map a given R/G/B triple to the screen palette.
333
334 @param r The red component of the color.
335 @param g The green component of the color.
336 @param b The blue component of the color.
337 */
338 virtual uInt32 mapRGB(uInt8 r, uInt8 g, uInt8 b) const = 0;
339
340 /**
341 This method is called to get the specified ARGB data from the viewable
342 FrameBuffer area. Note that this isn't the same as any internal
343 surfaces that may be in use; it should return the actual data as it
344 is currently seen onscreen.
345
346 @param buffer The actual pixel data in ARGB8888 format
347 @param pitch The pitch (in bytes) for the pixel data
348 @param rect The bounding rectangle for the buffer
349 */
350 virtual void readPixels(uInt8* buffer, uInt32 pitch, const Common::Rect& rect) const = 0;
351
352 /**
353 This method is called to query the video hardware for the index
354 of the display the current window is displayed on
355
356 @return the current display index or a negative value if no
357 window is displayed
358 */
359 virtual Int32 getCurrentDisplayIndex() = 0;
360
361 /**
362 This method is called to preserve the last current windowed position.
363 */
364 virtual void updateWindowedPos() = 0;
365
366 /**
367 Clear the framebuffer.
368 */
369 virtual void clear() = 0;
370
371 protected:
372 /**
373 This method is called to query and initialize the video hardware
374 for desktop and fullscreen resolution information. Since several
375 monitors may be attached, we need the resolution for all of them.
376
377 @param fullscreenRes Maximum resolution supported in fullscreen mode
378 @param windowedRes Maximum resolution supported in windowed mode
379 @param renderers List of renderer names (internal name -> end-user name)
380 */
381 virtual void queryHardware(vector<Common::Size>& fullscreenRes,
382 vector<Common::Size>& windowedRes,
383 VariantList& renderers) = 0;
384
385 /**
386 This method is called to change to the given video mode.
387
388 @param title The title for the created window
389 @param mode The video mode to use
390
391 @return False on any errors, else true
392 */
393 virtual bool setVideoMode(const string& title,
394 const FrameBuffer::VideoMode& mode) = 0;
395
396 /**
397 This method is called to create a surface with the given attributes.
398
399 @param w The requested width of the new surface.
400 @param h The requested height of the new surface.
401 @param data If non-null, use the given data values as a static surface
402 */
403 virtual unique_ptr<FBSurface>
404 createSurface(uInt32 w, uInt32 h, const uInt32* data) const = 0;
405
406 /**
407 Calls 'free()' on all surfaces that the framebuffer knows about.
408 */
409 void freeSurfaces();
410
411 /**
412 Calls 'reload()' on all surfaces that the framebuffer knows about.
413 */
414 void reloadSurfaces();
415
416 /**
417 Grabs or ungrabs the mouse based on the given boolean value.
418 */
419 virtual void grabMouse(bool grab) = 0;
420
421 /**
422 Set the icon for the main window.
423 */
424 virtual void setWindowIcon() = 0;
425
426 /**
427 This method must be called after all drawing is done, and indicates
428 that the buffers should be pushed to the physical screen.
429 */
430 virtual void renderToScreen() = 0;
431
432 /**
433 This method is called to provide information about the FrameBuffer.
434 */
435 virtual string about() const = 0;
436
437 protected:
438 // The parent system for the framebuffer
439 OSystem& myOSystem;
440
441 // Color palette for TIA and UI modes
442 uInt32 myPalette[256+kNumColors];
443
444 private:
445 /**
446 Draw pending messages.
447
448 @return Indicates whether any changes actually occurred.
449 */
450 bool drawMessage();
451
452 /**
453 Frees and reloads all surfaces that the framebuffer knows about.
454 */
455 void resetSurfaces();
456
457 /**
458 Calculate the maximum level by which the base window can be zoomed and
459 still fit in the given screen dimensions.
460 */
461 float maxZoomForScreen(uInt32 baseWidth, uInt32 baseHeight,
462 uInt32 screenWidth, uInt32 screenHeight) const;
463
464 /**
465 Set all possible video modes (both windowed and fullscreen) available for
466 this framebuffer based on given image dimensions and maximum window size.
467 */
468 void setAvailableVidModes(uInt32 basewidth, uInt32 baseheight);
469
470 /**
471 Returns an appropriate video mode based on the current eventhandler
472 state, taking into account the maximum size of the window.
473
474 @param fullscreen Whether to use a windowed or fullscreen mode
475 @return A valid VideoMode for this framebuffer
476 */
477 const FrameBuffer::VideoMode& getSavedVidMode(bool fullscreen);
478
479 private:
480 /**
481 This class implements an iterator around an array of VideoMode objects.
482 */
483 class VideoModeList
484 {
485 public:
486 VideoModeList();
487 VideoModeList(const VideoModeList&) = default;
488 VideoModeList& operator=(const VideoModeList&) = default;
489
490 void add(const FrameBuffer::VideoMode& mode);
491 void clear();
492
493 bool empty() const;
494 uInt32 size() const;
495
496 void previous();
497 const FrameBuffer::VideoMode& current() const;
498 void next();
499
500 void setByZoom(float zoom);
501 void setByStretch(FrameBuffer::VideoMode::Stretch stretch);
502
503 friend ostream& operator<<(ostream& os, const VideoModeList& l)
504 {
505 for(const auto& vm: l.myModeList)
506 os << "-----\n" << vm << endl << "-----\n";
507 return os;
508 }
509
510 private:
511 vector<FrameBuffer::VideoMode> myModeList;
512 int myIdx;
513 };
514
515 protected:
516 // Title of the main window/screen
517 string myScreenTitle;
518
519 // Number of displays
520 int myNumDisplays;
521
522 // The resolution of the attached displays in fullscreen mode
523 // The primary display is typically the first in the array
524 // Windowed modes use myDesktopSize directly
525 vector<Common::Size> myFullscreenDisplays;
526
527 private:
528 // Draws the frame stats overlay
529 void drawFrameStats(float framesPerSecond);
530
531 // Indicates the number of times the framebuffer was initialized
532 uInt32 myInitializedCount;
533
534 // Used to set intervals between messages while in pause mode
535 Int32 myPausedCount;
536
537 // Dimensions of the actual image, after zooming, and taking into account
538 // any image 'centering'
539 Common::Rect myImageRect;
540
541 // Dimensions of the main window (not always the same as the image)
542 // Use 'size' version when only wxh are required
543 // Use 'rect' version when x/y, wxh are required
544 Common::Size myScreenSize;
545 Common::Rect myScreenRect;
546
547 // Maximum dimensions of the desktop area
548 // Note that this takes 'hidpi' mode into account, so in some cases
549 // it will be less than the absolute desktop size
550 Common::Size myDesktopSize;
551
552 // Maximum absolute dimensions of the desktop area
553 Common::Size myAbsDesktopSize;
554
555 // Supported renderers
556 VariantList myRenderers;
557
558 #ifdef GUI_SUPPORT
559 // The font object to use for the normal in-game GUI
560 unique_ptr<GUI::Font> myFont;
561
562 // The info font object to use for the normal in-game GUI
563 unique_ptr<GUI::Font> myInfoFont;
564
565 // The font object to use when space is very limited
566 unique_ptr<GUI::Font> mySmallFont;
567
568 // The font object to use for the ROM launcher
569 unique_ptr<GUI::Font> myLauncherFont;
570 #endif
571
572 // The TIASurface class takes responsibility for TIA rendering
573 unique_ptr<TIASurface> myTIASurface;
574
575 // Used for onscreen messages and frame statistics
576 // (scanline count and framerate)
577 struct Message {
578 string text;
579 int counter;
580 int x, y, w, h;
581 MessagePosition position;
582 ColorId color;
583 shared_ptr<FBSurface> surface;
584 bool enabled;
585
586 Message()
587 : counter(-1), x(0), y(0), w(0), h(0), position(MessagePosition::BottomCenter),
588 color(kNone), enabled(false) { }
589 };
590 Message myMsg;
591 Message myStatsMsg;
592 bool myStatsEnabled;
593 uInt32 myLastScanlines;
594
595 bool myGrabMouse;
596 bool myHiDPIAllowed;
597 bool myHiDPIEnabled;
598
599 // The list of all available video modes for this framebuffer
600 VideoModeList* myCurrentModeList;
601 VideoModeList myWindowedModeList;
602 vector<VideoModeList> myFullscreenModeLists;
603
604 // Maximum TIA zoom level that can be used for this framebuffer
605 float myTIAMaxZoom;
606
607 // Holds a reference to all the surfaces that have been created
608 vector<shared_ptr<FBSurface>> mySurfaceList;
609
610 // Holds UI palette data (standard and classic colours)
611 static uInt32 ourGUIColors[3][kNumColors-256];
612
613 private:
614 // Following constructors and assignment operators not supported
615 FrameBuffer() = delete;
616 FrameBuffer(const FrameBuffer&) = delete;
617 FrameBuffer(FrameBuffer&&) = delete;
618 FrameBuffer& operator=(const FrameBuffer&) = delete;
619 FrameBuffer& operator=(FrameBuffer&&) = delete;
620};
621
622#endif
623