From 259a72541e5becbeae18742da2374bfa383428ef Mon Sep 17 00:00:00 2001
From: Sam Lantinga <slouken@libsdl.org>
Date: Sat, 10 Oct 2009 02:11:07 +0000
Subject: [PATCH] Fixed bug #798

kty@lavabit.com      2009-09-19 14:19:04 PDT

The stable release of SDL 1.2.13 for BeOS/Haiku has a bug in
BE_FindClosestFSMode that causes it to sometimes not select the best mode when
going fullscreen. There are in fact two bugs in the implementation but I will
not go into specifics because there is already a patch for it in the developer
SVN 1.3. However I am still reporting it because I believe the following code
is a better patch for the issue. The current implementation on SVN only works
if it is able to find an exact match for the requested mode. However, by
scanning from lowest-to-highest resolution instead of highest-to-lowest, one
can find the best mode at all times

--HG--
branch : SDL-1.2
extra : convert_revision : svn%3Ac70aab31-4412-0410-b14c-859654838e24/branches/SDL-1.2%403997
---
 src/video/bwindow/SDL_BWin.h       |   2 +
 src/video/bwindow/SDL_sysevents.cc |  17 +++
 src/video/bwindow/SDL_sysvideo.cc  | 159 +++++++++++++++--------------
 3 files changed, 103 insertions(+), 75 deletions(-)

diff --git a/src/video/bwindow/SDL_BWin.h b/src/video/bwindow/SDL_BWin.h
index 778914d6..ba8694fe 100644
--- a/src/video/bwindow/SDL_BWin.h
+++ b/src/video/bwindow/SDL_BWin.h
@@ -267,6 +267,8 @@ public:
 	}
 
 	virtual void DispatchMessage(BMessage *msg, BHandler *target);
+	
+	virtual void DirectConnected(direct_buffer_info *info);
 
 private:
 #if SDL_VIDEO_OPENGL
diff --git a/src/video/bwindow/SDL_sysevents.cc b/src/video/bwindow/SDL_sysevents.cc
index f0c452dc..f9409db6 100644
--- a/src/video/bwindow/SDL_sysevents.cc
+++ b/src/video/bwindow/SDL_sysevents.cc
@@ -379,3 +379,20 @@ void SDL_BWin::DispatchMessage(BMessage *msg, BHandler *target)
 	}
 	BDirectWindow::DispatchMessage(msg, target);
 }
+
+void SDL_BWin::DirectConnected(direct_buffer_info *info) {
+	switch (info->buffer_state & B_DIRECT_MODE_MASK) {
+		case B_DIRECT_START:
+		case B_DIRECT_MODIFY:
+			{
+				int32 width = info->window_bounds.right -
+					info->window_bounds.left + 1;
+				int32 height = info->window_bounds.bottom -
+					info->window_bounds.top + 1;
+				SDL_PrivateResize(width, height);
+				break;
+			}
+		default:
+			break;
+	}
+}
diff --git a/src/video/bwindow/SDL_sysvideo.cc b/src/video/bwindow/SDL_sysvideo.cc
index d0ffe992..33597621 100644
--- a/src/video/bwindow/SDL_sysvideo.cc
+++ b/src/video/bwindow/SDL_sysvideo.cc
@@ -57,7 +57,7 @@ static void BE_UnlockHWSurface(_THIS, SDL_Surface *surface);
 static void BE_FreeHWSurface(_THIS, SDL_Surface *surface);
 
 static int BE_ToggleFullScreen(_THIS, int fullscreen);
-SDL_Overlay *BE_CreateYUVOverlay(_THIS, int width, int height, Uint32 format, SDL_Surface *display);
+static SDL_Overlay *BE_CreateYUVOverlay(_THIS, int width, int height, Uint32 format, SDL_Surface *display);
 
 /* OpenGL functions */
 #if SDL_VIDEO_OPENGL
@@ -358,16 +358,25 @@ static bool BE_FindClosestFSMode(_THIS, int width, int height, int bpp,
 	                  (current.timing.h_total * current.timing.v_total);
 
 	modes = SDL_modelist[((bpp+7)/8)-1];
-	for ( i=0; modes[i] && (modes[i]->w > width) &&
-		      (modes[i]->h > height); ++i ) {
-		/* still looking */
-	}
-	if ( ! modes[i] || (modes[i]->w < width) || (modes[i]->h < width) ) {
-		--i;	/* We went too far */
+	
+	// find end of list (lowest-resolution mode; modes are ordered
+	// highest-to-lowest).
+	i = 0; while(modes[i]) i++;
+	if (!i) return false;		// what? no modes at all?
+	
+	// find first mode with resolution >= requested in both dimensions
+	for (--i; i >= 0; --i)
+	{
+		if (modes[i]->w >= width && modes[i]->h >= height)
+			break;
 	}
-
+	
+	// unable to find any mode with that high a resolution!
+	if (i < 0)
+		return false;
+	
 	width = modes[i]->w;
-	height = modes[i]->h;      
+	height = modes[i]->h;
 
 	bscreen.GetModeList(&dmodes, &nmodes);
 	for ( i = 0; i < nmodes; ++i ) {
@@ -396,88 +405,88 @@ static bool BE_FindClosestFSMode(_THIS, int width, int height, int bpp,
 
 static int BE_SetFullScreen(_THIS, SDL_Surface *screen, int fullscreen)
 {
-	int was_fullscreen;
-	bool needs_unlock;
+	// printf("SetFullScreen(%d)\n", fullscreen);
 	BScreen bscreen;
-	BRect bounds;
-	display_mode mode;
-	int width, height, bpp;
-
-	/* Set the fullscreen mode */
-	was_fullscreen = SDL_Win->IsFullScreen();
-	SDL_Win->SetFullScreen(fullscreen);
-	fullscreen = SDL_Win->IsFullScreen();
 
-	width = screen->w;
-	height = screen->h;
+	// SetFullSscreen() does not work as expected if called in a window
+	// that was never shown. This is probably a bug in the Haiku Game Kit that needs
+	// to be investigated.	
+	if (SDL_Win->Lock()) {
+		// Show our window.
+		SDL_Win->Show();
+	}	
+	
+	if (SDL_Win->IsLocked()) {
+		// Unlock the window if it was locked. This is needed as only the
+		// first call to Show() unlocks the looper. All other calls to it
+		// will not.
+		SDL_Win->Unlock();
+	}
 
-	/* Set the appropriate video mode */
-	if ( fullscreen ) {
-		bpp = screen->format->BitsPerPixel;
+	int width = screen->w;
+	int height = screen->h;
+	
+	if (fullscreen) {
+		// Set resolution to the closest available one that matches the
+		// current SDL resolution.
+		display_mode mode;
 		bscreen.GetMode(&mode);
-		if ( (bpp != ColorSpaceToBitsPerPixel(mode.space)) ||
-		     (width != mode.virtual_width) ||
-		     (height != mode.virtual_height)) {
+
+		int bpp = screen->format->BitsPerPixel;
+		if (bpp != ColorSpaceToBitsPerPixel(mode.space) ||
+			width != mode.virtual_width || height != mode.virtual_height) {
 			if(BE_FindClosestFSMode(_this, width, height, bpp, &mode)) {
 				bscreen.SetMode(&mode);
-				/* This simply stops the next resize event from being
-				 * sent to the SDL handler.
-				 */
-				SDL_Win->InhibitResize();
 			} else {
-				fullscreen = 0;
-				SDL_Win->SetFullScreen(fullscreen);
-			}
+				// printf("Could not set new mode.\n");
+				return(0);
+			}			
 		}
+	} else {
+		// Reset to the previous known resolution as we are now in window
+		// mode.
+		bscreen.SetMode(&saved_mode);	
 	}
-	if ( was_fullscreen && ! fullscreen ) {
-		bscreen.SetMode(&saved_mode);
-	}
-
-	if ( SDL_Win->Lock() ) {
-		int cx, cy;
-		if ( SDL_Win->Shown() ) {
-			needs_unlock = 1;
-			SDL_Win->Hide();
-		} else {
-			needs_unlock = 0;
-		}
-		/* This resizes the window and view area, but inhibits resizing
-		 * of the BBitmap due to the InhibitResize call above. Thus the
-		 * bitmap (pixel data) never changes.
-		 */
+	
+	// Effectivelly set/reset full screen mode. If we are already in
+	// full screen mode, we reset back to windowed mode first so the
+	// window can resize when going fullscreen.
+	// if (fullscreen)
+		// printf("Going fullscreen\n");
+	// else
+		// printf("Going windowed\n"); 
+	SDL_Win->SetFullScreen(fullscreen);
+	
+	// Calculate offsets for centering the window (in window mode) and for
+	// dentering the bitmap (in full screen mode).
+	BRect bounds = bscreen.Frame();
+	bounds.PrintToStream();
+	int32 cx = (bounds.IntegerWidth() - width)/2;
+	int32 cy = (bounds.IntegerHeight() - height)/2;
+	
+	// printf ("cx = %d, cy = %d\n", cx, cy);
+	if (!SDL_Win->IsFullScreen()) {
+		// printf("Doing not fullscreen stuff.\n");
+		// We are not in full screen mode, so we want to change the window
+		// size to match the resolution in SDL.
 		SDL_Win->ResizeTo(width, height);
-		bounds = bscreen.Frame();
-		/* Calculate offsets - used either to center window
-		 * (windowed mode) or to set drawing offsets (fullscreen mode)
-		 */
-		cx = (bounds.IntegerWidth() - width)/2;
-		cy = (bounds.IntegerHeight() - height)/2;
-
-		if ( fullscreen ) {
-			/* Set offset for drawing */
-			SDL_Win->SetXYOffset(cx, cy);
-		} else {
-			SDL_Win->SetXYOffset(0, 0);
-		}
-		if ( ! needs_unlock || was_fullscreen ) {
-			/* Center the window the first time */
-			SDL_Win->MoveTo(cx, cy);
-		}
-		SDL_Win->Show();
 		
-		/* Unlock the window manually after the first Show() */
-		if ( needs_unlock ) {
-			SDL_Win->Unlock();
-		}
+		// And also center the window and reset the drawing offset.
+		SDL_Win->MoveTo(cx, cy);
+		SDL_Win->SetXYOffset(0, 0);
+	} else {
+		// printf("Doing fullscreen stuff.");
+		// Center the bitmap whenever we are in full screen mode.
+		SDL_Win->SetXYOffset(cx, cy);
 	}
-
-	/* Set the fullscreen flag in the screen surface */
-	if ( fullscreen ) {
+	
+	// Set relevant internal SDL screen flags.
+	if (SDL_Win->IsFullScreen()) {
 		screen->flags |= SDL_FULLSCREEN;
 	} else {
 		screen->flags &= ~SDL_FULLSCREEN; 
 	}
+
 	return(1);
 }
 
-- 
2.18.1