Vitaly Minko to slouken SDL-1.2
authorSam Lantinga <slouken@libsdl.org>
Mon, 28 Sep 2009 06:23:22 +0000
branchSDL-1.2
changeset 4256 ba587a51f899
parent 4255 5a203e2b0162
child 4257 14195cfdb66e
Vitaly Minko to slouken Hi all, I wrote a patch, which allows user to rotate the screen in case of fbcon driver. The rotation angle is controlled by SDL_VIDEO_FBCON_ROTATION environment variable and possible values are: not set - Not rotating, no shadow. "NONE" - Not rotating, but still using shadow. "CW" - Rotating screen clockwise. "UD" - Rotating screen upside down. "CCW" - Rotating screen counter clockwise. The patch is based on wscons driver, written by Staffan Ulfberg. I tested it on Device: Sharp Zaurus SL-C1000 SDL version: 1.2.13 Kernel version: 2.6.24.4 Best regards, Vitaly.
src/video/fbcon/SDL_fbvideo.c
src/video/fbcon/SDL_fbvideo.h
--- a/src/video/fbcon/SDL_fbvideo.c	Sun Sep 27 23:22:57 2009 +0000
+++ b/src/video/fbcon/SDL_fbvideo.c	Mon Sep 28 06:23:22 2009 +0000
@@ -126,6 +126,14 @@
 	{ 1600, 1200,/*?*/0, 272, 48, 32,  5, 152, 5, 0, 0 },	/* 60 Hz */
 #endif
 };
+enum {
+	FBCON_ROTATE_NONE = 0,
+	FBCON_ROTATE_CCW = 90,
+	FBCON_ROTATE_UD = 180,
+	FBCON_ROTATE_CW = 270
+};
+
+#define min(a,b) ((a)<(b)?(a):(b))
 
 /* Initialization/Query functions */
 static int FB_VideoInit(_THIS, SDL_PixelFormat *vformat);
@@ -153,6 +161,10 @@
                                   struct fb_var_screeninfo *vinfo);
 static void FB_RestorePalette(_THIS);
 
+/* Shadow buffer functions */
+static FB_bitBlit FB_blit16;
+static FB_bitBlit FB_blit16blocked;
+
 static int SDL_getpagesize(void)
 {
 #ifdef HAVE_GETPAGESIZE
@@ -491,6 +503,7 @@
 	unsigned int current_w;
 	unsigned int current_h;
 	const char *SDL_fbdev;
+	const char *rotation;
 	FILE *modesdb;
 
 	/* Initialize the library */
@@ -619,9 +632,55 @@
 		}
 	}
 
+	rotate = FBCON_ROTATE_NONE;
+	rotation = SDL_getenv("SDL_VIDEO_FBCON_ROTATION");
+	if (rotation != NULL) {
+		if (SDL_strlen(rotation) == 0) {
+			shadow_fb = 0;
+			rotate = FBCON_ROTATE_NONE;
+#ifdef FBCON_DEBUG
+			printf("Not rotating, no shadow\n");
+#endif
+		} else if (!SDL_strcmp(rotation, "NONE")) {
+			shadow_fb = 1;
+			rotate = FBCON_ROTATE_NONE;
+#ifdef FBCON_DEBUG
+			printf("Not rotating, but still using shadow\n");
+#endif
+		} else if (!SDL_strcmp(rotation, "CW")) {
+			shadow_fb = 1;
+			rotate = FBCON_ROTATE_CW;
+#ifdef FBCON_DEBUG
+			printf("Rotating screen clockwise\n");
+#endif
+		} else if (!SDL_strcmp(rotation, "CCW")) {
+			shadow_fb = 1;
+			rotate = FBCON_ROTATE_CCW;
+#ifdef FBCON_DEBUG
+			printf("Rotating screen counter clockwise\n");
+#endif
+		} else if (!SDL_strcmp(rotation, "UD")) {
+			shadow_fb = 1;
+			rotate = FBCON_ROTATE_UD;
+#ifdef FBCON_DEBUG
+			printf("Rotating screen upside down\n");
+#endif
+		} else {
+			SDL_SetError("\"%s\" is not a valid value for "
+				 "SDL_VIDEO_FBCON_ROTATION", rotation);
+			return(-1);
+		}
+	}
+
+	if (rotate == FBCON_ROTATE_CW || rotate == FBCON_ROTATE_CCW) {
+		current_w = vinfo.yres;
+		current_h = vinfo.xres;
+	} else {
+		current_w = vinfo.xres;
+		current_h = vinfo.yres;
+	}
+
 	/* Query for the list of available video modes */
-	current_w = vinfo.xres;
-	current_h = vinfo.yres;
 	current_index = ((vinfo.bits_per_pixel+7)/8)-1;
 	modesdb = fopen(FB_MODES_DB, "r");
 	for ( i=0; i<NUM_MODELISTS; ++i ) {
@@ -635,9 +694,14 @@
 			for ( i=0; i<NUM_MODELISTS; ++i ) {
 				unsigned int w, h;
 
+				if (rotate == FBCON_ROTATE_CW || rotate == FBCON_ROTATE_CCW) {
+					w = vinfo.yres;
+					h = vinfo.xres;
+				} else {
+					w = vinfo.xres;
+					h = vinfo.yres;
+				}
 				/* See if we are querying for the current mode */
-				w = vinfo.xres;
-				h = vinfo.yres;
 				if ( i == current_index ) {
 					if ( (current_w > w) || (current_h > h) ) {
 						/* Only check once */
@@ -657,9 +721,14 @@
 			for ( j=0; j<(sizeof(checkres)/sizeof(checkres[0])); ++j ) {
 				unsigned int w, h;
 
+				if (rotate == FBCON_ROTATE_CW || rotate == FBCON_ROTATE_CCW) {
+					w = checkres[j].h;
+					h = checkres[j].w;
+				} else {
+					w = checkres[j].w;
+					h = checkres[j].h;
+				}
 				/* See if we are querying for the current mode */
-				w = checkres[j].w;
-				h = checkres[j].h;
 				if ( i == current_index ) {
 					if ( (current_w > w) || (current_h > h) ) {
 						/* Only check once */
@@ -674,12 +743,12 @@
 		}
 	}
 
-	/* Fill in our hardware acceleration capabilities */
 	this->info.current_w = current_w;
 	this->info.current_h = current_h;
 	this->info.wm_available = 0;
-	this->info.hw_available = 1;
-	this->info.video_mem = finfo.smem_len/1024;
+	this->info.hw_available = !shadow_fb;
+	this->info.video_mem = shadow_fb ? 0 : finfo.smem_len/1024;
+	/* Fill in our hardware acceleration capabilities */
 	if ( mapped_io ) {
 		switch (finfo.accel) {
 		    case FB_ACCEL_MATROX_MGA2064W:
@@ -715,6 +784,14 @@
 		}
 	}
 
+	if (shadow_fb) {
+		shadow_mem = (char *)SDL_malloc(mapped_memlen);
+		if (shadow_mem == NULL) {
+			SDL_SetError("No memory for shadow");
+			return (-1);
+		} 
+	}
+
 	/* Enable mouse and keyboard support */
 	if ( FB_OpenKeyboard(this) < 0 ) {
 		FB_VideoQuit(this);
@@ -947,6 +1024,11 @@
 	fprintf(stderr, "Printing original vinfo:\n");
 	print_vinfo(&vinfo);
 #endif
+	/* Do not use double buffering with shadow buffer */
+	if (shadow_fb) {
+		flags &= ~SDL_DOUBLEBUF;
+	}
+
 	if ( (vinfo.xres != width) || (vinfo.yres != height) ||
 	     (vinfo.bits_per_pixel != bpp) || (flags & SDL_DOUBLEBUF) ) {
 		vinfo.activate = FB_ACTIVATE_NOW;
@@ -973,7 +1055,8 @@
 		fprintf(stderr, "Printing wanted vinfo:\n");
 		print_vinfo(&vinfo);
 #endif
-		if ( ioctl(console_fd, FBIOPUT_VSCREENINFO, &vinfo) < 0 ) {
+		if ( !shadow_fb &&
+				ioctl(console_fd, FBIOPUT_VSCREENINFO, &vinfo) < 0 ) {
 			vinfo.yres_virtual = height;
 			if ( ioctl(console_fd, FBIOPUT_VSCREENINFO, &vinfo) < 0 ) {
 				SDL_SetError("Couldn't set console screen info");
@@ -1029,26 +1112,54 @@
 	/* Save hardware palette, if needed */
 	FB_SavePalette(this, &finfo, &vinfo);
 
+	if (shadow_fb) {
+		if (vinfo.bits_per_pixel == 16) {
+			blitFunc = (rotate == FBCON_ROTATE_NONE ||
+					rotate == FBCON_ROTATE_UD) ?
+				FB_blit16 : FB_blit16blocked;
+		} else {
+			fprintf(stderr, "Init vinfo:\n");
+			print_vinfo(&vinfo);
+			SDL_SetError("Using software buffer, but no blitter "
+					"function is available for %d bpp.",
+					vinfo.bits_per_pixel);
+			return(NULL);
+		}
+	}
+
 	/* Set up the new mode framebuffer */
-	current->flags = (SDL_FULLSCREEN|SDL_HWSURFACE);
+	current->flags &= SDL_FULLSCREEN;
+	if (shadow_fb) {
+		current->flags |= SDL_SWSURFACE;
+	} else {
+		current->flags |= SDL_HWSURFACE;
+	}
 	current->w = vinfo.xres;
 	current->h = vinfo.yres;
-	current->pitch = finfo.line_length;
-	current->pixels = mapped_mem+mapped_offset;
+	if (shadow_fb) {
+		current->pitch = current->w * ((vinfo.bits_per_pixel + 7) / 8);
+		current->pixels = shadow_mem;
+		physlinebytes = finfo.line_length;
+	} else {
+		current->pitch = finfo.line_length;
+		current->pixels = mapped_mem+mapped_offset;
+	}
 
 	/* Set up the information for hardware surfaces */
 	surfaces_mem = (char *)current->pixels +
-	                        vinfo.yres_virtual*current->pitch;
-	surfaces_len = (mapped_memlen-(surfaces_mem-mapped_mem));
+		vinfo.yres_virtual*current->pitch;
+	surfaces_len = (shadow_fb) ?
+		0 : (mapped_memlen-(surfaces_mem-mapped_mem));
+
 	FB_FreeHWSurfaces(this);
 	FB_InitHWSurfaces(this, current, surfaces_mem, surfaces_len);
 
 	/* Let the application know we have a hardware palette */
 	switch (finfo.visual) {
-	    case FB_VISUAL_PSEUDOCOLOR:
+		case FB_VISUAL_PSEUDOCOLOR:
 		current->flags |= SDL_HWPALETTE;
 		break;
-	    default:
+		default:
 		break;
 	}
 
@@ -1059,7 +1170,7 @@
 			flip_page = 0;
 			flip_address[0] = (char *)current->pixels;
 			flip_address[1] = (char *)current->pixels+
-			                          current->h*current->pitch;
+				current->h*current->pitch;
 			this->screen = current;
 			FB_FlipHWSurface(this, current);
 			this->screen = NULL;
@@ -1329,10 +1440,169 @@
 	return(0);
 }
 
+static void FB_blit16(Uint8 *byte_src_pos, int src_right_delta, int src_down_delta,
+		Uint8 *byte_dst_pos, int dst_linebytes, int width, int height)
+{
+	int w;
+	Uint16 *src_pos = (Uint16 *)byte_src_pos;
+	Uint16 *dst_pos = (Uint16 *)byte_dst_pos;
+
+	while (height) {
+		Uint16 *src = src_pos;
+		Uint16 *dst = dst_pos;
+		for (w = width; w != 0; w--) {
+			*dst = *src;
+			src += src_right_delta;
+			dst++;
+		}
+		dst_pos = (Uint16 *)((Uint8 *)dst_pos + dst_linebytes);
+		src_pos += src_down_delta;
+		height--;
+	}
+}
+
+#define BLOCKSIZE_W 32
+#define BLOCKSIZE_H 32
+
+static void FB_blit16blocked(Uint8 *byte_src_pos, int src_right_delta, int src_down_delta, 
+		Uint8 *byte_dst_pos, int dst_linebytes, int width, int height)
+{
+	int w;
+	Uint16 *src_pos = (Uint16 *)byte_src_pos;
+	Uint16 *dst_pos = (Uint16 *)byte_dst_pos;
+
+	while (height > 0) {
+		Uint16 *src = src_pos;
+		Uint16 *dst = dst_pos;
+		for (w = width; w > 0; w -= BLOCKSIZE_W) {
+			FB_blit16((Uint8 *)src,
+					src_right_delta,
+					src_down_delta,
+					(Uint8 *)dst,
+					dst_linebytes,
+					min(w, BLOCKSIZE_W),
+					min(height, BLOCKSIZE_H));
+			src += src_right_delta * BLOCKSIZE_W;
+			dst += BLOCKSIZE_W;
+		}
+		dst_pos = (Uint16 *)((Uint8 *)dst_pos + dst_linebytes * BLOCKSIZE_H);
+		src_pos += src_down_delta * BLOCKSIZE_H;
+		height -= BLOCKSIZE_H;
+	}
+}
+
 static void FB_DirectUpdate(_THIS, int numrects, SDL_Rect *rects)
 {
-	/* The application is already updating the visible video memory */
-	return;
+	int width = cache_vinfo.xres;
+	int height = cache_vinfo.yres;
+	int bytes_per_pixel = (cache_vinfo.bits_per_pixel + 7) / 8;
+	int i;
+
+	if (!shadow_fb) {
+		/* The application is already updating the visible video memory */
+		return;
+	}
+
+	if (cache_vinfo.bits_per_pixel != 16) {
+		SDL_SetError("Shadow copy only implemented for 16 bpp");
+		return;
+	}
+
+	for (i = 0; i < numrects; i++) {
+		int x1, y1, x2, y2;
+		int scr_x1, scr_y1, scr_x2, scr_y2;
+		int sha_x1, sha_y1;
+		int shadow_right_delta;  /* Address change when moving right in dest */
+		int shadow_down_delta;   /* Address change when moving down in dest */
+		char *src_start;
+		char *dst_start;
+
+		x1 = rects[i].x; 
+		y1 = rects[i].y;
+		x2 = x1 + rects[i].w; 
+		y2 = y1 + rects[i].h;
+
+		if (x1 < 0) {
+			x1 = 0;
+		} else if (x1 > width) {
+			x1 = width;
+		}
+		if (x2 < 0) {
+			x2 = 0;
+		} else if (x2 > width) {
+			x2 = width;
+		}
+		if (y1 < 0) {
+			y1 = 0;
+		} else if (y1 > height) {
+			y1 = height;
+		}
+		if (y2 < 0) {
+			y2 = 0;
+		} else if (y2 > height) {
+			y2 = height;
+		}
+		if (x2 <= x1 || y2 <= y1) {
+			continue;
+		}
+
+		switch (rotate) {
+			case FBCON_ROTATE_NONE:
+				sha_x1 = scr_x1 = x1;
+				sha_y1 = scr_y1 = y1;
+				scr_x2 = x2;
+				scr_y2 = y2;
+				shadow_right_delta = 1;
+				shadow_down_delta = width;
+				break;
+			case FBCON_ROTATE_CCW:
+				scr_x1 = y1;
+				scr_y1 = width - x2;
+				scr_x2 = y2;
+				scr_y2 = width - x1;
+				sha_x1 = x2 - 1;
+				sha_y1 = y1;
+				shadow_right_delta = width;
+				shadow_down_delta = -1;
+				break;
+			case FBCON_ROTATE_UD:
+				scr_x1 = width - x2;
+				scr_y1 = height - y2;
+				scr_x2 = width - x1;
+				scr_y2 = height - y1;
+				sha_x1 = x2 - 1;
+				sha_y1 = y2 - 1;
+				shadow_right_delta = -1;
+				shadow_down_delta = -width;
+				break;
+			case FBCON_ROTATE_CW:
+				scr_x1 = height - y2;
+				scr_y1 = x1;
+				scr_x2 = height - y1;
+				scr_y2 = x2;
+				sha_x1 = x1;
+				sha_y1 = y2 - 1;
+				shadow_right_delta = -width;
+				shadow_down_delta = 1;
+				break;
+			default:
+				SDL_SetError("Unknown rotation");
+				return;
+		}
+
+		src_start = shadow_mem +
+			(sha_y1 * width + sha_x1) * bytes_per_pixel;
+		dst_start = mapped_mem + mapped_offset + scr_y1 * physlinebytes + 
+			scr_x1 * bytes_per_pixel;
+
+		blitFunc((Uint8 *) src_start,
+				shadow_right_delta, 
+				shadow_down_delta, 
+				(Uint8 *) dst_start,
+				physlinebytes,
+				scr_x2 - scr_x1,
+				scr_y2 - scr_y1);
+	}
 }
 
 #ifdef VGA16_FBCON_SUPPORT
--- a/src/video/fbcon/SDL_fbvideo.h	Sun Sep 27 23:22:57 2009 +0000
+++ b/src/video/fbcon/SDL_fbvideo.h	Mon Sep 28 06:23:22 2009 +0000
@@ -38,6 +38,14 @@
 /* Hidden "this" pointer for the video functions */
 #define _THIS	SDL_VideoDevice *this
 
+typedef void FB_bitBlit(
+		Uint8 *src_pos,
+		int src_right_delta,	/* pixels, not bytes */
+		int src_down_delta,		/* pixels, not bytes */
+		Uint8 *dst_pos,
+		int dst_linebytes,
+		int width,
+		int height);
 
 /* This is the structure we use to keep track of video memory */
 typedef struct vidmem_bucket {
@@ -69,12 +77,17 @@
 #endif
 
 	char *mapped_mem;
+	char *shadow_mem;
 	int mapped_memlen;
 	int mapped_offset;
 	char *mapped_io;
 	long mapped_iolen;
 	int flip_page;
 	char *flip_address[2];
+	int rotate;
+	int shadow_fb;				/* Tells whether a shadow is being used. */
+	FB_bitBlit *blitFunc;
+	int physlinebytes;			/* Length of a line in bytes in physical fb */
 
 #define NUM_MODELISTS	4		/* 8, 16, 24, and 32 bits-per-pixel */
 	int SDL_nummodes[NUM_MODELISTS];
@@ -110,12 +123,17 @@
 #define saved_cmaplen		(this->hidden->saved_cmaplen)
 #define saved_cmap		(this->hidden->saved_cmap)
 #define mapped_mem		(this->hidden->mapped_mem)
+#define shadow_mem		(this->hidden->shadow_mem)
 #define mapped_memlen		(this->hidden->mapped_memlen)
 #define mapped_offset		(this->hidden->mapped_offset)
 #define mapped_io		(this->hidden->mapped_io)
 #define mapped_iolen		(this->hidden->mapped_iolen)
 #define flip_page		(this->hidden->flip_page)
 #define flip_address		(this->hidden->flip_address)
+#define rotate			(this->hidden->rotate)
+#define shadow_fb		(this->hidden->shadow_fb)
+#define blitFunc		(this->hidden->blitFunc)
+#define physlinebytes		(this->hidden->physlinebytes)
 #define SDL_nummodes		(this->hidden->SDL_nummodes)
 #define SDL_modelist		(this->hidden->SDL_modelist)
 #define surfaces		(this->hidden->surfaces)