aboutsummaryrefslogtreecommitdiff
path: root/backends/graphics/windowed.h
diff options
context:
space:
mode:
Diffstat (limited to 'backends/graphics/windowed.h')
-rw-r--r--backends/graphics/windowed.h313
1 files changed, 313 insertions, 0 deletions
diff --git a/backends/graphics/windowed.h b/backends/graphics/windowed.h
new file mode 100644
index 0000000000..b3e5b832b3
--- /dev/null
+++ b/backends/graphics/windowed.h
@@ -0,0 +1,313 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef BACKENDS_GRAPHICS_WINDOWED_H
+#define BACKENDS_GRAPHICS_WINDOWED_H
+
+#include "backends/graphics/graphics.h"
+#include "common/frac.h"
+#include "common/rect.h"
+#include "common/textconsole.h"
+#include "graphics/scaler/aspect.h"
+
+class WindowedGraphicsManager : virtual public GraphicsManager {
+public:
+ WindowedGraphicsManager() :
+ _windowWidth(0),
+ _windowHeight(0),
+ _overlayVisible(false),
+ _forceRedraw(false),
+ _cursorX(0),
+ _cursorY(0),
+ _cursorNeedsRedraw(false) {}
+
+ virtual void showOverlay() override {
+ if (_overlayVisible)
+ return;
+
+ _activeArea.drawRect = _overlayDrawRect;
+ _activeArea.width = getOverlayWidth();
+ _activeArea.height = getOverlayHeight();
+ _overlayVisible = true;
+ _forceRedraw = true;
+ }
+
+ virtual void hideOverlay() override {
+ if (!_overlayVisible)
+ return;
+
+ _activeArea.drawRect = _gameDrawRect;
+ _activeArea.width = getWidth();
+ _activeArea.height = getHeight();
+ _overlayVisible = false;
+ _forceRedraw = true;
+ }
+
+protected:
+ /**
+ * @returns whether or not the game screen must have aspect ratio correction
+ * applied for correct rendering.
+ */
+ virtual bool gameNeedsAspectRatioCorrection() const = 0;
+
+ /**
+ * Backend-specific implementation for updating internal surfaces that need
+ * to reflect the new window size.
+ */
+ virtual void handleResizeImpl(const int width, const int height) = 0;
+
+ /**
+ * Converts the given point from the active virtual screen's coordinate
+ * space to the window's coordinate space (i.e. game-to-window or
+ * overlay-to-window).
+ */
+ Common::Point convertVirtualToWindow(const int x, const int y) const {
+ const int targetX = _activeArea.drawRect.left;
+ const int targetY = _activeArea.drawRect.top;
+ const int targetWidth = _activeArea.drawRect.width();
+ const int targetHeight = _activeArea.drawRect.height();
+ const int sourceWidth = _activeArea.width;
+ const int sourceHeight = _activeArea.height;
+
+ if (sourceWidth == 0 || sourceHeight == 0) {
+ error("convertVirtualToWindow called without a valid draw rect");
+ }
+
+ return Common::Point(targetX + x * targetWidth / sourceWidth,
+ targetY + y * targetHeight / sourceHeight);
+ }
+
+ /**
+ * Converts the given point from the window's coordinate space to the
+ * active virtual screen's coordinate space (i.e. window-to-game or
+ * window-to-overlay).
+ */
+ Common::Point convertWindowToVirtual(int x, int y) const {
+ const int sourceX = _activeArea.drawRect.left;
+ const int sourceY = _activeArea.drawRect.top;
+ const int sourceMaxX = _activeArea.drawRect.right - 1;
+ const int sourceMaxY = _activeArea.drawRect.bottom - 1;
+ const int sourceWidth = _activeArea.drawRect.width();
+ const int sourceHeight = _activeArea.drawRect.height();
+ const int targetWidth = _activeArea.width;
+ const int targetHeight = _activeArea.height;
+
+ if (sourceWidth == 0 || sourceHeight == 0) {
+ error("convertWindowToVirtual called without a valid draw rect");
+ }
+
+ x = CLIP<int>(x, sourceX, sourceMaxX);
+ y = CLIP<int>(y, sourceY, sourceMaxY);
+
+ return Common::Point(((x - sourceX) * targetWidth) / sourceWidth,
+ ((y - sourceY) * targetHeight) / sourceHeight);
+ }
+
+ /**
+ * @returns the desired aspect ratio of the game surface.
+ */
+ frac_t getDesiredGameAspectRatio() const {
+ if (getHeight() == 0 || gameNeedsAspectRatioCorrection()) {
+ return intToFrac(4) / 3;
+ }
+
+ return intToFrac(getWidth()) / getHeight();
+ }
+
+ /**
+ * Called after the window has been updated with new dimensions.
+ *
+ * @param width The new width of the window, excluding window decoration.
+ * @param height The new height of the window, excluding window decoration.
+ */
+ void handleResize(const int width, const int height) {
+ _windowWidth = width;
+ _windowHeight = height;
+ handleResizeImpl(width, height);
+ }
+
+ /**
+ * Recalculates the display areas for the game and overlay surfaces within
+ * the window.
+ */
+ virtual void recalculateDisplayAreas() {
+ if (_windowHeight == 0) {
+ return;
+ }
+
+ const frac_t outputAspect = intToFrac(_windowWidth) / _windowHeight;
+
+ populateDisplayAreaDrawRect(getDesiredGameAspectRatio(), outputAspect, _gameDrawRect);
+
+ if (getOverlayHeight()) {
+ const frac_t overlayAspect = intToFrac(getOverlayWidth()) / getOverlayHeight();
+ populateDisplayAreaDrawRect(overlayAspect, outputAspect, _overlayDrawRect);
+ }
+
+ if (_overlayVisible) {
+ _activeArea.drawRect = _overlayDrawRect;
+ _activeArea.width = getOverlayWidth();
+ _activeArea.height = getOverlayHeight();
+ } else {
+ _activeArea.drawRect = _gameDrawRect;
+ _activeArea.width = getWidth();
+ _activeArea.height = getHeight();
+ }
+ }
+ /**
+ * Sets the position of the hardware mouse cursor in the host system,
+ * relative to the window.
+ *
+ * @param x X coordinate in window coordinates.
+ * @param y Y coordinate in window coordinates.
+ */
+ virtual void setSystemMousePosition(const int x, const int y) = 0;
+
+ /**
+ * Move ("warp") the mouse cursor to the specified position.
+ *
+ * @param x The new X position of the mouse in virtual screen coordinates.
+ * @param y The new Y position of the mouse in virtual screen coordinates.
+ */
+ void warpMouse(const int x, const int y) {
+ // Check active coordinate instead of window coordinate to avoid warping
+ // the mouse if it is still within the same virtual pixel
+ const Common::Point virtualCursor = convertWindowToVirtual(_cursorX, _cursorY);
+ if (virtualCursor.x != x || virtualCursor.y != y) {
+ // Warping the mouse in SDL generates a mouse movement event, so
+ // `setMousePosition` would be called eventually through the
+ // `notifyMousePosition` callback if we *only* set the system mouse
+ // position here. However, this can cause problems with some games.
+ // For example, the cannon script in CoMI calls to warp the mouse
+ // twice each time the cannon is reloaded, and unless we update the
+ // mouse position immediately, the second call is ignored, which
+ // causes the cannon to change its aim.
+ const Common::Point windowCursor = convertVirtualToWindow(x, y);
+ setMousePosition(windowCursor.x, windowCursor.y);
+ setSystemMousePosition(windowCursor.x, windowCursor.y);
+ }
+ }
+
+ /**
+ * Sets the position of the rendered mouse cursor in the window.
+ *
+ * @param x X coordinate in window coordinates.
+ * @param y Y coordinate in window coordinates.
+ */
+ void setMousePosition(int x, int y) {
+ if (_cursorX != x || _cursorY != y) {
+ _cursorNeedsRedraw = true;
+ }
+
+ _cursorX = x;
+ _cursorY = y;
+ }
+
+ /**
+ * The width of the window, excluding window decoration.
+ */
+ int _windowWidth;
+
+ /**
+ * The height of the window, excluding window decoration.
+ */
+ int _windowHeight;
+
+ /**
+ * Whether the overlay (i.e. launcher, including the out-of-game launcher)
+ * is visible or not.
+ */
+ bool _overlayVisible;
+
+ /**
+ * The scaled draw rectangle for the game surface within the window.
+ */
+ Common::Rect _gameDrawRect;
+
+ /**
+ * The scaled draw rectangle for the overlay (launcher) surface within the
+ * window.
+ */
+ Common::Rect _overlayDrawRect;
+
+ /**
+ * Data about the display area of a virtual screen.
+ */
+ struct DisplayArea {
+ /**
+ * The scaled area where the virtual screen is drawn within the window.
+ */
+ Common::Rect drawRect;
+
+ /**
+ * The width of the virtual screen's unscaled coordinate space.
+ */
+ int width;
+
+ /**
+ * The height of the virtual screen's unscaled coordinate space.
+ */
+ int height;
+ };
+
+ /**
+ * Display area information about the currently active virtual screen. This
+ * will be the overlay screen when the overlay is active, and the game
+ * screen otherwise.
+ */
+ DisplayArea _activeArea;
+
+ /**
+ * Whether the screen must be redrawn on the next frame.
+ */
+ bool _forceRedraw;
+
+ /**
+ * Whether the mouse cursor needs to be redrawn on the next frame.
+ */
+ bool _cursorNeedsRedraw;
+
+ /**
+ * The position of the mouse cursor, in window coordinates.
+ */
+ int _cursorX, _cursorY;
+
+private:
+ void populateDisplayAreaDrawRect(const frac_t inputAspect, const frac_t outputAspect, Common::Rect &drawRect) const {
+ int width = _windowWidth;
+ int height = _windowHeight;
+
+ // Maintain aspect ratios
+ if (outputAspect < inputAspect) {
+ height = intToFrac(width) / inputAspect;
+ } else if (outputAspect > inputAspect) {
+ width = fracToInt(height * inputAspect);
+ }
+
+ drawRect.left = (_windowWidth - width) / 2;
+ drawRect.top = (_windowHeight - height) / 2;
+ drawRect.setWidth(width);
+ drawRect.setHeight(height);
+ }
+};
+
+#endif