--- /dev/null
+/*
+ * xve.c --- X Video Explorer
+ */
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <ctype.h>
+
+#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 */