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 | |
23 | class OSystem; |
24 | class Console; |
25 | class Settings; |
26 | class FBSurface; |
27 | class TIASurface; |
28 | |
29 | namespace 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 | */ |
50 | class 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 | |