[Scummvm-git-logs] scummvm master -> b398c8b1942442a47cfada35d6cce8d3f54943c0

neuromancer noreply at scummvm.org
Tue Oct 20 11:27:23 UTC 2026


This automated email contains information about 4 new commits which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .

Summary:
9d816efb68 COLONY: corrected mac b&w intro
6b97d8529e COLONY: avoid fractional scaling in widescreen
fbe4c3482f COLONY: avoid camera of getting too close to objects
b398c8b194 COLONY: show UI when player interacts with an animation


Commit: 9d816efb68101cc8a94b9af8a0946f153c160634
    https://github.com/scummvm/scummvm/commit/9d816efb68101cc8a94b9af8a0946f153c160634
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-10-20T13:26:51+02:00

Commit Message:
COLONY: corrected mac b&w intro

Changed paths:
    engines/colony/intro.cpp


diff --git a/engines/colony/intro.cpp b/engines/colony/intro.cpp
index d65a727e5ad..44ce75aba05 100644
--- a/engines/colony/intro.cpp
+++ b/engines/colony/intro.cpp
@@ -645,12 +645,12 @@ bool ColonyEngine::scrollInfo(const Graphics::Font *macFont) {
 
 	// Set up gradient palette entries (200-213) for story text
 	// Mac original: tColor.blue starts at 0xFFFF and decreases by 4096 per visible line
-	// B&W Mac: white gradient instead of blue
-	const bool bwMac = (macFont && !isMacColorMode());
+	// B&W Mac uses solid white text.
+	const bool bwMac = isMacRenderMode() && !isMacColorMode();
 	byte pal[14 * 3]; // storyLength entries
 	memset(pal, 0, sizeof(pal));
 	for (int i = 0; i < storyLength; i++) {
-		int val = 255 - i * 16;
+		int val = bwMac ? 255 : 255 - i * 16;
 		if (val < 0)
 			val = 0;
 		pal[i * 3 + 0] = bwMac ? val : 0;  // R
@@ -881,10 +881,11 @@ bool ColonyEngine::makeStars(const Common::Rect &r, int btn) {
 
 bool ColonyEngine::makeBlackHole() {
 	// Original: Makeblackhole() in intro.c
-	// Mac original draws spiral lines with fading colors:
+	// Color Mac original draws spiral lines with fading colors:
 	// bcolor starts at (0,0,0) and subtracts (rd=2048, gd=1024, bd=4096) per step,
 	// which wraps around creating shifting color gradients.
-	// We use palette entries 128-191 for the gradient colors.
+	// Palette entry 128 holds the current pen color.
+	const bool bwMac = isMacRenderMode() && !isMacColorMode();
 	int centerX = _width / 2;
 	int centerY = _height / 2;
 	int dec = 16;
@@ -922,7 +923,11 @@ bool ColonyEngine::makeBlackHole() {
 			uint8 palB = (colorB >> 8) & 0xFF;
 
 			// Use palette entry 128 for current step color
-			byte pal[3] = { palR, palG, palB };
+			byte pal[3] = {
+				(byte)(bwMac ? 255 : palR),
+				(byte)(bwMac ? 255 : palG),
+				(byte)(bwMac ? 255 : palB)
+			};
 			_gfx->setPalette(pal, 128, 1);
 
 			starcnt++;
@@ -1322,8 +1327,8 @@ bool ColonyEngine::explodePlanet() {
 bool ColonyEngine::timeSquare(const Common::String &str, const Graphics::Font *macFont, bool gameOver) {
 	// Original: TimeSquare() in intro.c
 	// Mac and DOS use different presentation here. DOS is a monochrome/gray
-	// warning band with 16-pixel blits and white text; Mac uses the colorful
-	// gradient treatment.
+	// warning band with 16-pixel blits and white text; color Mac uses the
+	// gradient treatment, while B&W Mac uses thin, dithered borders.
 
 	_gfx->clear(_gfx->black());
 
@@ -1344,19 +1349,19 @@ bool ColonyEngine::timeSquare(const Common::String &str, const Graphics::Font *m
 	const uint32 textIndex = 176;
 	const Common::Rect textBand(0, centery + 1, _width, centery + 16);
 
-	if (macStyle) {
+	if (macStyle && !bwMac) {
 		// Mac original: blue starts at 0xFFFF and decreases by 4096 per line pair.
 		for (int i = 0; i < 16; i++) {
 			int val = 255 - i * 16; // 255, 239, 223, ... 15
 			if (val < 0)
 				val = 0;
-			byte pal[3] = { (byte)(bwMac ? val : 0), (byte)(bwMac ? val : 0), (byte)val };
+			byte pal[3] = { 0, 0, (byte)val };
 			_gfx->setPalette(pal, 160 + i, 1);
 		}
 
-		// Text is red in color, white in B&W.
+		// Color Mac uses red text.
 		{
-			byte pal[3] = { 255, (byte)(bwMac ? 255 : 0), (byte)(bwMac ? 255 : 0) };
+			byte pal[3] = { 255, 0, 0 };
 			_gfx->setPalette(pal, textIndex, 1);
 		}
 
@@ -1366,6 +1371,17 @@ bool ColonyEngine::timeSquare(const Common::String &str, const Graphics::Font *m
 			_gfx->fillRect(Common::Rect(0, centery - 3 - i * 2, _width, centery - 1 - i * 2), 160 + i);
 			_gfx->fillRect(Common::Rect(0, centery + 16 + i * 2, _width, centery + 18 + i * 2), 160 + i);
 		}
+	} else if (bwMac) {
+		const byte whitePal[3] = { 255, 255, 255 };
+		_gfx->setPalette(whitePal, textIndex, 1);
+
+		// Original B&W TimeSquare: white borders with QuickDraw gray middle rows.
+		_gfx->fillRect(Common::Rect(0, centery - 3, _width, centery), textIndex);
+		_gfx->fillRect(Common::Rect(0, centery + 17, _width, centery + 20), textIndex);
+		for (int x = centery & 1; x < _width; x += 2) {
+			_gfx->setPixel(x, centery - 2, _gfx->black());
+			_gfx->setPixel(x, centery + 18, _gfx->black());
+		}
 	} else {
 		// DOS warning band: white outer lines, gray inner lines, white text.
 		const byte grayPal[3] = { 170, 170, 170 };


Commit: 6b97d8529ecf1e6a38c18dedfc2febc708de0243
    https://github.com/scummvm/scummvm/commit/6b97d8529ecf1e6a38c18dedfc2febc708de0243
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-10-20T13:26:51+02:00

Commit Message:
COLONY: avoid fractional scaling in widescreen

Changed paths:
    engines/colony/animation.cpp
    engines/colony/colony.cpp
    engines/colony/colony.h
    engines/colony/gfx.cpp
    engines/colony/renderer.h
    engines/colony/renderer_opengl.cpp
    engines/colony/renderer_opengl_shaders.cpp
    graphics/macgui/macmenu.cpp
    graphics/macgui/macmenu.h


diff --git a/engines/colony/animation.cpp b/engines/colony/animation.cpp
index 8c3ea9e87aa..ea19afa29d7 100644
--- a/engines/colony/animation.cpp
+++ b/engines/colony/animation.cpp
@@ -602,6 +602,13 @@ void ColonyEngine::playAnimation() {
 	uint32 lastDrawTime = 0;
 	bool needsDraw = true;
 	while (_animationRunning && !shouldQuit()) {
+		if (updateScreenSize()) {
+			_animExitPressed = _animExitInside = false;
+			_coderPressed = -1;
+			_coderPressInside = false;
+			drawAnimation();
+			needsDraw = true;
+		}
 		Common::Event event;
 		while (_system->getEventManager()->pollEvent(event)) {
 			if (event.type == Common::EVENT_QUIT || event.type == Common::EVENT_RETURN_TO_LAUNCHER) {
@@ -609,6 +616,12 @@ void ColonyEngine::playAnimation() {
 				break;
 			} else if (event.type == Common::EVENT_SCREEN_CHANGED) {
 				_gfx->computeScreenViewport();
+				if (updateScreenSize()) {
+					_animExitPressed = _animExitInside = false;
+					_coderPressed = -1;
+					_coderPressInside = false;
+				}
+				drawAnimation();
 				needsDraw = true;
 			} else if (event.type == Common::EVENT_LBUTTONDOWN) {
 				const Common::Point pt = eventMouseToLogical(event.mouse);
diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 47320c98dbd..31231e01a10 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -321,6 +321,32 @@ void ColonyEngine::warpMouseLogical(int x, int y) {
 	_system->warpMouse(p.x, p.y);
 }
 
+bool ColonyEngine::updateScreenSize() {
+	if (isMacRenderMode()) {
+		const Common::Point size = computeMacCanvasSize(_system->getWidth(), _system->getHeight(), _widescreen);
+		if (size.x != _width || size.y != _height) {
+			_width = size.x;
+			_height = size.y;
+			_gfx->setCanvasSize(_width, _height);
+			const bool menuWasActive = _wm && _wm->isMenuActive();
+			if (_wm) {
+				_macMenu->closeMenu();
+				_menuSurface->create(_width, _height, _gfx->getPixelFormat());
+				_wm->setScreen(_menuSurface);
+				_macMenu->resize(_width, _height);
+			}
+			updateViewportLayout();
+			_mousePos = eventMouseToLogical(_system->getEventManager()->getMousePos());
+			if (menuWasActive)
+				updateMouseCapture(true);
+			else if (_mouseLocked && !_animationRunning)
+				warpMouseLogical(_centerX, _centerY);
+			return true;
+		}
+	}
+	return false;
+}
+
 void ColonyEngine::pauseEngineIntern(bool pause) {
 	if (pause && _gfx && _level >= 1 && _level <= 7) {
 		if (_savedScreen) {
@@ -811,9 +837,8 @@ Common::Error ColonyEngine::run() {
 		_sound->init();
 	}
 
-	// Original Mac Colony: rScreen capped at 640x480 (inits.c lines 111-112).
-	// DOS EGA: 640x350 with non-square pixels displayed at 4:3.
-	// Mac uses square pixels at native 640x480.
+	// DOS keeps its 640x350 EGA canvas. Mac starts at 640x480 and adapts
+	// its layout to the window once the graphics backend is initialized.
 	if (isMacRenderMode()) {
 		_width = 640;
 		_height = 480;
@@ -833,7 +858,9 @@ Common::Error ColonyEngine::run() {
 	if (!_gfx)
 		return Common::kUserCanceled;
 
+	_gfx->setSquarePixelViewport(isMacRenderMode());
 	loadMacColors();
+	updateScreenSize();
 	updateViewportLayout();
 	const Graphics::PixelFormat format = _system->getScreenFormat();
 	debugC(1, kColonyDebugRender, "Screen format: %d bytesPerPixel. Actual size: %dx%d", format.bytesPerPixel, _width, _height);
@@ -896,6 +923,8 @@ Common::Error ColonyEngine::run() {
 	uint32 lastCenterTick = lastMoveTick;
 	while (!shouldQuit()) {
 		_frameLimiter->startFrame();
+		// Modal screens keep their layout stable until the next full redraw.
+		updateScreenSize();
 
 		auto qlog = [](int32 x) -> int {
 			int i = 0;
@@ -989,6 +1018,7 @@ Common::Error ColonyEngine::run() {
 				return Common::kNoError;
 			} else if (event.type == Common::EVENT_SCREEN_CHANGED) {
 				_gfx->computeScreenViewport();
+				updateScreenSize();
 			} else if (event.type == Common::EVENT_CUSTOM_ENGINE_ACTION_START) {
 				switch (event.customType) {
 				case kActionMoveForward:
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 429a8abe7a2..5638c1cb675 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -810,6 +810,7 @@ private:
 	void updateMouseCapture(bool recenter = true);
 	Common::Point getAimPoint() const;
 	void updateViewportLayout();
+	bool updateScreenSize();
 	void drawDashboardStep1();
 	void drawDashboardMac();
 	void drawDOSBarGraph(int x, int y, int height);
diff --git a/engines/colony/gfx.cpp b/engines/colony/gfx.cpp
index aa96483488b..784ff1cffb7 100644
--- a/engines/colony/gfx.cpp
+++ b/engines/colony/gfx.cpp
@@ -58,6 +58,48 @@ Common::Point canvasToWindow(const Common::Rect &viewport, const Common::Point &
 		viewport.top + (int)((int64)p.y * viewport.height() / canvasH));
 }
 
+Common::Rect computeScreenViewport(OSystem *system, int width, int height, bool squarePixels) {
+	const int screenWidth = system->getWidth();
+	const int screenHeight = system->getHeight();
+	int viewportWidth = screenWidth;
+	int viewportHeight = screenHeight;
+
+	// Fractional enlargement makes bitmap fonts and dithering uneven.
+	if (squarePixels) {
+		const int scale = MIN(screenWidth / width, screenHeight / height);
+		if (scale >= 1) {
+			viewportWidth = width * scale;
+			viewportHeight = height * scale;
+		} else {
+			// Fit smaller windows without cropping the UI.
+			viewportWidth = MIN(screenWidth, (screenHeight * width + height / 2) / height);
+			viewportHeight = MIN(screenHeight, (screenWidth * height + width / 2) / width);
+		}
+	} else if (ConfMan.getBool("widescreen_mod")) {
+		viewportWidth = MIN(screenWidth, (screenHeight * 16 + 4) / 9);
+		viewportHeight = MIN(screenHeight, (screenWidth * 9 + 8) / 16);
+	} else if (system->getFeatureState(OSystem::kFeatureAspectRatioCorrection)) {
+		viewportWidth = MIN(screenWidth, screenHeight * 4 / 3);
+		viewportHeight = MIN(screenHeight, screenWidth * 3 / 4);
+	}
+
+	Common::Rect viewport(viewportWidth, viewportHeight);
+	viewport.translate((screenWidth - viewportWidth) / 2, (screenHeight - viewportHeight) / 2);
+	return viewport;
+}
+
+Common::Point computeMacCanvasSize(int screenWidth, int screenHeight, bool widescreen) {
+	if (!widescreen) {
+		const int width = MIN(screenWidth, screenHeight * 4 / 3);
+		screenHeight = MIN(screenHeight, screenWidth * 3 / 4);
+		screenWidth = width;
+	}
+
+	// Grow the layout to fill the window while keeping bitmap pixels uniform.
+	const int scale = MAX(1, MIN(screenWidth / 640, screenHeight / 480));
+	return Common::Point(MAX(640, screenWidth / scale), MAX(480, screenHeight / scale));
+}
+
 // Pick the renderer type. Honors --renderer=<code> on the command line /
 // ConfMan key, restricted to what was compiled in.
 //
diff --git a/engines/colony/renderer.h b/engines/colony/renderer.h
index 47ed2aae323..95488fa4896 100644
--- a/engines/colony/renderer.h
+++ b/engines/colony/renderer.h
@@ -77,6 +77,7 @@ public:
 	virtual void setFeatureClipX(int left, int right) {}
 	virtual void clearFeatureClipX() {}
 	virtual void setSquarePixelViewport(bool enable) = 0;
+	virtual void setCanvasSize(int width, int height) = 0;
 	virtual void computeScreenViewport() = 0;
 
 	// Window-pixel rect the logical canvas is currently drawn into.
@@ -102,6 +103,9 @@ protected:
 Common::Point windowToCanvas(const Common::Rect &viewport, const Common::Point &p, int canvasW, int canvasH);
 Common::Point canvasToWindow(const Common::Rect &viewport, const Common::Point &p, int canvasW, int canvasH);
 
+Common::Rect computeScreenViewport(OSystem *system, int width, int height, bool squarePixels);
+Common::Point computeMacCanvasSize(int screenWidth, int screenHeight, bool widescreen);
+
 // Factory function (follows Freescape pattern: picks best available renderer)
 Renderer *createRenderer(OSystem *system, int width, int height);
 
diff --git a/engines/colony/renderer_opengl.cpp b/engines/colony/renderer_opengl.cpp
index 88312b5807a..89040b34113 100644
--- a/engines/colony/renderer_opengl.cpp
+++ b/engines/colony/renderer_opengl.cpp
@@ -25,7 +25,6 @@
  *
  */
 
-#include "common/config-manager.h"
 #include "common/system.h"
 #include "graphics/font.h"
 #include "graphics/surface.h"
@@ -118,6 +117,7 @@ public:
 		applyScissorRect(_active3DViewport);
 	}
 	void setSquarePixelViewport(bool enable) override;
+	void setCanvasSize(int width, int height) override;
 	void computeScreenViewport() override;
 	void drawSurface(const Graphics::Surface *surf, int x, int y) override;
 	Graphics::Surface *getScreenshot() override;
@@ -127,9 +127,7 @@ public:
 
 private:
 	void useColor(uint32 color);
-	// Set glLineWidth scaled to window/logical ratio. Mirrors Freescape
-	// (gfx_opengl_shaders.cpp:692). Cached so back-to-back same-width
-	// line draws don't re-issue the GL call.
+	// Scale line widths with the canvas, excluding letterboxing.
 	void applyLineWidthForLines();
 	float _lineWidth = 1.0f;
 	GLuint _overlayTexId = 0;
@@ -201,10 +199,7 @@ void OpenGLRenderer::useColor(uint32 color) {
 }
 
 void OpenGLRenderer::applyLineWidthForLines() {
-	const int sysW = _system ? _system->getWidth() : 0;
-	float w = 1.0f;
-	if (sysW > _width)
-		w = MAX(1.0f, (float)sysW / (float)_width);
+	const float w = MAX(1.0f, (float)_screenViewport.width() / _width);
 	if (w != _lineWidth) {
 		glLineWidth(w);
 		_lineWidth = w;
@@ -583,46 +578,24 @@ void OpenGLRenderer::setSquarePixelViewport(bool enable) {
 }
 
 void OpenGLRenderer::computeScreenViewport() {
-	int32 screenWidth = _system->getWidth();
-	int32 screenHeight = _system->getHeight();
-
-	bool widescreen = ConfMan.getBool("widescreen_mod");
-
-	if (_squarePixelViewport) {
-		// Animation bitmaps use square pixels. Fit the uncorrected logical
-		// canvas so their authored 416x264 geometry matches the Mac version.
-		const int32 viewportWidth = MIN<int32>(screenWidth,
-			(screenHeight * _width + _height / 2) / _height);
-		const int32 viewportHeight = MIN<int32>(screenHeight,
-			(screenWidth * _height + _width / 2) / _width);
-		_screenViewport = Common::Rect(viewportWidth, viewportHeight);
-		_screenViewport.translate((screenWidth - viewportWidth) / 2,
-			(screenHeight - viewportHeight) / 2);
-	} else if (widescreen) {
-		// Widescreen is a 16:9 presentation even when fullscreen uses a
-		// taller or wider display. Keep it centered instead of stretching it.
-		const int32 viewportWidth = MIN<int32>(screenWidth, (screenHeight * 16 + 4) / 9);
-		const int32 viewportHeight = MIN<int32>(screenHeight, (screenWidth * 9 + 8) / 16);
-		_screenViewport = Common::Rect(viewportWidth, viewportHeight);
-		_screenViewport.translate((screenWidth - viewportWidth) / 2,
-			(screenHeight - viewportHeight) / 2);
-	} else if (_system->getFeatureState(OSystem::kFeatureAspectRatioCorrection)) {
-		// Aspect ratio correction (4:3)
-		int32 viewportWidth = MIN<int32>(screenWidth, screenHeight * 4 / 3);
-		int32 viewportHeight = MIN<int32>(screenHeight, screenWidth * 3 / 4);
-		_screenViewport = Common::Rect(viewportWidth, viewportHeight);
-
-		// Pillarboxing/Letterboxing
-		_screenViewport.translate((screenWidth - viewportWidth) / 2,
-			(screenHeight - viewportHeight) / 2);
-	} else {
-		_screenViewport = Common::Rect(screenWidth, screenHeight);
-	}
+	const int screenHeight = _system->getHeight();
+	_screenViewport = Colony::computeScreenViewport(_system, _width, _height, _squarePixelViewport);
 
 	glViewport(_screenViewport.left, screenHeight - _screenViewport.bottom, _screenViewport.width(), _screenViewport.height());
 	glScissor(_screenViewport.left, screenHeight - _screenViewport.bottom, _screenViewport.width(), _screenViewport.height());
 }
 
+void OpenGLRenderer::setCanvasSize(int width, int height) {
+	_width = width;
+	_height = height;
+	computeScreenViewport();
+	glMatrixMode(GL_PROJECTION);
+	glLoadIdentity();
+	glOrtho(0, _width, _height, 0, -1, 1);
+	glMatrixMode(GL_MODELVIEW);
+	glLoadIdentity();
+}
+
 void OpenGLRenderer::drawEllipse(int x, int y, int rx, int ry, uint32 color) {
 	GLint savedViewport[4];
 	GLint savedScissor[4];
@@ -697,10 +670,14 @@ void OpenGLRenderer::fillEllipse(int x, int y, int rx, int ry, uint32 color) {
 void OpenGLRenderer::fillDitherRect(const Common::Rect &rect, uint32 color1, uint32 color2) {
 	fillRect(rect, color1);
 	useColor(color2);
-	glBegin(GL_POINTS);
+	// Each checker covers a logical pixel, even on a scaled viewport.
+	glBegin(GL_QUADS);
 	for (int y = rect.top; y < rect.bottom; y++) {
 		for (int x = rect.left + (y % 2); x < rect.right; x += 2) {
 			glVertex2i(x, y);
+			glVertex2i(x + 1, y);
+			glVertex2i(x + 1, y + 1);
+			glVertex2i(x, y + 1);
 		}
 	}
 	glEnd();
diff --git a/engines/colony/renderer_opengl_shaders.cpp b/engines/colony/renderer_opengl_shaders.cpp
index 5bc655fced7..28767b69da6 100644
--- a/engines/colony/renderer_opengl_shaders.cpp
+++ b/engines/colony/renderer_opengl_shaders.cpp
@@ -26,7 +26,7 @@
  */
 
 #include "common/scummsys.h"
-#include "common/config-manager.h"
+#include "common/array.h"
 #include "common/debug.h"
 #include "common/system.h"
 #include "common/textconsole.h"
@@ -84,6 +84,7 @@ public:
 	void clearFeatureClipX() override;
 	void applyScissorRect(const Common::Rect &logical);
 	void setSquarePixelViewport(bool enable) override;
+	void setCanvasSize(int width, int height) override;
 	void computeScreenViewport() override;
 
 	void drawSurface(const Graphics::Surface *surf, int x, int y) override;
@@ -102,9 +103,7 @@ private:
 	void uploadSolid(const float *positions, int vertCount);
 	void drawSolid(GLenum mode, const float *positions, int vertCount, const float rgba[4]);
 	void drawTexturedQuad(int x, int y, int w, int h);
-	// Set glLineWidth to scale with the window size (Freescape pattern,
-	// gfx_opengl_shaders.cpp:692). No-op when mode isn't a line primitive.
-	// Cached to avoid redundant state changes across same-width draws.
+	// Scale line widths with the canvas, excluding letterboxing.
 	void applyLineWidth(GLenum mode);
 
 	void uploadSolid3D(const float *positions, int vertCount);
@@ -175,9 +174,7 @@ private:
 	bool _stipplePatternDirty = false;
 	bool _stippleColorsDirty = true;
 
-	// Solid VBO holds vec2 position only. Sized for the worst-case 2D
-	// primitive — the dither overlay can stream up to width*height/2 dots,
-	// so leave room for typical screens (≈170k floats for an 800×600 split).
+	// Initial 2D buffer size; uploadSolid reallocates it for each draw.
 	enum { kSolidVertexCapacity = 320 * 1024 };
 	// 3D VBO holds vec3 positions. Corridor polygons are typically <16
 	// vertices but a few features (sprite billboards, stairs) push higher.
@@ -333,15 +330,7 @@ void OpenGLShaderRenderer::uploadSolid(const float *positions, int vertCount) {
 void OpenGLShaderRenderer::applyLineWidth(GLenum mode) {
 	if (mode != GL_LINES && mode != GL_LINE_STRIP && mode != GL_LINE_LOOP)
 		return;
-	// Scale by window pixel width versus engine logical width — mirrors
-	// Freescape (gfx_opengl_shaders.cpp:692), MAX(1, ...) for safety.
-	// With kSupportsArbitraryResolutions, _system->getWidth() returns the
-	// overlay (window) pixel width; without it, getWidth() == _width and
-	// the ratio is 1.
-	const int sysW = _system ? _system->getWidth() : 0;
-	float w = 1.0f;
-	if (sysW > _width)
-		w = MAX(1.0f, (float)sysW / (float)_width);
+	const float w = MAX(1.0f, (float)_screenViewport.width() / _width);
 	if (w != _lineWidth) {
 		glLineWidth(w);
 		_lineWidth = w;
@@ -499,24 +488,26 @@ void OpenGLShaderRenderer::fillDitherRect(const Common::Rect &rect, uint32 c1, u
 	if (w <= 0 || h <= 0)
 		return;
 
-	// 50% checkerboard: place a dot on every other pixel, alternating per row.
-	// Capacity guard — fall back to solid c2 if the rect is larger than our
-	// streaming buffer (very rare: only the dashboard background hits this
-	// path, and it is much smaller than kSolidVertexCapacity).
-	const int maxDots = kSolidVertexCapacity;
-	int dots = 0;
-	float *verts = new float[maxDots * 2];
-	for (int yi = 0; yi < h && dots < maxDots; yi++) {
-		const int yy = rect.top + yi;
-		for (int xi = (yi & 1); xi < w && dots < maxDots; xi += 2) {
-			verts[dots * 2 + 0] = (float)(rect.left + xi);
-			verts[dots * 2 + 1] = (float)yy;
-			dots++;
+	// GL_POINTS stay one framebuffer pixel wide. Use logical pixel cells
+	// so the checkerboard retains its 50% coverage at every scale.
+	Common::Array<float> verts;
+	verts.reserve(((w + 1) / 2) * h * 12);
+	for (int y = rect.top; y < rect.bottom; y++) {
+		for (int x = rect.left + (y & 1); x < rect.right; x += 2) {
+			const float cell[] = {
+				(float)x,       (float)y,
+				(float)(x + 1), (float)y,
+				(float)x,       (float)(y + 1),
+				(float)(x + 1), (float)y,
+				(float)(x + 1), (float)(y + 1),
+				(float)x,       (float)(y + 1)
+			};
+			for (uint i = 0; i < ARRAYSIZE(cell); i++)
+				verts.push_back(cell[i]);
 		}
 	}
-	if (dots > 0)
-		drawSolid(GL_POINTS, verts, dots, rgba);
-	delete[] verts;
+	if (!verts.empty())
+		drawSolid(GL_TRIANGLES, &verts[0], verts.size() / 2, rgba);
 }
 
 // ---------------------------------------------------------------------------
@@ -641,34 +632,8 @@ void OpenGLShaderRenderer::setSquarePixelViewport(bool enable) {
 }
 
 void OpenGLShaderRenderer::computeScreenViewport() {
-	const int32 screenWidth = _system->getWidth();
-	const int32 screenHeight = _system->getHeight();
-	const bool widescreen = ConfMan.getBool("widescreen_mod");
-
-	if (_squarePixelViewport) {
-		// Animation bitmaps use square pixels. Fit the uncorrected logical
-		// canvas so their authored 416x264 geometry matches the Mac version.
-		const int32 vpW = MIN<int32>(screenWidth,
-			(screenHeight * _width + _height / 2) / _height);
-		const int32 vpH = MIN<int32>(screenHeight,
-			(screenWidth * _height + _width / 2) / _width);
-		_screenViewport = Common::Rect(vpW, vpH);
-		_screenViewport.translate((screenWidth - vpW) / 2, (screenHeight - vpH) / 2);
-	} else if (widescreen) {
-		// Widescreen is a 16:9 presentation even when fullscreen uses a
-		// taller or wider display. Keep it centered instead of stretching it.
-		const int32 vpW = MIN<int32>(screenWidth, (screenHeight * 16 + 4) / 9);
-		const int32 vpH = MIN<int32>(screenHeight, (screenWidth * 9 + 8) / 16);
-		_screenViewport = Common::Rect(vpW, vpH);
-		_screenViewport.translate((screenWidth - vpW) / 2, (screenHeight - vpH) / 2);
-	} else if (_system->getFeatureState(OSystem::kFeatureAspectRatioCorrection)) {
-		const int32 vpW = MIN<int32>(screenWidth, screenHeight * 4 / 3);
-		const int32 vpH = MIN<int32>(screenHeight, screenWidth * 3 / 4);
-		_screenViewport = Common::Rect(vpW, vpH);
-		_screenViewport.translate((screenWidth - vpW) / 2, (screenHeight - vpH) / 2);
-	} else {
-		_screenViewport = Common::Rect(screenWidth, screenHeight);
-	}
+	const int screenHeight = _system->getHeight();
+	_screenViewport = Colony::computeScreenViewport(_system, _width, _height, _squarePixelViewport);
 
 	glViewport(_screenViewport.left, screenHeight - _screenViewport.bottom,
 		_screenViewport.width(), _screenViewport.height());
@@ -681,6 +646,14 @@ void OpenGLShaderRenderer::copyToScreen() {
 	_system->updateScreen();
 }
 
+void OpenGLShaderRenderer::setCanvasSize(int width, int height) {
+	_width = width;
+	_height = height;
+	computeScreenViewport();
+	rebuildProjection();
+	uploadProjectionUniform();
+}
+
 Graphics::Surface *OpenGLShaderRenderer::getScreenshot() {
 	Graphics::Surface *surface = new Graphics::Surface();
 	surface->create(_screenViewport.width(), _screenViewport.height(),
diff --git a/graphics/macgui/macmenu.cpp b/graphics/macgui/macmenu.cpp
index 16f0b1de21c..d0f07424bd4 100644
--- a/graphics/macgui/macmenu.cpp
+++ b/graphics/macgui/macmenu.cpp
@@ -589,6 +589,14 @@ void MacMenu::calcDimensions() {
 	_dimensionsDirty = false;
 }
 
+void MacMenu::resize(int width, int height) {
+	_composeSurface->create(width, height, _wm->_pixelformat);
+	_tempSurface.create(width, _font->getFontHeight(), _wm->_pixelformat);
+	_bbox.right = width;
+	_dimensionsDirty = true;
+	_contentIsDirty = true;
+}
+
 void MacMenu::loadMenuResource(Common::MacResManager *resFork, uint16 id) {
 	Common::SeekableReadStream *res = resFork->getResource(MKTAG('M', 'E', 'N', 'U'), id);
 	assert(res);
diff --git a/graphics/macgui/macmenu.h b/graphics/macgui/macmenu.h
index 5e56ff27aa8..7acaaaf7cee 100644
--- a/graphics/macgui/macmenu.h
+++ b/graphics/macgui/macmenu.h
@@ -111,6 +111,7 @@ public:
 
 	void addStaticMenus(const MacMenuData *data);
 	void calcDimensions();
+	void resize(int width, int height);
 
 	int numberOfMenus();
 	int numberOfMenuItems(MacMenuItem *menu);


Commit: fbe4c3482f5c5d60eceaa1bfe42e713890fbe874
    https://github.com/scummvm/scummvm/commit/fbe4c3482f5c5d60eceaa1bfe42e713890fbe874
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-10-20T13:26:51+02:00

Commit Message:
COLONY: avoid camera of getting too close to objects

Changed paths:
    engines/colony/colony.h
    engines/colony/render.cpp
    engines/colony/render_features.cpp
    engines/colony/render_objects.cpp
    engines/colony/renderer.h
    engines/colony/renderer_opengl.cpp
    engines/colony/renderer_opengl_shaders.cpp


diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 5638c1cb675..2d878c831af 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -668,6 +668,7 @@ private:
 	int _direction = 0;
 
 	float _eyeDepthPull = 0.0f; // world units the eye parts are pulled at the camera
+	Common::Point _renderCamera;
 	// think.c: the snoop's snout bobs while it hunts (sniff/csniff).
 	int _snoopSnoutZ = 0;
 	int _snoopSniff = 5;
@@ -729,6 +730,8 @@ private:
 	bool drawStaticObjectPrisms3D(Thing &obj);
 	void initRobots();
 	void renderCorridor3D();
+	void computeRenderCamera();
+	void separateRenderCameraFromWall(int x, int y, uint8 bit, int separation, bool featuresVisible);
 	void drawWallFeatures3D();
 	bool isWallFeatureFacingCamera(int cellX, int cellY, int direction) const;
 	void drawWallFeature3D(int cellX, int cellY, int direction);
@@ -736,6 +739,7 @@ private:
 	void getWallFace3D(int cellX, int cellY, int direction, float corners[4][3]);
 	bool isVisibleRecessFeature(int x, int y, int direction) const;
 	bool wallSegmentIsOpenWell(int x, int y, uint8 bit) const;
+	bool wallSegmentIsRendered(int x, int y, uint8 bit) const;
 	void getWallRecess3D(const float corners[4][3], float farC[4][3]) const;
 	void recessPoint(const float nearC[4][3], const float farC[4][3], float u, float v, float depth, float out[3]) const;
 	void recessLine(const float nearC[4][3], const float farC[4][3], float u1, float v1, float d1,
diff --git a/engines/colony/render.cpp b/engines/colony/render.cpp
index 66fdb975f44..cf92834b9a8 100644
--- a/engines/colony/render.cpp
+++ b/engines/colony/render.cpp
@@ -322,7 +322,7 @@ void projectCorridorPointClamped(const Common::Rect &screenR, int look, int look
 	const float eyeX = side;
 	const float eyeY = dz * cosPitch + forward * sinPitch;
 	const float eyeZ = dz * sinPitch - forward * cosPitch;
-	const float depth = MAX(-eyeZ, 1.0f);
+	const float depth = MAX(-eyeZ, kProjectionNearClip);
 
 	const float centerX = screenR.left + screenR.width() * 0.5f;
 	const float centerY = screenR.top + screenR.height() * 0.5f;
@@ -455,6 +455,11 @@ bool ColonyEngine::wallSegmentIsOpenWell(int x, int y, uint8 bit) const {
 	return isVisibleRecessFeature(x, y, kDirWest) || isVisibleRecessFeature(x - 1, y, kDirEast);
 }
 
+bool ColonyEngine::wallSegmentIsRendered(int x, int y, uint8 bit) const {
+	return x >= 0 && x < 32 && y >= 0 && y < 32 &&
+		(_wall[x][y] & bit) && !wallSegmentIsOpenWell(x, y, bit);
+}
+
 const uint8 *ColonyEngine::mapFeatureAt(int x, int y, int direction) const {
 	if (x < 0 || x >= 31 || y < 0 || y >= 31 || direction < 0 || direction >= 5)
 		return nullptr;
@@ -481,7 +486,7 @@ bool projectCorridorPoint(const Common::Rect &screenR, uint8 look, int8 lookY,
 	const float eyeX = side;
 	const float eyeY = dz * cosPitch + forward * sinPitch;
 	const float eyeZ = dz * sinPitch - forward * cosPitch;
-	if (eyeZ >= -1.0f)
+	if (eyeZ >= -kProjectionNearClip)
 		return false;
 
 	const float centerX = screenR.left + screenR.width() * 0.5f;
@@ -517,14 +522,14 @@ void ColonyEngine::draw3DPrism(Thing &obj, const PrismPartDef &def, bool useLook
 		transformedX[i] = (float)(rx + obj.where.xloc);
 		transformedY[i] = (float)(ry + obj.where.yloc);
 		transformedZ[i] = (float)(oz - 160);
-		projectCorridorPointClamped(_screenR, _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc,
+		projectCorridorPointClamped(_screenR, _me.look, _me.lookY, _sint, _cost, _renderCamera.x, _renderCamera.y,
 			transformedX[i], transformedY[i], transformedZ[i],
 			projectedX[i], projectedY[i]);
 
 		if (accumulateBounds) {
 			int sx = 0;
 			int sy = 0;
-			if (projectCorridorPoint(_screenR, _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc,
+			if (projectCorridorPoint(_screenR, _me.look, _me.lookY, _sint, _cost, _renderCamera.x, _renderCamera.y,
 				transformedX[i], transformedY[i], transformedZ[i], sx, sy)) {
 				obj.where.xmn = MIN(obj.where.xmn, sx);
 				obj.where.xmx = MAX(obj.where.xmx, sx);
@@ -721,14 +726,14 @@ void ColonyEngine::pullTowardCamera(float *px, float *py, float *pz, int count)
 	if (_eyeDepthPull <= 0.0f)
 		return;
 	for (int i = 0; i < count; i++) {
-		const float dx = px[i] - (float)_me.xloc;
-		const float dy = py[i] - (float)_me.yloc;
+		const float dx = px[i] - (float)_renderCamera.x;
+		const float dy = py[i] - (float)_renderCamera.y;
 		const float d = sqrtf(dx * dx + dy * dy + pz[i] * pz[i]);
 		if (d <= _eyeDepthPull)
 			continue;
 		const float k = (d - _eyeDepthPull) / d;
-		px[i] = (float)_me.xloc + dx * k;
-		py[i] = (float)_me.yloc + dy * k;
+		px[i] = (float)_renderCamera.x + dx * k;
+		py[i] = (float)_renderCamera.y + dy * k;
 		pz[i] *= k;
 	}
 }
@@ -769,9 +774,9 @@ void ColonyEngine::draw3DSphere(Thing &obj, int pt0x, int pt0y, int pt0z,
 	// Billboard turned to face the camera in 3D. The original drew the ball as a
 	// screen-space oval and never tilted the view; keeping the disc upright in
 	// world Z instead flattens it to a sliver when looking down at a floor egg.
-	// Camera is at (_me.xloc, _me.yloc, 0).
-	float viewX = cx - (float)_me.xloc;
-	float viewY = cy - (float)_me.yloc;
+	// Camera is at (_renderCamera.x, _renderCamera.y, 0).
+	float viewX = cx - (float)_renderCamera.x;
+	float viewY = cy - (float)_renderCamera.y;
 	float viewZ = cz;
 	float viewLen = sqrtf(viewX * viewX + viewY * viewY + viewZ * viewZ);
 	if (viewLen < 0.001f)
@@ -813,7 +818,7 @@ void ColonyEngine::draw3DSphere(Thing &obj, int pt0x, int pt0y, int pt0z,
 		for (int i = 0; i < N; i++) {
 			int sx = 0;
 			int sy = 0;
-			if (projectCorridorPoint(_screenR, _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc,
+			if (projectCorridorPoint(_screenR, _me.look, _me.lookY, _sint, _cost, _renderCamera.x, _renderCamera.y,
 				px[i], py[i], pz[i], sx, sy)) {
 				obj.where.xmn = MIN(obj.where.xmn, sx);
 				obj.where.xmx = MAX(obj.where.xmx, sx);
@@ -958,8 +963,61 @@ void ColonyEngine::computeVisibleCells() {
 	}
 }
 
+void ColonyEngine::separateRenderCameraFromWall(int x, int y, uint8 bit, int separation, bool featuresVisible) {
+	if (!wallSegmentIsRendered(x, y, bit))
+		return;
+
+	const bool horizontal = (bit == 0x01);
+	const int along = horizontal ? _me.xloc : _me.yloc;
+	const int start = (horizontal ? x : y) * 256;
+	if (along < start - separation || along > start + 256 + separation)
+		return;
+
+	const bool positiveSide = horizontal ? _me.yindex >= y : _me.xindex >= x;
+	float wall = (horizontal ? y : x) * 256.0f;
+	const int cellX = horizontal || positiveSide ? x : x - 1;
+	const int cellY = !horizontal || positiveSide ? y : y - 1;
+	const int direction = horizontal ? (positiveSide ? kDirSouth : kDirNorth)
+		: (positiveSide ? kDirWest : kDirEast);
+	const uint8 *feature = mapFeatureAt(cellX, cellY, direction);
+	if (featuresVisible && feature && feature[0] != kWallFeatureNone && _visibleCell[cellX][cellY]) {
+		float corners[4][3];
+		getWallFace3D(cellX, cellY, direction, corners);
+		wall = corners[0][horizontal ? 1 : 0];
+	}
+
+	int camera = horizontal ? _renderCamera.y : _renderCamera.x;
+	camera = positiveSide ? MAX(camera, (int)ceilf(wall + separation))
+		: MIN(camera, (int)floorf(wall - separation));
+	if (horizontal)
+		_renderCamera.y = camera;
+	else
+		_renderCamera.x = camera;
+}
+
+void ColonyEngine::computeRenderCamera() {
+	_renderCamera = Common::Point(_me.xloc, _me.yloc);
+
+	// Enclose the near-plane corners for every view angle, including pitch.
+	const float halfWidth = kProjectionNearClip * _screenR.width() * 0.5f / kProjectionFocalLength;
+	const float halfHeight = kProjectionNearClip * _screenR.height() * 0.5f / kProjectionFocalLength;
+	const int separation = (int)ceilf(sqrtf(kProjectionNearClip * kProjectionNearClip +
+		halfWidth * halfWidth + halfHeight * halfHeight));
+	const bool featuresVisible = isMacColorMode() || _corePower[_coreIndex] > 0;
+
+	// Like Freescape, separate only the rendering camera. Touching a wall must
+	// still reach the door, stair and other interaction triggers.
+	for (int y = 0; y < 32; y++) {
+		for (int x = 0; x < 32; x++) {
+			separateRenderCameraFromWall(x, y, 0x01, separation, featuresVisible);
+			separateRenderCameraFromWall(x, y, 0x02, separation, featuresVisible);
+		}
+	}
+}
+
 void ColonyEngine::renderCorridor3D() {
 	computeVisibleCells();
+	computeRenderCamera();
 
 	bool lit = (_corePower[_coreIndex] > 0);
 	bool macMode = isMacRenderMode();
@@ -990,7 +1048,7 @@ void ColonyEngine::renderCorridor3D() {
 		ceilColor  = macMode ? (lit ? 255 : 0) : wallFill;
 	}
 
-	_gfx->begin3D(_me.xloc, _me.yloc, 0, _me.look, _me.lookY, _screenR);
+	_gfx->begin3D(_renderCamera.x, _renderCamera.y, 0, _me.look, _me.lookY, _screenR);
 	_gfx->clear(ceilColor);
 
 	uint32 wallColor = wallLine;
@@ -1037,12 +1095,11 @@ void ColonyEngine::renderCorridor3D() {
 
 	for (int y = 0; y < 32; y++) {
 		for (int x = 0; x < 32; x++) {
-			uint8 w = _wall[x][y];
 			// A stair well is a hole: the feature draws the opening itself.
-			if ((w & 0x01) && !wallSegmentIsOpenWell(x, y, 0x01)) {
+			if (wallSegmentIsRendered(x, y, 0x01)) {
 				_gfx->draw3DWall(x, y, x + 1, y, wallColor);
 			}
-			if ((w & 0x02) && !wallSegmentIsOpenWell(x, y, 0x02)) {
+			if (wallSegmentIsRendered(x, y, 0x02)) {
 				_gfx->draw3DWall(x, y, x, y + 1, wallColor);
 			}
 		}
diff --git a/engines/colony/render_features.cpp b/engines/colony/render_features.cpp
index 140e250ec21..5f294dd8411 100644
--- a/engines/colony/render_features.cpp
+++ b/engines/colony/render_features.cpp
@@ -109,8 +109,8 @@ void ColonyEngine::getWallRecess3D(const float corners[4][3], float farC[4][3])
 	ny /= len;
 	nz /= len;
 
-	const float awayX = corners[0][0] - (float)_me.xloc;
-	const float awayY = corners[0][1] - (float)_me.yloc;
+	const float awayX = corners[0][0] - (float)_renderCamera.x;
+	const float awayY = corners[0][1] - (float)_renderCamera.y;
 	const float awayZ = corners[0][2];
 	if (nx * awayX + ny * awayY + nz * awayZ < 0.0f) {
 		nx = -nx;
@@ -171,7 +171,7 @@ void ColonyEngine::clipToWallFace(const float corners[4][3]) {
 	for (int i = 0; i < 4; i++) {
 		int sx = 0;
 		int sy = 0;
-		projectCorridorPointClamped(_screenR, _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc,
+		projectCorridorPointClamped(_screenR, _me.look, _me.lookY, _sint, _cost, _renderCamera.x, _renderCamera.y,
 			corners[i][0], corners[i][1], corners[i][2], sx, sy);
 		if (i == 0) {
 			minX = sx;
@@ -603,13 +603,13 @@ bool ColonyEngine::isWallFeatureFacingCamera(int cellX, int cellY, int direction
 	// Keep features visible while standing on the boundary.
 	switch (direction) {
 	case kDirNorth:
-		return _me.yloc <= (cellY + 1) * 256;
+		return _renderCamera.y <= (cellY + 1) * 256;
 	case kDirSouth:
-		return _me.yloc >= cellY * 256;
+		return _renderCamera.y >= cellY * 256;
 	case kDirWest:
-		return _me.xloc >= cellX * 256;
+		return _renderCamera.x >= cellX * 256;
 	case kDirEast:
-		return _me.xloc <= (cellX + 1) * 256;
+		return _renderCamera.x <= (cellX + 1) * 256;
 	default:
 		return true;
 	}
diff --git a/engines/colony/render_objects.cpp b/engines/colony/render_objects.cpp
index 6794814e1a2..ab5daddba88 100644
--- a/engines/colony/render_objects.cpp
+++ b/engines/colony/render_objects.cpp
@@ -111,7 +111,7 @@ bool projectCorridorPointRaw(const Common::Rect &screenR, uint8 look, int8 lookY
 	const float eyeX = side;
 	const float eyeY = dz * cosPitch + forward * sinPitch;
 	const float eyeZ = dz * sinPitch - forward * cosPitch;
-	if (eyeZ >= -1.0f)
+	if (eyeZ >= -kProjectionNearClip)
 		return false;
 
 	const float centerX = screenR.left + screenR.width() * 0.5f;
@@ -1344,7 +1344,7 @@ void ColonyEngine::drawPrismOval3D(Thing &thing, const PrismPartDef &def, bool u
 		transformedX[i] = (float)(rx + thing.where.xloc);
 		transformedY[i] = (float)(ry + thing.where.yloc);
 		transformedZ[i] = (float)(oz - 160);
-		projected[i] = projectCorridorPointRaw(_screenR, _me.look, _me.lookY, _sint, _cost, _me.xloc, _me.yloc,
+		projected[i] = projectCorridorPointRaw(_screenR, _me.look, _me.lookY, _sint, _cost, _renderCamera.x, _renderCamera.y,
 			transformedX[i], transformedY[i], transformedZ[i],
 			projectedX[i], projectedY[i]);
 	}
@@ -1494,7 +1494,7 @@ void ColonyEngine::drawDOSEyeSlit3D(Thing &thing, const PrismPartDef &irisDef, b
 void ColonyEngine::drawEyeOverlays3D(Thing &thing, const PrismPartDef &irisDef, int irisColorOverride,
 		const PrismPartDef &pupilDef, int pupilColorOverride, bool useLook, bool dosFill) {
 	if (!isProjectedPrismSurfaceVisible(_screenR, thing, irisDef, useLook, 0,
-			_me.look, _me.lookY, _me.xloc, _me.yloc, _sint, _cost)) {
+			_me.look, _me.lookY, _renderCamera.x, _renderCamera.y, _sint, _cost)) {
 		return;
 	}
 
@@ -1521,8 +1521,8 @@ void ColonyEngine::drawEnemyEye3D(Thing &obj, Thing &eye, int eyeballColor, int
 		const uint8 eyeAng = obj.where.ang + 32;
 		const int32 eyeBaseX = obj.where.xloc + (_cost[eyeAng] >> 1);
 		const int32 eyeBaseY = obj.where.yloc + (_sint[eyeAng] >> 1);
-		const int64 dx = eyeBaseX - _me.xloc;
-		const int64 dy = eyeBaseY - _me.yloc;
+		const int64 dx = eyeBaseX - _renderCamera.x;
+		const int64 dy = eyeBaseY - _renderCamera.y;
 		dosFill = dx * dx + dy * dy > 64 * 64;
 	}
 
@@ -1940,7 +1940,7 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 		// surface is facing the camera, because each part uses a separate
 		// depth range bucket rather than true inter-part depth sorting.
 		const bool lensFrontVisible = isProjectedPrismSurfaceVisible(_screenR, obj, kProjectorParts[2], false, 6,
-															_me.look, _me.lookY, _me.xloc, _me.yloc, _sint, _cost);
+															_me.look, _me.lookY, _renderCamera.x, _renderCamera.y, _sint, _cost);
 		_gfx->setDepthRange(0.008f, 1.0f);
 		draw3DPrism(obj, kTableParts[1], false, -1, true, false); // table base
 		_gfx->setDepthRange(0.006f, 1.0f);
@@ -1985,7 +1985,7 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 	case kObjPToilet: {
 		// MakePToilet draws the two-sided partition first when it is behind the toilet.
 		const bool partitionBehind = isProjectedPrismSurfaceVisible(_screenR, obj, kPToiletParts[4], false, 0,
-			_me.look, _me.lookY, _me.xloc, _me.yloc, _sint, _cost);
+			_me.look, _me.lookY, _renderCamera.x, _renderCamera.y, _sint, _cost);
 		for (int i = 0; i < 5; i++) {
 			const int part = partitionBehind ? (i == 0 ? 4 : i - 1) : i;
 			_gfx->setDepthRange((4 - i) * 0.002f, 1.0f);
@@ -2011,8 +2011,8 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 		break;
 	// === Robot types (1-20) ===
 	case kRobEye:
-		if ((obj.where.xloc - _me.xloc) * (obj.where.xloc - _me.xloc) +
-			(obj.where.yloc - _me.yloc) * (obj.where.yloc - _me.yloc) <= 64 * 64) {
+		if ((obj.where.xloc - _renderCamera.x) * (obj.where.xloc - _renderCamera.x) +
+			(obj.where.yloc - _renderCamera.y) * (obj.where.yloc - _renderCamera.y) <= 64 * 64) {
 			break;
 		}
 		// Writes off, as in drawEnemyEye3D().
@@ -2120,7 +2120,7 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 		break;
 	case kRobQueen:
 		{
-			EnemyEyePair eyes = buildEnemyEyePair(_screenR, obj, _me.look, _me.lookY, _me.xloc, _me.yloc, _sint, _cost);
+			EnemyEyePair eyes = buildEnemyEyePair(_screenR, obj, _me.look, _me.lookY, _renderCamera.x, _renderCamera.y, _sint, _cost);
 			Thing &firstEye = eyes.leftFirst ? eyes.left : eyes.right;
 			Thing &secondEye = eyes.leftFirst ? eyes.right : eyes.left;
 			const PrismPartDef &firstWing = eyes.leftFirst ? kQLWingDef : kQRWingDef;
@@ -2150,7 +2150,7 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 				draw3DPrism(obj, kQAbdomenDef, false, -1, true, false);
 			};
 
-			const int body = queenBodyOrder(_screenR, obj, _me.look, _me.lookY, _me.xloc, _me.yloc, _sint, _cost);
+			const int body = queenBodyOrder(_screenR, obj, _me.look, _me.lookY, _renderCamera.x, _renderCamera.y, _sint, _cost);
 			if (body == 0) {
 				drawThorax();
 				drawAbdomen();
@@ -2158,7 +2158,7 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 			drawWingEye(firstEye, firstWing);
 			if (body == 1) {
 				const bool abdomenBackVisible = prismSurfaceVisible(_screenR, obj, kQAbdomenDef, 8,
-					_me.look, _me.lookY, _me.xloc, _me.yloc, _sint, _cost);
+					_me.look, _me.lookY, _renderCamera.x, _renderCamera.y, _sint, _cost);
 				if (abdomenBackVisible) {
 					drawAbdomen();
 					drawThorax();
@@ -2177,7 +2177,7 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 		break;
 	case kRobDrone:
 		{
-			EnemyEyePair eyes = buildEnemyEyePair(_screenR, obj, _me.look, _me.lookY, _me.xloc, _me.yloc, _sint, _cost);
+			EnemyEyePair eyes = buildEnemyEyePair(_screenR, obj, _me.look, _me.lookY, _renderCamera.x, _renderCamera.y, _sint, _cost);
 			Thing &firstEye = eyes.leftFirst ? eyes.left : eyes.right;
 			Thing &secondEye = eyes.leftFirst ? eyes.right : eyes.left;
 			const int enemyEyeballColor = isMacRenderMode() ? eyeballColor : 5;
@@ -2194,7 +2194,7 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 				drawEnemyEye3D(obj, eye, enemyEyeballColor, kColorDroneEye, pupilColor);
 			};
 
-			const int body = droneBodyOrder(_screenR, obj, _me.look, _me.lookY, _me.xloc, _me.yloc, _sint, _cost);
+			const int body = droneBodyOrder(_screenR, obj, _me.look, _me.lookY, _renderCamera.x, _renderCamera.y, _sint, _cost);
 			if (body == 0)
 				drawBody();
 			drawEye(firstEye);
@@ -2225,12 +2225,12 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 			const PrismPartDef leftPincerDef = {4, leftPincerPts, 4, kDLPincerSurf};
 			const PrismPartDef rightPincerDef = {4, rightPincerPts, 4, kDRPincerSurf};
 
-			EnemyEyePair eyes = buildEnemyEyePair(_screenR, obj, _me.look, _me.lookY, _me.xloc, _me.yloc, _sint, _cost);
+			EnemyEyePair eyes = buildEnemyEyePair(_screenR, obj, _me.look, _me.lookY, _renderCamera.x, _renderCamera.y, _sint, _cost);
 			Thing &firstEye = eyes.leftFirst ? eyes.left : eyes.right;
 			Thing &secondEye = eyes.leftFirst ? eyes.right : eyes.left;
 			const int enemyEyeballColor = isMacRenderMode() ? eyeballColor : 5;
-			const float leftPincerDepth = prismPointForwardDepth(obj, leftPincerDef, 1, _me.look, _me.xloc, _me.yloc, _sint, _cost);
-			const float rightPincerDepth = prismPointForwardDepth(obj, rightPincerDef, 1, _me.look, _me.xloc, _me.yloc, _sint, _cost);
+			const float leftPincerDepth = prismPointForwardDepth(obj, leftPincerDef, 1, _me.look, _renderCamera.x, _renderCamera.y, _sint, _cost);
+			const float rightPincerDepth = prismPointForwardDepth(obj, rightPincerDef, 1, _me.look, _renderCamera.x, _renderCamera.y, _sint, _cost);
 			int layer = 8;
 			auto setNextDepthRange = [&]() {
 				_gfx->setDepthRange((layer--) * 0.002f, 1.0f);
@@ -2257,7 +2257,7 @@ bool ColonyEngine::drawStaticObjectPrisms3D(Thing &obj) {
 				}
 			};
 
-			const int body = droneBodyOrder(_screenR, obj, _me.look, _me.lookY, _me.xloc, _me.yloc, _sint, _cost);
+			const int body = droneBodyOrder(_screenR, obj, _me.look, _me.lookY, _renderCamera.x, _renderCamera.y, _sint, _cost);
 			switch (body) {
 			case 0:
 				drawBody();
diff --git a/engines/colony/renderer.h b/engines/colony/renderer.h
index 95488fa4896..86073313501 100644
--- a/engines/colony/renderer.h
+++ b/engines/colony/renderer.h
@@ -37,6 +37,7 @@ namespace Colony {
 
 // CALCROBO.C projects both axes with (coordinate << 8) / depth.
 const float kProjectionFocalLength = 256.0f;
+const float kProjectionNearClip = 1.0f;
 
 class Renderer {
 public:
diff --git a/engines/colony/renderer_opengl.cpp b/engines/colony/renderer_opengl.cpp
index 89040b34113..af782578d97 100644
--- a/engines/colony/renderer_opengl.cpp
+++ b/engines/colony/renderer_opengl.cpp
@@ -307,7 +307,7 @@ void OpenGLRenderer::begin3D(int camX, int camY, int camZ, int angle, int angleY
 	
 	// The original projection is center + (coordinate << 8) / depth, so
 	// derive the frustum from its fixed 256-logical-pixel focal length.
-	const float nearClip = 1.0f;
+	const float nearClip = kProjectionNearClip;
 	const float farClip = 10000.0f;
 	const float xmax = nearClip * viewport.width() * 0.5f / kProjectionFocalLength;
 	const float ymax = nearClip * viewport.height() * 0.5f / kProjectionFocalLength;
diff --git a/engines/colony/renderer_opengl_shaders.cpp b/engines/colony/renderer_opengl_shaders.cpp
index 28767b69da6..8f58fd40dc8 100644
--- a/engines/colony/renderer_opengl_shaders.cpp
+++ b/engines/colony/renderer_opengl_shaders.cpp
@@ -772,7 +772,7 @@ void OpenGLShaderRenderer::begin3D(int camX, int camY, int camZ, int angle, int
 
 	// Match CALCROBO.C's center + (coordinate << 8) / depth projection and
 	// the fixed-function path by using a 256-logical-pixel focal length.
-	const float nearClip = 1.0f;
+	const float nearClip = kProjectionNearClip;
 	const float farClip = 10000.0f;
 	const float xmax = nearClip * viewport.width() * 0.5f / kProjectionFocalLength;
 	const float ymax = nearClip * viewport.height() * 0.5f / kProjectionFocalLength;


Commit: b398c8b1942442a47cfada35d6cce8d3f54943c0
    https://github.com/scummvm/scummvm/commit/b398c8b1942442a47cfada35d6cce8d3f54943c0
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-10-20T13:26:51+02:00

Commit Message:
COLONY: show UI when player interacts with an animation

Changed paths:
    engines/colony/animation.cpp
    engines/colony/colony.cpp
    engines/colony/colony.h


diff --git a/engines/colony/animation.cpp b/engines/colony/animation.cpp
index ea19afa29d7..c6927241681 100644
--- a/engines/colony/animation.cpp
+++ b/engines/colony/animation.cpp
@@ -423,6 +423,7 @@ void ColonyEngine::playAnimation() {
 	_rotateRight = false;
 
 	_animationRunning = true;
+	syncMacMenuChecks();
 	_animExitPressed = _animExitInside = false;
 	_animExitStrip = _animExitButton = Common::Rect();
 	_coderCursor = 0;
@@ -610,11 +611,12 @@ void ColonyEngine::playAnimation() {
 			needsDraw = true;
 		}
 		Common::Event event;
-		while (_system->getEventManager()->pollEvent(event)) {
+		while (_animationRunning && _system->getEventManager()->pollEvent(event)) {
 			if (event.type == Common::EVENT_QUIT || event.type == Common::EVENT_RETURN_TO_LAUNCHER) {
 				_animationRunning = false;
 				break;
-			} else if (event.type == Common::EVENT_SCREEN_CHANGED) {
+			}
+			if (event.type == Common::EVENT_SCREEN_CHANGED) {
 				_gfx->computeScreenViewport();
 				if (updateScreenSize()) {
 					_animExitPressed = _animExitInside = false;
@@ -623,7 +625,25 @@ void ColonyEngine::playAnimation() {
 				}
 				drawAnimation();
 				needsDraw = true;
-			} else if (event.type == Common::EVENT_LBUTTONDOWN) {
+				continue;
+			}
+			// Only an open menu captures input; the menu bar stays visible.
+			if (_macMenu && _macMenu->hasAllFocus() && event.type == Common::EVENT_CUSTOM_ENGINE_ACTION_START &&
+					event.customType == kActionEscape) {
+				_macMenu->closeMenu();
+				needsDraw = true;
+				continue;
+			}
+			if (processMacMenuEvent(event)) {
+				needsDraw = true;
+				if (!_animationRunning || shouldQuit())
+					break;
+				continue;
+			}
+			if (_macMenu && _macMenu->hasAllFocus())
+				continue;
+
+			if (event.type == Common::EVENT_LBUTTONDOWN) {
 				const Common::Point pt = eventMouseToLogical(event.mouse);
 				_messageSourceRect = Common::Rect();
 				if (handleColonyCoderClick(pt)) {
@@ -751,11 +771,14 @@ void ColonyEngine::playAnimation() {
 
 	if (useSquarePixelViewport)
 		_gfx->setSquarePixelViewport(false);
-	_system->lockMouse(true);
-	CursorMan.showMouse(false);
+	_animationRunning = false;
+	if (_macMenu)
+		_macMenu->closeMenu();
 	CursorMan.popAllCursors();
+	updateMouseCapture(true);
+	syncMacMenuChecks();
 
-	// Purge pending mouse/keyboard events AFTER re-locking the mouse,
+	// Purge pending mouse/keyboard events after restoring mouse capture,
 	// so that any synthetic events from the lock transition are also
 	// drained and don't trigger actions (e.g. cShoot) in the main loop.
 	_system->getEventManager()->purgeMouseEvents();
@@ -812,6 +835,9 @@ void ColonyEngine::drawAnimation() {
 	// Full clear required: OpenGL framebuffer is undefined between frames.
 	_gfx->clear(0);
 
+	if (isMacRenderMode())
+		drawDashboardStep1();
+
 	// Center 416x264 animation area on screen (from original InitDejaVu)
 	int ox = _screenR.left + (_screenR.width() - 416) / 2;
 	ox = (ox / 8) * 8;
@@ -919,6 +945,7 @@ void ColonyEngine::drawAnimation() {
 
 	drawAnimationExitButton(ox, oy);
 	drawColonyCoder(ox, oy);
+	drawMacMenuBar();
 }
 
 // gamesprt.c DrawButton(): 30px strip below the 416x264 scene with a framed
diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 31231e01a10..bb3acf3c1a8 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -43,6 +43,8 @@
 #include "graphics/macgui/macwindowmanager.h"
 #include "graphics/palette.h"
 #include "graphics/paletteman.h"
+#include "gui/message.h"
+#include "gui/saveload.h"
 
 #include "colony/colony.h"
 #include "colony/console.h"
@@ -499,6 +501,9 @@ void ColonyEngine::menuCommandsCallback(int action, Common::String &text, void *
 }
 
 bool ColonyEngine::showSaveDialog() {
+	if (!canSaveGameStateCurrently())
+		return false;
+
 	_system->lockMouse(false);
 	CursorMan.setDefaultArrowCursor();
 	CursorMan.showMouse(true);
@@ -521,6 +526,12 @@ Graphics::MacMenuItem *ColonyEngine::macMenuItemForAction(int menuIndex, int act
 }
 
 void ColonyEngine::syncMacMenuChecks() {
+	const bool canSave = canSaveGameStateCurrently();
+	if (Graphics::MacMenuItem *item = macMenuItemForAction(kMenuFile, kMenuActionSave))
+		_macMenu->setEnabled(item, canSave);
+	if (Graphics::MacMenuItem *item = macMenuItemForAction(kMenuFile, kMenuActionSaveAs))
+		_macMenu->setEnabled(item, canSave);
+
 	// inits.c:50-53. "Faster" is checked when polygon fill is OFF.
 	if (Graphics::MacMenuItem *item = macMenuItemForAction(kMenuOptions, kMenuActionSound))
 		_macMenu->setCheckMark(item, _soundOn);
@@ -533,13 +544,14 @@ void ColonyEngine::syncMacMenuChecks() {
 }
 
 void ColonyEngine::updateMouseCapture(bool recenter) {
-	_system->lockMouse(_mouseLocked);
-	CursorMan.showMouse(!_mouseLocked);
+	const bool mouseLocked = _mouseLocked && !_animationRunning;
+	_system->lockMouse(mouseLocked);
+	CursorMan.showMouse(!mouseLocked);
 
 	int cursorMode = 0;
 
-	if (!_mouseLocked && isMacRenderMode() && _wm) {
-		if (_macCrossCursor) {
+	if (!mouseLocked && isMacRenderMode() && _wm) {
+		if (_macCrossCursor && !_animationRunning) {
 			cursorMode = 1;
 			_wm->replaceCursor(Graphics::kMacCursorCustom, _macCrossCursor);
 		} else if (_macArrowCursor) {
@@ -564,9 +576,9 @@ void ColonyEngine::updateMouseCapture(bool recenter) {
 		}
 		_lastLoggedCursorMode = cursorMode;
 	}
-	CursorMan.showMouse(!_mouseLocked);
+	CursorMan.showMouse(!mouseLocked);
 
-	if (_mouseLocked && recenter) {
+	if (mouseLocked && recenter) {
 		_mousePos = Common::Point(_centerX, _centerY);
 		warpMouseLogical(_centerX, _centerY);
 		_system->getEventManager()->purgeMouseEvents();
@@ -587,26 +599,44 @@ void ColonyEngine::handleMenuAction(int action) {
 	case kMenuActionAbout:
 		runMacAbout();
 		break;
-	// gmain.c DoCommand: New, Open and Quit all go through QSave() first, so
-	// an unsaved game is never dropped on the floor. Yes saves and falls
-	// through to the action, No goes straight to it, Cancel does nothing.
+	// QSave() precedes New, Open and Quit, except when an animation prevents saving.
 	case kMenuActionNew: {
-		const int answer = runMacSaveQuery();
+		const int answer = _animationRunning ? 2 : runMacSaveQuery();
 		if (answer == 1)
 			showSaveDialog();
-		if (answer)
-			startNewGame();
+		if (answer) {
+			if (_animationRunning)
+				_pendingMenuAction = kMenuActionNew;
+			else
+				startNewGame();
+		}
 		break;
 	}
 	case kMenuActionOpen: {
-		const int answer = runMacSaveQuery();
+		const int answer = _animationRunning ? 2 : runMacSaveQuery();
 		if (answer == 1)
 			showSaveDialog();
 		if (answer) {
 			_system->lockMouse(false);
 			CursorMan.setDefaultArrowCursor();
 			CursorMan.showMouse(true);
-			loadGameDialog();
+			if (_animationRunning) {
+				// Select now, but replace the game only after animation callers return.
+				if (_macMenu)
+					_macMenu->closeMenu();
+				GUI::SaveLoadChooser dialog(false);
+				int slot;
+				{
+					PauseToken pause = pauseEngine();
+					slot = dialog.runModalWithCurrentTarget();
+				}
+				if (slot >= 0) {
+					_pendingLoadSlot = slot;
+					_pendingMenuAction = kMenuActionOpen;
+				}
+			} else {
+				loadGameDialog();
+			}
 			updateMouseCapture(true);
 		}
 		break;
@@ -616,7 +646,7 @@ void ColonyEngine::handleMenuAction(int action) {
 		showSaveDialog();
 		break;
 	case kMenuActionQuit: {
-		const int answer = runMacSaveQuery();
+		const int answer = _animationRunning ? 2 : runMacSaveQuery();
 		// Quit only if the save went through, matching "if(saved)notDone=FALSE".
 		if (answer == 2 || (answer == 1 && showSaveDialog()))
 			quitGame();
@@ -647,6 +677,65 @@ void ColonyEngine::handleMenuAction(int action) {
 	default:
 		break;
 	}
+
+	if (_animationRunning && (_pendingMenuAction || shouldQuit())) {
+		_animationResult = 0;
+		_animationRunning = false;
+	}
+}
+
+bool ColonyEngine::processMacMenuEvent(const Common::Event &event) {
+	if (!_wm)
+		return false;
+
+	syncMacMenuChecks();
+	Common::Event wmEvent = event;
+	switch (event.type) {
+	case Common::EVENT_MOUSEMOVE:
+	case Common::EVENT_LBUTTONDOWN:
+	case Common::EVENT_LBUTTONUP:
+	case Common::EVENT_RBUTTONDOWN:
+	case Common::EVENT_RBUTTONUP:
+	case Common::EVENT_MBUTTONDOWN:
+	case Common::EVENT_MBUTTONUP:
+		wmEvent.mouse = eventMouseToLogical(event.mouse);
+		break;
+	default:
+		break;
+	}
+	return _wm->processEvent(wmEvent);
+}
+
+void ColonyEngine::processPendingMenuAction() {
+	const int action = _pendingMenuAction;
+	const int slot = _pendingLoadSlot;
+	_pendingMenuAction = 0;
+	_pendingLoadSlot = -1;
+
+	if (action == kMenuActionNew) {
+		startNewGame();
+	} else if (action == kMenuActionOpen) {
+		const Common::Error error = loadGameState(slot);
+		if (error.getCode() != Common::kNoError) {
+			_system->lockMouse(false);
+			CursorMan.setDefaultArrowCursor();
+			CursorMan.showMouse(true);
+			GUI::MessageDialog dialog(error.getDesc());
+			dialog.runModal();
+		}
+	}
+	updateMouseCapture(true);
+}
+
+void ColonyEngine::drawMacMenuBar() {
+	if (!_macMenu || !_menuSurface || _fullscreen)
+		return;
+
+	syncMacMenuChecks();
+	// Render through our surface; the WM's screen copies bypass OpenGL.
+	_menuSurface->fillRect(Common::Rect(_width, _height), _menuSurface->format.ARGBToColor(0, 0, 0, 0));
+	_macMenu->draw(_menuSurface, true);
+	_gfx->drawSurface(&_menuSurface->rawSurface(), 0, 0);
 }
 
 void ColonyEngine::initMacMenus() {
@@ -923,6 +1012,14 @@ Common::Error ColonyEngine::run() {
 	uint32 lastCenterTick = lastMoveTick;
 	while (!shouldQuit()) {
 		_frameLimiter->startFrame();
+		if (_pendingMenuAction) {
+			processPendingMenuAction();
+			mouseDX = mouseDY = 0;
+			mouseLookAccumX = mouseLookAccumY = 0.0f;
+			mouseMoved = false;
+			lastMoveTick = lastColonyTick = lastBattleTick = lastCenterTick = _system->getMillis();
+			_lastColonyThinkTime = lastColonyTick;
+		}
 		// Modal screens keep their layout stable until the next full redraw.
 		updateScreenSize();
 
@@ -975,27 +1072,11 @@ Common::Error ColonyEngine::run() {
 		}
 
 		Common::Event event;
-		while (_system->getEventManager()->pollEvent(event)) {
-			// Let MacWindowManager handle menu events first. The menu bar is
-			// drawn into _menuSurface (engine logical coords, _width×_height
-			// per colony.cpp:570), so its hit-testing rects are in logical
-			// space. With kSupportsArbitraryResolutions, event.mouse arrives
-			// in window pixels — pass a coord-scaled copy so the WM resolves
-			// menu clicks correctly. The original event is preserved for the
-			// engine's own handlers downstream.
+		while (!_pendingMenuAction && _system->getEventManager()->pollEvent(event)) {
+			// Menu events use logical coordinates; keep the original event for gameplay.
 			if (_wm) {
 				bool wasMenuActive = _wm->isMenuActive();
-				Common::Event wmEvent = event;
-				if (event.type == Common::EVENT_MOUSEMOVE
-						|| event.type == Common::EVENT_LBUTTONDOWN
-						|| event.type == Common::EVENT_LBUTTONUP
-						|| event.type == Common::EVENT_RBUTTONDOWN
-						|| event.type == Common::EVENT_RBUTTONUP
-						|| event.type == Common::EVENT_MBUTTONDOWN
-						|| event.type == Common::EVENT_MBUTTONUP) {
-					wmEvent.mouse = eventMouseToLogical(event.mouse);
-				}
-				if (_wm->processEvent(wmEvent)) {
+				if (processMacMenuEvent(event)) {
 					// WM consumed the event (menu interaction)
 					if (!wasMenuActive && _wm->isMenuActive()) {
 						_system->lockMouse(false);
@@ -1166,6 +1247,9 @@ Common::Error ColonyEngine::run() {
 			}
 		}
 
+		if (_pendingMenuAction)
+			continue;
+
 		if (mouseMoved && _mouseLocked) {
 			if (mouseDX != 0) {
 				mouseLookAccumX -= mouseDX * _mouseSensitivity;
@@ -1267,6 +1351,9 @@ Common::Error ColonyEngine::run() {
 			}
 		}
 
+		if (_pendingMenuAction)
+			continue;
+
 		if (_gameMode == kModeBattle) {
 			renderBattle();
 			drawForkliftOverlay();
@@ -1285,13 +1372,7 @@ Common::Error ColonyEngine::run() {
 		if (_showAutomap && _gameMode == kModeColony)
 			drawAutomap();
 
-		// Draw Mac menu bar overlay (render directly to our surface, skip WM's
-		// g_system->copyRectToScreen which conflicts with the OpenGL backend)
-		if (_macMenu && _menuSurface && !_fullscreen) {
-			_menuSurface->fillRect(Common::Rect(_width, _height), _menuSurface->format.ARGBToColor(0, 0, 0, 0));
-			_macMenu->draw(_menuSurface, true);
-			_gfx->drawSurface(&_menuSurface->rawSurface(), 0, 0);
-		}
+		drawMacMenuBar();
 
 		_displayCount++; // Mac: count++ after Display()
 		_frameLimiter->delayBeforeSwap();
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 2d878c831af..b57457fe225 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -38,6 +38,7 @@
 #include "graphics/surface.h"
 
 namespace Common {
+struct Event;
 class MacResManager;
 class SeekableReadStreamEndian;
 }
@@ -652,7 +653,12 @@ private:
 	Graphics::MacMenu *_macMenu = nullptr;
 	Graphics::ManagedSurface *_menuSurface = nullptr;
 	int _menuBarHeight = 0;
+	int _pendingMenuAction = 0;
+	int _pendingLoadSlot = -1;
 	void initMacMenus();
+	void drawMacMenuBar();
+	bool processMacMenuEvent(const Common::Event &event);
+	void processPendingMenuAction();
 	void loadMacCursorResources();
 	void handleMenuAction(int action);
 	static void menuCommandsCallback(int action, Common::String &text, void *data);




More information about the Scummvm-git-logs mailing list