]> thyrsus.com Git - xve.git/commitdiff
Initial revision
authorEric S. Raymond <esr@thyrsus.com>
Tue, 25 Oct 1994 06:17:38 +0000 (06:17 +0000)
committerEric S. Raymond <esr@thyrsus.com>
Tue, 25 Oct 1994 06:17:38 +0000 (06:17 +0000)
xve.c [new file with mode: 0644]

diff --git a/xve.c b/xve.c
new file mode 100644 (file)
index 0000000..ccdce42
--- /dev/null
+++ b/xve.c
@@ -0,0 +1,785 @@
+/*
+ * 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 */