Added support for parsing /etc/fb.modes, based on Stephane Marchesin's patch
authorSam Lantinga <slouken@libsdl.org>
Mon, 02 Jan 2006 09:08:05 +0000
changeset 1215 d082d2d66ec8
parent 1214 31103dbf1c26
child 1216 f8fc6fdd589d
Added support for parsing /etc/fb.modes, based on Stephane Marchesin's patch
src/video/fbcon/SDL_fbvideo.c
--- a/src/video/fbcon/SDL_fbvideo.c	Mon Jan 02 08:07:41 2006 +0000
+++ b/src/video/fbcon/SDL_fbvideo.c	Mon Jan 02 09:08:05 2006 +0000
@@ -51,6 +51,7 @@
 #include "SDL_fbmatrox.h"
 #include "SDL_fbriva.h"
 
+/*#define FBCON_DEBUG*/
 
 #if defined(i386) && defined(FB_TYPE_VGA_PLANES)
 #define VGA16_FBCON_SUPPORT
@@ -234,6 +235,108 @@
 	FB_Available, FB_CreateDevice
 };
 
+#define FB_MODES_DB	"/etc/fb.modes"
+
+static int read_fbmodes_line(FILE*f, char* line, int length)
+{
+	int blank;
+	char* c;
+	int i;
+	
+	blank=0;
+	/* find a relevant line */
+	do
+	{
+		if (fgets(line,length,f)<=0)
+			return 0;
+		c=line;
+		while(((*c=='\t')||(*c==' '))&&(*c!=0))
+			c++;
+		
+		if ((*c=='\n')||(*c=='#')||(*c==0))
+			blank=1;
+		else
+			blank=0;
+	}
+	while(blank);
+	/* remove whitespace at the begining of the string */
+	i=0;
+	do
+	{
+		line[i]=c[i];
+		i++;
+	}
+	while(c[i]!=0);
+	return 1;
+}
+
+static int read_fbmodes_mode(FILE *f, struct fb_var_screeninfo *vinfo)
+{
+	char line[1024];
+	char option[256];
+
+	/* Find a "geometry" */
+	do {
+		if (read_fbmodes_line(f, line, sizeof(line))==0)
+			return 0;
+		if (strncmp(line,"geometry",8)==0)
+			break;
+	}
+	while(1);
+
+	sscanf(line, "geometry %d %d %d %d %d", &vinfo->xres, &vinfo->yres, 
+			&vinfo->xres_virtual, &vinfo->yres_virtual, &vinfo->bits_per_pixel);
+	if (read_fbmodes_line(f, line, sizeof(line))==0)
+		return 0;
+			
+	sscanf(line, "timings %d %d %d %d %d %d %d", &vinfo->pixclock, 
+			&vinfo->left_margin, &vinfo->right_margin, &vinfo->upper_margin, 
+			&vinfo->lower_margin, &vinfo->hsync_len, &vinfo->vsync_len);
+		
+	vinfo->sync=0;
+	vinfo->vmode=FB_VMODE_NONINTERLACED;
+				
+	/* Parse misc options */
+	do {
+		if (read_fbmodes_line(f, line, sizeof(line))==0)
+			return 0;
+
+		if (strncmp(line,"hsync",5)==0) {
+			sscanf(line,"hsync %s",option);
+			if (strncmp(option,"high",4)==0)
+				vinfo->sync |= FB_SYNC_HOR_HIGH_ACT;
+		}
+		else if (strncmp(line,"vsync",5)==0) {
+			sscanf(line,"vsync %s",option);
+			if (strncmp(option,"high",4)==0)
+				vinfo->sync |= FB_SYNC_VERT_HIGH_ACT;
+		}
+		else if (strncmp(line,"csync",5)==0) {
+			sscanf(line,"csync %s",option);
+			if (strncmp(option,"high",4)==0)
+				vinfo->sync |= FB_SYNC_COMP_HIGH_ACT;
+		}
+		else if (strncmp(line,"extsync",5)==0) {
+			sscanf(line,"extsync %s",option);
+			if (strncmp(option,"true",4)==0)
+				vinfo->sync |= FB_SYNC_EXT;
+		}
+		else if (strncmp(line,"laced",5)==0) {
+			sscanf(line,"laced %s",option);
+			if (strncmp(option,"true",4)==0)
+				vinfo->vmode |= FB_VMODE_INTERLACED;
+		}
+		else if (strncmp(line,"double",6)==0) {
+			sscanf(line,"double %s",option);
+			if (strncmp(option,"true",4)==0)
+				vinfo->vmode |= FB_VMODE_DOUBLE;
+		}
+	}
+	while(strncmp(line,"endmode",7)!=0);
+
+	return 1;
+}
+
 static int FB_CheckMode(_THIS, struct fb_var_screeninfo *vinfo,
                         int index, unsigned int *w, unsigned int *h)
 {
@@ -259,7 +362,7 @@
 	return mode_okay;
 }
 
-static int FB_AddMode(_THIS, int index, unsigned int w, unsigned int h)
+static int FB_AddMode(_THIS, int index, unsigned int w, unsigned int h, int check_timings)
 {
 	SDL_Rect *mode;
 	int i;
@@ -277,19 +380,21 @@
 	}
 
 	/* Only allow a mode if we have a valid timing for it */
-	next_mode = -1;
-	for ( i=0; i<(sizeof(vesa_timings)/sizeof(vesa_timings[0])); ++i ) {
-		if ( (w == vesa_timings[i].xres) &&
-		     (h == vesa_timings[i].yres) && vesa_timings[i].pixclock ) {
-			next_mode = i;
-			break;
+	if ( check_timings ) {
+		int found_timing = 0;
+		for ( i=0; i<(sizeof(vesa_timings)/sizeof(vesa_timings[0])); ++i ) {
+			if ( (w == vesa_timings[i].xres) &&
+			     (h == vesa_timings[i].yres) && vesa_timings[i].pixclock ) {
+				found_timing = 1;
+				break;
+			}
 		}
-	}
-	if ( next_mode == -1 ) {
+		if ( !found_timing ) {
 #ifdef FBCON_DEBUG
-		fprintf(stderr, "No valid timing line for mode %dx%d\n", w, h);
+			fprintf(stderr, "No valid timing line for mode %dx%d\n", w, h);
 #endif
-		return(0);
+			return(0);
+		}
 	}
 
 	/* Set up the new video mode rectangle */
@@ -323,6 +428,26 @@
 	return(0);
 }
 
+static int cmpmodes(const void *va, const void *vb)
+{
+    const SDL_Rect *a = *(const SDL_Rect**)va;
+    const SDL_Rect *b = *(const SDL_Rect**)vb;
+    if ( a->h == b->h )
+        return b->w - a->w;
+    else
+        return b->h - a->h;
+}
+
+static int FB_SortModes(_THIS)
+{
+	int i;
+	for ( i=0; i<NUM_MODELISTS; ++i ) {
+		if ( SDL_nummodes[i] > 0 ) {
+			qsort(SDL_modelist[i], SDL_nummodes[i], sizeof *SDL_modelist[i], cmpmodes);
+		}
+	}
+}
+
 static int FB_VideoInit(_THIS, SDL_PixelFormat *vformat)
 {
 	struct fb_fix_screeninfo finfo;
@@ -332,6 +457,7 @@
 	unsigned int current_w;
 	unsigned int current_h;
 	const char *SDL_fbdev;
+	FILE *modesdb;
 
 	/* Initialize the library */
 	SDL_fbdev = getenv("SDL_FBDEV");
@@ -463,12 +589,37 @@
 	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 ) {
+		SDL_nummodes[i] = 0;
+		SDL_modelist[i] = NULL;
+	}
 	if ( getenv("SDL_FB_BROKEN_MODES") != NULL ) {
-		FB_AddMode(this, current_index, current_w, current_h);
+		FB_AddMode(this, current_index, current_w, current_h, 0);
+	} else if(modesdb) {
+		while ( read_fbmodes_mode(modesdb, &vinfo) ) {
+			for ( i=0; i<NUM_MODELISTS; ++i ) {
+				unsigned int w, h;
+
+				/* 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 */
+						FB_AddMode(this, i, current_w, current_h, 0);
+						current_index = -1;
+					}
+				}
+				if ( FB_CheckMode(this, &vinfo, i, &w, &h) ) {
+					FB_AddMode(this, i, w, h, 0);
+				}
+			}
+		}
+		fclose(modesdb);
+		FB_SortModes(this);
 	} else {
 		for ( i=0; i<NUM_MODELISTS; ++i ) {
-			SDL_nummodes[i] = 0;
-			SDL_modelist[i] = NULL;
 			for ( j=0; j<(sizeof(checkres)/sizeof(checkres[0])); ++j ) {
 				unsigned int w, h;
 
@@ -478,12 +629,12 @@
 				if ( i == current_index ) {
 					if ( (current_w > w) || (current_h > h) ) {
 						/* Only check once */
-						FB_AddMode(this, i, current_w, current_h);
+						FB_AddMode(this, i, current_w, current_h, 0);
 						current_index = -1;
 					}
 				}
 				if ( FB_CheckMode(this, &vinfo, i, &w, &h) ) {
-					FB_AddMode(this, i, w, h);
+					FB_AddMode(this, i, w, h, 1);
 				}
 			}
 		}
@@ -611,13 +762,21 @@
 static int choose_fbmodes_mode(struct fb_var_screeninfo *vinfo)
 {
 	int matched;
-	FILE *fbmodes;
+	FILE *modesdb;
+	struct fb_var_screeninfo cinfo;
 
 	matched = 0;
-	fbmodes = fopen("/etc/fb.modes", "r");
-	if ( fbmodes ) {
-		/* FIXME: Parse the mode definition file */
-		fclose(fbmodes);
+	modesdb = fopen(FB_MODES_DB, "r");
+	if ( modesdb ) {
+		/* Parse the mode definition file */
+		while ( read_fbmodes_mode(modesdb, &cinfo) ) {
+			if ( vinfo->xres == cinfo.xres &&
+			     vinfo->yres == cinfo.yres ) {
+				matched = 1;
+				break;
+			}
+		}
+		fclose(modesdb);
 	}
 	return(matched);
 }