From 834866272131c4ee6c0927257e12a4c148db8060 Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Tue, 25 Oct 1994 06:17:38 +0000 Subject: [PATCH] Initial revision --- xve.c | 785 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 785 insertions(+) create mode 100644 xve.c diff --git a/xve.c b/xve.c new file mode 100644 index 0000000..ccdce42 --- /dev/null +++ b/xve.c @@ -0,0 +1,785 @@ +/* + * xve.c --- X Video Explorer + */ +#include +#include +#include +#include + +#ifndef TRUE +typedef int bool; +#define TRUE 1 +#define FALSE 0 +#endif /* !TRUE */ + +#ifndef PATH_MAX +#define PATH_MAX 256 +#endif + +#define KHZ 1000 +#define MHZ 1000000 +#define K 1024 + +#define NEW 0 +#define CLOCK 1 +#define DEPTH 2 +#define EDIT 3 +#define DONE 4 + +#ifndef XCONFIG +#define XCONFIG "/usr/X386/lib/X11/Xconfig" +#endif + +/************************************************************************** + * + * Globals + * + **************************************************************************/ + +/* location of the Xconfig file */ +static char *xconfig; + +/* informational stuff */ +static char card[PATH_MAX]; /* Card type (informational only) */ +static char monitor[PATH_MAX]; /* Monitor type (informational only) */ + +/* the components of our mini-spreadsheet */ +struct vparam +{ + char *tag; + float value; + int level; + char *legend; +} +parameters[] = +{ + /* basic monitor parameters */ + {"HSF", 31.5, NEW, "Maximum horizontal scan frequency (in KHz)"}, + {"VSF", 60, NEW, "Maximum vertical scan frequency (in Hz)"}, + {"VB", 65, NEW, "Video bandwidth (in MHz)"}, + + /* basic card parameters */ + {"VRAM", 1024, NEW, "The card's on-board VRAM (in K)"}, + + /* dot clock */ + {"DCF", 0, CLOCK, "The card's dot clock frequency (in MHz)"}, + + /* optional monitor parameters (the defaults work for these) */ + {"HSP", 3.8, NEW, "Horizontal sync pulse width (microseconds)"}, + {"HGT", 30, NEW, "Guard pixels for horizontal sync pulse"}, + {"VSP", 150, NEW, "Vertical sync pulse width (in microseconds)"}, + {"VGT", 3, NEW, "Guard pixels for vertical sync pulse"}, + + /* sizes and sync pulses */ + {"HFL", 0, CLOCK, "Horizontal frame length (pixels)"}, + {"VFL", 0, CLOCK, "Vertical frame length (pixels)"}, + {"HR", 0, CLOCK, "Horizontal resolution (pixels)"}, + {"VR", 0, CLOCK, "Vertical resolution (pixels)"}, + {"RR", 0, CLOCK, "Refresh rate (screens per second)"}, + {"HS1", 0, CLOCK, "Leading edge of horizontal sync (ticks)"}, + {"HS2", 0, CLOCK, "Trailing edge of horizontal sync (ticks)"}, + {"VS1", 0, CLOCK, "Leading edge of verical sync (ticks)"}, + {"VS2", 0, CLOCK, "Trailing edge of verical sync (ticks)"}, + + /* pixel depth */ + {"BPP", 8, DEPTH, "Bits per pixel"}, + +}; + +#define hsf parameters[0] +#define vsf parameters[1] +#define vb parameters[2] + +#define vram parameters[3] + +#define dcf parameters[4] + +#define hsp parameters[5] +#define hgt parameters[6] +#define vsp parameters[7] +#define vgt parameters[8] + +#define hfl parameters[9] +#define vfl parameters[10] +#define hr parameters[11] +#define vr parameters[12] +#define rr parameters[13] +#define hs1 parameters[14] +#define hs2 parameters[15] +#define vs1 parameters[16] +#define vs2 parameters[17] + +#define bpp parameters[18] + +#define for_params(sp) \ + for (sp = parameters; \ + sp < parameters + sizeof(parameters)/sizeof(struct vparam); \ + sp++) + +/* available clock timings */ +static int nclocks; +static float clocks[16]; + +/************************************************************************** + * + * Query functions + * + **************************************************************************/ + +static char buf[BUFSIZ]; + +static void getstring(char *sparam, const char *prompt) +/* get a string value (default on bare \n) */ +{ + if (sparam[0] == '\0') + (void) printf("%s [unknown]: ", prompt, sparam); + else + (void) printf("%s [%s]: ", prompt, sparam); + (void) fgets(buf, BUFSIZ, stdin); + + if (buf[0] != '\n') + { + (void) strcpy(sparam, buf); + sparam[strlen(sparam) - 1] = '\0'; + } +} + +static void getfloat(struct vparam *p) +/* accept a new value for a given parameter */ +{ + float floatval; + + if (p->value == 0) + (void) printf("%s: %s [unknown]: ", p->tag, p->legend); + else + (void) printf("%s: %s [%4.2f]: ", p->tag, p->legend, p->value); + (void) fgets(buf, BUFSIZ, stdin); + if (sscanf(buf, "%f\n", &floatval) == 1) + p->value = floatval; +} + +static void getint(struct vparam *p) +/* accept a new value for a given parameter */ +{ + float floatval; + + if (p->value == 0) + (void) printf("%s: %s [unknown]: ", p->tag, p->legend); + else + (void) printf("%s: %s [%d]: ", p->tag, p->legend, (int)p->value); + (void) fgets(buf, BUFSIZ, stdin); + if (sscanf(buf, "%f\n", &floatval) == 1) + p->value = floatval; +} + +static char getchoice(const char *prompt, const char *opts, const char deflt) +{ + (void) printf("%s? [%c] ", prompt, deflt); + (void) fgets(buf, BUFSIZ, stdin); + + if (strchr(opts, buf[0])) + return(buf[0]); + else + return(deflt); +} + +static bool yornp(const char *prompt, const bool deflt) +{ + (void) printf("%s? [%c] ", prompt, deflt ? 'y' : 'c'); + (void) fgets(buf, BUFSIZ, stdin); + + if (buf[0] == 'y' || buf[0] == 'Y') + return(TRUE); + if (buf[0] == 'n' || buf[0] == 'N') + return(FALSE); + + return(deflt); +} + +static int floatcmp(const float *f1, const float *f2) +/* reverse-compare floats, so we get decreasing order */ +{ + return((int) (*f2 - *f1)); +} + +static void getclocks(char *bfr) +/* fill the clocks array */ +{ + char clockbuf[BUFSIZ], *cp; + + if (bfr == (char *)NULL) + (void) fgets(clockbuf, BUFSIZ, stdin); + else + (void) strcpy(clockbuf, bfr); + + nclocks = 0; + cp = strtok(clockbuf, " \t,"); + do { + clocks[nclocks++] = atof(cp); + } while + (cp = strtok((char *)NULL, " \t,")); + + qsort(clocks, nclocks, sizeof(float), floatcmp); + + /* lose trailing zero entries */ + while (clocks[nclocks-1] == 0) + nclocks--; +} + +static void redisplay(struct vparam *lo, struct vparam *hi) +/* display a given range of parameters */ +{ + struct vparam *sp; + + if (lo == &dcf) + { + int i; + + (void) printf("CARD: %s \t# Your card type\n", card); + (void) printf("MONITOR: %s \t# Your monitor type\n", monitor); + + (void) printf("CLOCKS:"); + for (i = 0; i < nclocks; i++) + if (clocks[i]) + (void) printf(" %4.2f", clocks[i]); + (void) printf("\n"); + } + + for (sp = lo; sp <= hi; sp++) + if (sp->value) + (void) printf("%s: %4.2f \t# %s\n", sp->tag,sp->value,sp->legend); + + (void) putchar('\n'); +} + +/************************************************************************** + * + * Parameter file handling + * + **************************************************************************/ + +static char path[PATH_MAX]; +static FILE *pfp; + +#define PREFIX(key) (strncmp(key, buf, strlen(key)) == 0) + +static bool read_config() +/* read configuration from file */ +{ + char buf[BUFSIZ]; + + (void) sprintf(path, "%s/.xve", getenv("HOME")); + + if ((pfp = fopen(path, "r")) == (FILE *)NULL) + return(FALSE); + + while (fgets(buf, BUFSIZ, pfp) != (char *)NULL) + { + struct vparam *sp; + char *cp; + + if (cp = strchr(buf, '#')) + { + while (isspace(*--cp)) + continue; + cp[1] = '\0'; + } + + if (PREFIX("CARD:")) + (void) sscanf(buf, "%*s %[^\n]", card); + else if (PREFIX("MONITOR:")) + (void) sscanf(buf, "%*s %[^\n]", monitor); + else if (PREFIX("CLOCKS:")) + getclocks(buf + 7); + else + { + for_params(sp) + if (PREFIX(sp->tag)) + (void) sscanf(buf, "%*s %f", &sp->value); + } + } + + (void) fclose(pfp); + + return(TRUE); +} + +void write_config(const int level) +/* write out parameter values to the save file */ +{ + struct vparam *sp; + int i; + + /* read_config() must have run already for this to work */ + if ((pfp = fopen(path, "w")) == (FILE *)NULL) + return; + + (void) fprintf(pfp, "# X386 Video Space Explorer parameters\n"); + (void) fprintf(pfp, "CARD: %s \t# Your card type\n", card); + (void) fprintf(pfp, "MONITOR: %s \t# Your monitor type\n", monitor); + + (void) fprintf(pfp, "CLOCKS:"); + for (i = 0; i < nclocks; i++) + if (clocks[i]) + (void) fprintf(pfp, " %4.2f", clocks[i]); + (void) fprintf(pfp, "\n"); + + for_params(sp) + if (sp->value && sp->level <= level) + (void) fprintf(pfp,"%s: %4.2f \t# %s\n", sp->tag, sp->value, sp->legend); + + (void) fclose(pfp); +} + +/************************************************************************** + * + * Parameter pickers + * + * These functions automate the three stages of picking a trial configuration. + * They have to run in order; each one takes as input parameters set by the + * previous one. + * + **************************************************************************/ + +static void new_config() +/* accept basic parameters for a completely new card/monitor combination */ +{ + FILE *fp; + + getstring(card, "Your card type"); + getstring(monitor, "Your monitor type"); + + (void) printf("We need to know some things to get started\n\n"); + + /* see if we can mine the clocks vector out of the X config file */ + nclocks = 0; + if ((fp = fopen(xconfig, "r")) != (FILE *)NULL) + { + char *cp; + + while (fgets(buf, BUFSIZ, fp) != (char *)NULL) + if (buf[0] != '#' && (cp = strstr(buf, "Clocks"))) + { + cp += 7; + getclocks(cp); + break; + } + (void) fclose(fp); + + if (nclocks) + { + (void) printf("I found these timings in %s: %s\n", xconfig, cp); + if (!yornp("Use them", TRUE)) + nclocks = 0; + } + } + + if (nclocks == 0) + { + (void) printf("Please enter the available clock timings reported by the X386 banner line:\n"); + (void) printf("CLOCKS: "); + getclocks((char *)NULL); + } + + /* get basic params */ + getint(&vram); + + getfloat(&hsf); + getfloat(&vsf); + + /* get optional params */ + (void) printf("\nIf you don't know these values, use the defaults.\n"); + + getfloat(&vb); + if (vb.value == 0) + { + (void) printf("We can estimate the monitor's video bandwidth from its highest-rated\n"); + (void) printf("resolution. Pick one of the following:\n"); + (void) printf(" a = 640x480 b = 800x600 c = 1024x768 d = 1280x1024\n"); + switch (getchoice("Which one?", "abcd", 'c')) + { + case 'a': + vb.value = 25; + break; + + case 'b': + vb.value = 36; + break; + + case 'c': + vb.value = 65; + break; + + case 'd': + vb.value = 110; + break; + } + } + + getfloat(&hsp); + getint(&hgt); + getfloat(&vsp); + getint(&vgt); +} + +static void recalc() +/* recalculate derived parameters */ +{ + float deadcenter, syncwidth, excess; + + /* compute our first hack at a horizontal frame length */ + hfl.value = (dcf.value * MHZ) / (hsf.value * KHZ); + + /* horizontal resolution */ + hr.value = hfl.value * 0.8; + + /* now round the frame length to nearest multiple of 8 */ + excess = fmodf(hfl.value, 8); + hfl.value = hfl.value - excess + 8 * (excess > 4); + + /* vertical resolution */ + vr.value = hr.value * 0.75; + + /* vertical frame length */ + vfl.value = vr.value * 1.05; + + /* trial-position the sync pulses */ + + /* center the sync pulse in the inactive part */ + deadcenter = (hfl.value + hr.value) / 2; + syncwidth = (hsp.value * dcf.value); + hs1.value = (int) (deadcenter - syncwidth / 2); + hs2.value = (int) (deadcenter + syncwidth / 2); + + /* we don't center the vertical sync pulse */ + syncwidth = (vsp.value * dcf.value) / hfl.value; + vs1.value = (int) (vr.value + vgt.value); + vs2.value = (int) (vs1.value + syncwidth); + + /* figure the refresh rate */ + rr.value = (dcf.value * MHZ) / (hfl.value * vfl.value); +} + +static void new_clock() +/* select a clock speed */ +{ + for (;;) + { + int i; + bool found = FALSE; + + (void) printf("Available dot clocks are:"); + for (i = 0; i < nclocks; i++) + if (clocks[i]) + (void) printf(" %4.2f", clocks[i]); + (void) printf("\n"); + + for (i = 0; i < nclocks; i++) + if (clocks[i] <= vb.value) + break; + dcf.value = clocks[i]; + getfloat(&dcf); + + for (i = 0; i < nclocks; i++) + if (clocks[i] == dcf.value) + found =TRUE; + if (!found) + continue; + + if (vb.value < dcf.value * .7) + (void) printf("Your video bandwidth is probably too low for a %4.2f dot clock.\n", dcf.value); + else + break; + } + + recalc(); +} + +static void new_depth() +/* get a new pixel depth for the display */ +{ + for (;;) + { + getint(&bpp); + + if (bpp.value!=1 && bpp.value!=2 && bpp.value!=4 && bpp.value!=4 && bpp.value!=8) + (void) printf("Only 1, 2, 4, or 8-bit displays are supported\n"); + else + break; + } +} + +/************************************************************************** + * + * Parameter tweaking and recalculation + * + **************************************************************************/ + +static struct vparam controls[] = +{ +#define lsh controls[0] + {"LSH", 1, EDIT, "Left shift (in units of 8 pixels)"}, +#define rsh controls[1] + {"RSH", 1, EDIT, "Right shift (in units of 8 pixels)"}, +}; + +static int tweak_config() +{ + int newlevel, c; + float diff; + + + c = '\0'; + for (;;) + { + if (c) + c = getchoice("Command", "abx", 'x'); + else + { + (void) printf("\nRecomputing...\n"); + c = '?'; + } + + switch (c) + { + case 'a': /* change HSP */ + diff = (hsp.value * dcf.value); + getfloat(&hsp); + diff -= (hsp.value * dcf.value); + hs1.value += (diff / 2); + hs2.value -= (diff / 2); + break; + + case 'b': /* change VSP width */ + diff = (vsp.value * dcf.value) / vfl.value; + getfloat(&vsp); + diff -= (vsp.value * dcf.value) / vfl.value; + vs1.value += (diff / 2); + vs2.value -= (diff / 2); + break; + + + case 'c': /* shift left */ + getint(&lsh); + hs1.value += lsh.value; + hs2.value += lsh.value; + break; + + case 'd': /* shift right */ + getint(&rsh); + hs1.value += rsh.value; + hs2.value += rsh.value; + break; + + case 'x': + return(DONE); + + default: + (void) printf("Available commands are:\n"); + /* FALL THROUGH */ + case '?': + (void) printf("a -- change horizontal sync pulse width (HSP).\n"); + (void) printf("b -- change vertical sync pulse width (VSP).\n"); + (void) printf("c -- shift image to the left.\n"); + (void) printf("d -- shift image to the right.\n"); + (void) printf("? -- display commands.\n"); + (void) printf("x -- return to main menu.\n"); + break; + } + + redisplay(&hfl, &vs2); + + (void) printf("Current timing mumbers: %d %d %d %d %d %d %d %d\n", + (int)hr.value, (int)hs1.value, (int)hs2.value, (int)hfl.value, + (int)vr.value, (int)vs1.value, (int)vs2.value, (int)vfl.value); + + if ((newlevel = sanecheck(EDIT)) < EDIT) + return(newlevel); + (void) putchar('\n'); + } +} + +/************************************************************************** + * + * Main sequence + * + **************************************************************************/ + +static int sanecheck(const int level) +/* check for configuration problems */ +{ +#define P (void) printf + if (level >= CLOCK) + { + bool sync_ok = TRUE; + int minhfl; + + if (hs1.value <= hr.value) + { + P("Horizontal sync pulse overlaps the visible area by %.0f ticks.\n", + (hr.value - hs1.value) + 1); + sync_ok = FALSE; + } + else if (hs1.value - hgt.value <= hr.value) + { + P("Horizontal guard time overlaps the visible area by %.0f ticks.\n", + (hr.value - (hs1.value - hgt.value)) + 1); + sync_ok = FALSE; + } + + if (hfl.value <= hs2.value) + { + P("Horizontal sync pulse overlaps the end of scan by %.0f ticks.\n", + (hs2.value - hs2.value) + 1); + sync_ok = FALSE; + } + else if (hfl.value <= hs2.value + hgt.value) + { + P("Horizontal guard time overlaps the end of scan by %.0f ticks.\n", + (hs2.value + hgt.value - hfl.value) + 1); + sync_ok = FALSE; + } + + if (vs1.value <= vr.value) + { + P("Vertical sync pulse overlaps the visible area by %.0f ticks.\n", + (vr.value - vs1.value)); + sync_ok = FALSE; + } + else if (vs1.value - vgt.value <= vr.value) + { + P("Vertical guard time overlaps the visible area by %.0f ticks.\n", + (vr.value - (vs1.value - vgt.value)) + 1); + sync_ok = FALSE; + } + + if (vfl.value <= vs2.value) + { + P("Vertical sync pulse overlaps the end of scan by %.0f ticks.\n", + (vs2.value - vfl.value) + 1); + sync_ok = FALSE; + } + + minhfl = (int) ((dcf.value * MHZ) / (hsf.value * KHZ)); + minhfl -= fmodf(minhfl, 8); + if (hfl.value < minhfl) + { + (void) printf("Horizontal frame length must be at least %.0f at this clock speed.\n", minhfl); + sync_ok =FALSE; + } + + if (rr.value > vsf.value) + { + (void) printf("You have pushed the refresh rate above the vertical sync frequency.\n"); + sync_ok =FALSE; + } + else if (rr.value < 45) + P("%4.2fHz refresh will hurt your eyes.\n", rr.value); + else if (rr.value < 60) + P("%4.2fHz refresh is pretty poor.\n", rr.value); + else if (rr.value < 72) + P("%4.2fHz refresh is marginally OK.\n", rr.value); + + if (!sync_ok) + return(EDIT); + } + + if (level >= DEPTH && vr.value * hr.value * bpp.value > vram.value * 8 * K) + { + P("You don't have enough VRAM for %dx%dx%d.\n", + (int)hr.value, (int)vr.value, (int)bpp.value); + P("You'll have to configure for either fewer colors\n"); + P("or lower resolution (that is, a lower dot clock).\n\n"); + return(CLOCK); + } +#undef P + + if (level == DONE) + return(DONE); + else + return(level + 1); +} + +main(int argc, char **argv) +{ + int enable; + char command; + + (void) printf("Welcome to the X Video Explorer.\n\n"); + + if (argc > 1) + xconfig = argv[1]; + else + xconfig = XCONFIG; + + if (read_config()) + { + (void) printf("Loading configuration from %s...\n", path); + + redisplay(&dcf, &vs2); + + if (dcf.value == 0) + enable = CLOCK; + else + enable = EDIT; + command = ' '; + } + else + { + /* force new configuration */ + (void) printf("No parameter file.\n\n"); + command = '0'; + } + + for (;;) + { + int newlevel; + + if (command == ' ') + { + (void) printf("Commands available:\n"); + (void) printf(" 0 - New video card or monitor\n"); + if (enable >= CLOCK) + (void) printf(" 1 - New dot clock value\n"); + if (enable >= DEPTH) + (void) printf(" 2 - New pixel depth\n"); + if (enable >= EDIT) + (void) printf(" 3 - Tweak a configuration\n"); + if (enable >= DONE) + (void) printf(" 4 - Save configuration and exit\n"); + command = getchoice("Command", "01234", '0' + enable); + (void) printf("\n"); + } + + switch(command) + { + case '0': + new_config(); + if (yornp("Save card & monitor parameters", TRUE)) + write_config(NEW); + /* FALL THROUGH */ + case '1': + new_clock(); + /* FALL THROUGH */ + case '2': + new_depth(); + /* FALL THROUGH */ + case '3': + if ((enable = tweak_config()) < DONE) + break; + /* FALL THROUGH */ + case '4': + write_config(DONE); + exit(0); + break; + + default: + (void) printf("Unknown command, please try again.\n"); + continue; + } + + command = ' '; + } +} + +/* xve.c ends here */ -- 2.43.0