/*
** Astrolog (Version 4.40) File: xscreen.c
**
** IMPORTANT NOTICE: The graphics database and chart display routines
** used in this program are Copyright (C) 1991-1995 by Walter D. Pullen
** (astara@u.washington.edu). Permission is granted to freely use and
** distribute these routines provided one doesn't sell, restrict, or
** profit from them in any way. Modification is allowed provided these
** notices remain with any altered or edited versions of the program.
**
** The main planetary calculation routines used in this program have
** been Copyrighted and the core of this program is basically a
** conversion to C of the routines created by James Neely as listed in
** Michael Erlewine's 'Manual of Computer Programming for Astrologers',
** available from Matrix Software. The copyright gives us permission to
** use the routines for personal use but not to sell them or profit from
** them in any way.
**
** The PostScript code within the core graphics routines are programmed
** and Copyright (C) 1992-1993 by Brian D. Willoughby
** (brianw@sounds.wa.com). Conditions are identical to those above.
**
** The extended accurate ephemeris databases and formulas are from the
** calculation routines in the program "Placalc" and are programmed and
** Copyright (C) 1989,1991,1993 by Astrodienst AG and Alois Treindl
** (alois@azur.ch). The use of that source code is subject to
** regulations made by Astrodienst Zurich, and the code is not in the
** public domain. This copyright notice must not be changed or removed
** by any user of this program.
**
** Initial programming 8/28,30, 9/10,13,16,20,23, 10/3,6,7, 11/7,10,21/1991.
** X Window graphics initially programmed 10/23-29/1991.
** PostScript graphics initially programmed 11/29-30/1992.
** Last code change made 1/29/1995.
*/

#include "astrolog.h"
#ifdef ATARI				/* Ajout Abel PHILIPPE */
#include <screen.h>
void BGIDriver(void);
#endif

#ifdef GRAPH
/*
******************************************************************************
** Astrolog Icon.
******************************************************************************
*/

#ifdef X11
/* This information used to define Astrolog's X icon (Rainbow over Third */
/* Eye) is identical to the output format used by the bitmap program.    */
/* You could extract this section and run xsetroot -bitmap on it.        */

#define icon_width 63
#define icon_height 32
static char icon_bits[] = {
 0x00,0x00,0x00,0xa8,0x0a,0x00,0x00,0x00,0x00,0x00,0x40,0x55,0x55,0x01,0x00,
 0x00,0x00,0x00,0xa8,0xaa,0xaa,0x0a,0x00,0x00,0x00,0x00,0x54,0xf5,0x57,0x15,
 0x00,0x00,0x00,0x80,0xaa,0xaa,0xaa,0xaa,0x00,0x00,0x00,0x40,0xd5,0xff,0xff,
 0x55,0x01,0x00,0x00,0xa0,0xaa,0xaa,0xaa,0xaa,0x02,0x00,0x00,0x50,0xfd,0xff,
 0xff,0x5f,0x05,0x00,0x00,0xa8,0xaa,0x2a,0xaa,0xaa,0x0a,0x00,0x00,0xd4,0xff,
 0xaf,0xfa,0xff,0x15,0x00,0x00,0xaa,0x2a,0x00,0x00,0xaa,0x2a,0x00,0x00,0xf5,
 0xbf,0xaa,0xaa,0xfe,0x57,0x00,0x80,0xaa,0x02,0x00,0x00,0xa0,0xaa,0x00,0x40,
 0xfd,0xab,0xfa,0xaf,0xea,0x5f,0x01,0xa0,0xaa,0x80,0xff,0xff,0x80,0xaa,0x02,
 0x50,0xff,0xea,0xff,0xff,0xab,0x7f,0x05,0xa0,0x2a,0xf0,0xff,0xff,0x07,0xaa,
 0x02,0xd0,0xbf,0xfa,0x0f,0xf8,0xaf,0x7e,0x05,0xa8,0x0a,0xfc,0x01,0xc0,0x1f,
 0xa8,0x0a,0xd4,0xaf,0x7e,0x00,0x00,0xbf,0xfa,0x15,0xa8,0x0a,0x3f,0x00,0x00,
 0x7e,0xa8,0x0a,0xf4,0xaf,0x1f,0xe0,0x03,0xfc,0xfa,0x15,0xaa,0x82,0x0f,0xdc,
 0x1d,0xf8,0xa0,0x2a,0xf4,0xab,0x07,0x23,0x62,0xf0,0xea,0x17,0xaa,0xc2,0x87,
 0x91,0xc4,0xf0,0xa1,0x2a,0xf4,0xeb,0xc3,0xd0,0x85,0xe1,0xeb,0x17,0xaa,0xe0,
 0x83,0x91,0xc4,0xe0,0x83,0x2a,0xf5,0xeb,0x03,0x23,0x62,0xe0,0xeb,0x57,0xaa,
 0xe0,0x01,0xdc,0x1d,0xc0,0x83,0x2a,0xf5,0xeb,0x01,0xe0,0x03,0xc0,0xeb,0x57,
 0xaa,0xe0,0x01,0x00,0x00,0xc0,0x83,0x2a,0xfd,0xeb,0x01,0x00,0x00,0xc0,0xeb,
 0x5f};
#endif


/*
******************************************************************************
** Interactive Screen Graphics Routines.
******************************************************************************
*/

/* Set up all the colors used by the program, i.e. the foreground and   */
/* background colors, and all the colors in the object arrays, based on */
/* whether or not we are in monochrome and/or reverse video mode.       */

void InitColorsX()
{
  int i;
#ifdef X11
  Colormap cmap;
  XColor xcol;

  if (!gi.fFile) {
    cmap = XDefaultColormap(gi.disp, gi.screen);

    /* Allocate a color from the present X11 colormap. Given a string like */
    /* "violet", allocate this color and return a value specifying it.     */

    for (i = 0; i < 16; i++) {
      XParseColor(gi.disp, cmap, szColorX[i], &xcol);
      XAllocColor(gi.disp, cmap, &xcol);
      rgbind[i] = xcol.pixel;
    }
  }
#endif
  gi.kiOn   = kMainA[!gs.fInverse];
  gi.kiOff  = kMainA[gs.fInverse];
  gi.kiLite = gs.fColor ? kMainA[2+gs.fInverse] : gi.kiOn;
  gi.kiGray = gs.fColor ? kMainA[3-gs.fInverse] : gi.kiOn;
  for (i = 0; i <= 8; i++)
    kMainB[i]    = gs.fColor ? kMainA[i]    : gi.kiOn;
  for (i = 0; i <= 7; i++)
    kRainbowB[i] = gs.fColor ? kRainbowA[i] : gi.kiOn;
  for (i = 0; i < 4; i++)
    kElemB[i]    = gs.fColor ? kElemA[i]    : gi.kiOn;
  for (i = 0; i <= cAspect; i++)
    kAspB[i]     = gs.fColor ? kAspA[i]     : gi.kiOn;
  for (i = 0; i <= cObj; i++)
    kObjB[i]     = gs.fColor ? kObjA[i]     : gi.kiOn;
#ifdef X11
  if (!gi.fFile) {
    XSetBackground(gi.disp, gi.gc,   rgbind[gi.kiOff]);
    XSetForeground(gi.disp, gi.pmgc, rgbind[gi.kiOff]);
  }
#endif
}


#ifdef ISG
/* This routine opens up and initializes a window and prepares it to be */
/* drawn upon, and gets various information about the display, too.     */

void BeginX()
{
#ifdef X11
  gi.disp = XOpenDisplay(gs.szDisplay);
  if (gi.disp == NULL) {
    PrintError("Can't open display.");
    Terminate(tcFatal);
  }
  gi.screen = DefaultScreen(gi.disp);
  bg = BlackPixel(gi.disp, gi.screen);
  fg = WhitePixel(gi.disp, gi.screen);
  hint.x = gi.xOffset; hint.y = gi.yOffset;
  hint.width = gs.xWin; hint.height = gs.yWin;
  hint.min_width = BITMAPX1; hint.min_height = BITMAPY1;
  hint.max_width = BITMAPX;  hint.max_height = BITMAPY;
  hint.flags = PPosition | PSize | PMaxSize | PMinSize;
#if FALSE
  wmhint = XGetWMHints(gi.disp, gi.wind);
  wmhint->input = True;
  XSetWMHints(gi.disp, gi.wind, wmhint);
#endif
  gi.depth = DefaultDepth(gi.disp, gi.screen);
  if (gi.depth < 5) {
    gi.fMono = fTrue;      /* Is this a monochrome monitor? */
    gs.fColor = fFalse;
  }
  gi.root = RootWindow(gi.disp, gi.screen);
  if (gs.fRoot)
    gi.wind = gi.root;     /* If -XB in effect, we'll use the root window. */
  else
    gi.wind = XCreateSimpleWindow(gi.disp, DefaultRootWindow(gi.disp),
      hint.x, hint.y, hint.width, hint.height, 5, fg, bg);
  gi.pmap = XCreatePixmap(gi.disp, gi.wind, gs.xWin, gs.yWin, gi.depth);
  gi.icon = XCreateBitmapFromData(gi.disp, DefaultRootWindow(gi.disp),
    icon_bits, icon_width, icon_height);
  if (!gs.fRoot)
    XSetStandardProperties(gi.disp, gi.wind, szAppName, szAppName, gi.icon,
      (char **)xkey, 0, &hint);

  /* We have two graphics workareas. One is what the user currently sees in */
  /* the window, and the other is what we are currently drawing on. When    */
  /* done, we can quickly copy this to the viewport for a smooth look.      */

  gi.gc = XCreateGC(gi.disp, gi.wind, 0, 0);
  XSetGraphicsExposures(gi.disp, gi.gc, 0);
  gi.pmgc = XCreateGC(gi.disp, gi.wind, 0, 0);
  InitColorsX();                           /* Go set up colors. */
  if (!gs.fRoot)
    XSelectInput(gi.disp, gi.wind, KeyPressMask | StructureNotifyMask |
      ExposureMask | ButtonPressMask | ButtonReleaseMask | ButtonMotionMask);
  XMapRaised(gi.disp, gi.wind);
  XSync(gi.disp, 0);
  XFillRectangle(gi.disp, gi.pmap, gi.pmgc, 0, 0, gs.xWin, gs.yWin);
#endif /* X11 */

#ifdef MSG
  if (!FValidResmode(gi.nRes))    /* Initialize graphics mode to hi-res. */
    gi.nRes = gs.nResHi;
  _setvideomode(gi.nRes);
  if (_grstatus()) {
    PrintError("Can't enter graphics mode.");
    Terminate(tcFatal);
  }
  _getvideoconfig((struct videoconfig far *) &gi.cfg);
  if (gi.cfg.numcolors < 16) {
    gi.fMono  = fTrue;
    gs.fColor = fFalse;
  }
  _remapallpalette((long FAR *) rgb);
  _setactivepage(0);
  _setvisualpage(0);
  InitColorsX();
#ifdef MOUSE
  MouseInit(xPcScreen, yPcScreen);
#endif
  /* Make sure we reset textrows upon restart. */
  gs.nTextRows = abs(gs.nTextRows);
#endif /* MSG */

#ifdef BGI
  int i;
  static struct palettetype pal;

  if (!FValidResmode(gi.nRes))    /* Initialize graphics mode to hi-res. */
    gi.nRes = gs.nResHi;
  if (!gi.fLoaded) {
#ifdef ATARI
    registerbgidriver(BGIDriver);
#else										/* Ajout Abel PHILIPPE */
    registerfarbgidriver(ATT_driver_far);      /* attf.obj     */
    registerfarbgidriver(CGA_driver_far);      /* cgaf.obj     */
    registerfarbgidriver(EGAVGA_driver_far);   /* egavgaf.obj  */
    registerfarbgidriver(Herc_driver_far);     /* hercf.obj    */
    registerfarbgidriver(IBM8514_driver_far);  /* ibm8514f.obj */
    registerfarbgidriver(PC3270_driver_far);   /* pc3270f.obj  */
#endif							/* Ajout Abel PHILIPPE */
    gi.nDriver = DETECT;
    initgraph(&gi.nDriver, &gi.nGraph, "");
    gi.fLoaded = fTrue;
  }
  if (gi.nRes <= 0) {
    switch (gi.nDriver) {
    case CGA:      gi.nGraph = CGAHI;      break;
    case MCGA:     gi.nGraph = MCGAHI;     break;
    case EGA:      gi.nGraph = EGAHI;      break;
    case EGA64:    gi.nGraph = EGA64HI;    break;
    case EGAMONO:  gi.nGraph = EGAMONOHI;  break;
    case HERCMONO: gi.nGraph = HERCMONOHI; break;
    case ATT400:   gi.nGraph = ATT400HI;   break;
    case VGA:      gi.nGraph = VGAHI;      break;
    case PC3270:   gi.nGraph = PC3270HI;   break;
    case IBM8514:  gi.nGraph = IBM8514HI;  break;
    default:       gi.nGraph = 0;
    }
  } else {
    switch (gi.nDriver) {
    case CGA:      gi.nGraph = CGAHI;      break;
    case MCGA:     gi.nGraph = MCGAHI;     break;
    case EGA:      gi.nGraph = EGAHI;      break;
    case EGA64:    gi.nGraph = EGA64HI;    break;
    case EGAMONO:  gi.nGraph = EGAMONOHI;  break;
    case HERCMONO: gi.nGraph = HERCMONOHI; break;
    case ATT400:   gi.nGraph = ATT400HI;   break;
    case VGA:      gi.nGraph = VGAMED;     break;
    case PC3270:   gi.nGraph = PC3270HI;   break;
    case IBM8514:  gi.nGraph = IBM8514LO;  break;
    default:       gi.nGraph = 0;
    }
  }
#ifdef ATARI								/* Ajout Abel PHILIPPE */
  Cur_off();
#endif
  setgraphmode(gi.nGraph);
  if (graphresult()) {
    PrintError("Can't enter graphics mode.");
    Terminate(tcFatal);
  }
  gi.nPages = 1 + (gi.nDriver == HERCMONO ||
    (gi.nDriver == VGA && gi.nGraph != VGAHI) || gi.nDriver == EGA);
  if (getmaxcolor()+1 < 16) {
    gi.fMono  = fTrue;
    gs.fColor = fFalse;
  }
  getpalette(&pal);
  for (i = 0; i < pal.size; i++)
    pal.colors[i] = (char)rgb[i];
  setallpalette(&pal);
  setactivepage(0);
  setvisualpage(0);
  gi.nPageCur = 0;
  InitColorsX();
#ifdef MOUSE
  MouseInit(xPcScreen, yPcScreen);
#endif
  /* Make sure we reset textrows upon restart. */
  gs.nTextRows = abs(gs.nTextRows);
#endif /* BGI */
}


/* Add a certain amount of time to the current hour/day/month/year quantity */
/* defining the present chart. This is used by the chart animation feature. */
/* We can add or subtract anywhere from 1 to 9 seconds, minutes, hours,     */
/* days, months, years, decades, centuries, or millenia in any one call.    */
/* This is mainly just addition to the appropriate quantity, but we have    */
/* to check for overflows, e.g. Dec 30 + 3 days = Jan 2 of Current year + 1 */

void AddTime(mode, toadd)
int mode, toadd;
{
  int d;
  real h, m;

  if (!FBetween(mode, 1, 9))
    mode = 4;

  h = RFloor(TT);
  m = RFract(TT)*100.0;
  if (mode == 1)
    m += 1.0/60.0*(real)toadd;    /* Add seconds. */
  else if (mode == 2)
    m += (real)toadd;             /* add minutes. */

  /* Add hours, either naturally or if minute value overflowed. */

  if (m < 0.0 || m >= 60.0 || mode == 3) {
    if (m >= 60.0) {
      m -= 60.0; toadd = NSgn(toadd);
    } else if (m < 0.0) {
      m += 60.0; toadd = NSgn(toadd);
    }
    h += (real)toadd;
  }

  /* Add days, either naturally or if hour value overflowed. */

  if (h >= 24.0 || h < 0.0 || mode == 4) {
    if (h >= 24.0) {
      h -= 24.0; toadd = NSgn(toadd);
    } else if (h < 0.0) {
      h += 24.0; toadd = NSgn(toadd);
    }
    DD = AddDay(MM, DD, YY, toadd);
  }

  /* Add months, either naturally or if day value overflowed. */

  if (DD > (d = DayInMonth(MM, YY)) || DD < 1 || mode == 5) {
    if (DD > d) {
      DD -= d; toadd = NSgn(toadd);
    } else if (DD < 1) {
      DD += DayInMonth(Mod12(MM - 1), YY);
      toadd = NSgn(toadd);
    }
    MM += toadd;
  }

  /* Add years, either naturally or if month value overflowed. */

  if (MM > 12 || MM < 1 || mode == 6) {
    if (MM > 12) {
      MM -= 12; toadd = NSgn(toadd);
    } else if (MM < 1) {
      MM += 12; toadd = NSgn(toadd);
    }
    YY += toadd;
  }
  if (mode == 7)
    YY += 10 * toadd;      /* Add decades.   */
  else if (mode == 8)
    YY += 100 * toadd;     /* Add centuries. */
  else if (mode == 9)
    YY += 1000 * toadd;    /* Add millenia.  */
  TT = h+m/100.0;          /* Recalibrate hour time. */
}


/* Animate the current chart based on the given values indicating how much  */
/* to update by. We update and recast the current chart info appropriately. */

void Animate(mode, toadd)
int mode, toadd;
{
  if (gi.nMode == gWorldMap || gi.nMode == gGlobe || gi.nMode == gPolar) {
    gs.nRot += toadd;
    if (gs.nRot >= nDegMax)     /* For animating globe display, add */
      gs.nRot -= nDegMax;       /* in appropriate degree value.     */
    else if (gs.nRot < 0)
      gs.nRot += nDegMax;
  } else {
    if (mode == 10) {
#ifdef TIME
      /* For the continuous chart update to present moment */
      /* animation mode, go get whatever time it is now.   */
      FInputData(szNowCore);
#else
      ciCore = ciMain;
      AddTime(1, toadd);
#endif
    } else {  /* Otherwise add on appropriate time vector to chart info. */
      ciCore = ciMain;
      AddTime(mode, toadd);
    }
    ciMain = ciCore;
    if (us.nRel)
      CastRelation(fFalse);
    else
      CastChart(fTrue);
  }
}


/* This routine exits graphics mode, prompts the user for a set of command */
/* switches, processes them, and returns to the previous graphics with the */
/* new settings in effect, allowing one to change most any setting without */
/* having to lose their graphics state or fall way back to a -Q loop.      */

void CommandLineX()
{
  char szCommandLine[cchSzMax], *rgsz[MAXSWITCHES];
  int argc, fT, fPause = fFalse;

  ciCore = ciMain;
#ifdef MSG
  _setvideomode(_DEFAULTMODE);
  _settextrows(gs.nTextRows);
#endif
#ifdef BGI
  restorecrtmode();
#ifdef ATARI					/* Ajout Abel PHILIPPE */
  Cur_on();
#else
  if (gs.nTextRows > 25)
    textmode(C4350);
#endif							/* Ajout Abel PHILIPPE */
#endif
  fT = us.fLoop; us.fLoop = fTrue;
  argc = NPromptSwitches(szCommandLine, rgsz);
  is.cchRow = 0;
  is.fSzInteract = fTrue;
  if (!FProcessSwitches(argc, rgsz))
    fPause = fTrue;
  else {
    is.fMult = fFalse;
    FPrintTables();
    if (is.fMult) {
      ClearB((lpbyte)&us.fCredit,
        (int)((lpbyte)&us.fLoop - (lpbyte)&us.fCredit));
      fPause = fTrue;
    }
  }

#ifdef PCG
  /* Pause for the user if there was either an error processing the    */
  /* switches, or one of the informational text tables was brought up. */

  if (fPause) {
    AnsiColor(kDefault);
    is.cchRow = 0;
    PrintSz("Press any key to return to graphics.\n");
    while (!kbhit())
      ;
    getch();
  }
#endif
  is.fSzInteract = fFalse;
  us.fLoop = fT;
  ciMain = ciCore;
  BeginX();
}


/* Given two chart size values, adjust them such that the chart will look */
/* "square". We round the higher value down and check certain conditions. */

void SquareX(x, y, force)
int *x, *y, force;
{
  if (!force && !fSquare)    /* Unless we want to force a square, realize */
    return;                  /* that some charts look better rectangular. */
  if (*x > *y)
    *x = *y;
  else
    *y = *x;
#ifdef PCG
  if (FEgaRes(gi.nRes))         /* Scale horizontal size if we're in a PC */
    *x = VgaFromEga(*x);        /* graphics mode without "square" pixels. */
  else if (FCgaRes(gi.nRes))
    *x = VgaFromCga(*x);
#endif
  if (fSidebar)      /* Take into account chart's sidebar, if any. */
    *x += xSideT;
}


/* This routine gets called after graphics are brought up and displayed     */
/* on the screen. It loops, processing key presses, mouse clicks, etc, that */
/* the window receives, until the user specifies they want to exit program. */

void InteractX()
{
#ifdef X11
  char sz[cchSzDef];
  XEvent xevent;
  KeySym keysym;
  int fResize = fFalse, fRedraw = fTrue;
#else /* PCG */
#ifdef MOUSE
  int eventx, eventy, eventbtn;
#endif
  int fResize = fTrue, fRedraw = fFalse;
#endif /* PCG */
  int fBreak = fFalse, fPause = fFalse, fCast = fFalse, xcorner = 7,
#ifdef MOUSE
    mousex = -1, mousey = -1, buttonx = -1, buttony = -1,
#endif
    dir = 1, length, key, i;
  bool fT;
#ifdef MOUSE
  KI coldrw = gi.kiLite;
#endif

  neg(gs.nAnim);
  while (!fBreak) {
    gi.nScale = gs.nScale/100;

    /* Some chart windows, like the world maps and aspect grids, should */
    /* always be a certian size, so correct if a resize was attempted.  */

    if (fMap) {
      length = nDegMax*gi.nScale;
      if (gs.xWin != length) {
        gs.xWin = length;
        fResize = fTrue;
      }
      length = nDegHalf*gi.nScale;
      if (gs.yWin != length) {
        gs.yWin = length;
        fResize = fTrue;
      }
    } else if (gi.nMode == gGrid) {
      if (gs.xWin != (length =
        (gs.nGridCell + (us.nRel <= rcDual))*CELLSIZE*gi.nScale+1)) {
        gs.xWin = length;
        fResize = fTrue;
      } if (gs.yWin != length) {
        gs.yWin = length;
        fResize = fTrue;
      }

    /* Make sure the window isn't too large or too small. */

    } else {
      if (gs.xWin < BITMAPX1) {
        gs.xWin = BITMAPX1;
        fResize = fTrue;
      } else if (gs.xWin > BITMAPX) {
        gs.xWin = BITMAPX;
        fResize = fTrue;
      }
      if (gs.yWin < BITMAPY1) {
        gs.yWin = BITMAPY1;
        fResize = fTrue;
      } else if (gs.yWin > BITMAPY) {
        gs.yWin = BITMAPY;
        fResize = fTrue;
      }
    }

    /* If in animation mode, ensure we are in the flicker free resolution. */

    if (gs.nAnim < 0) {
      neg(gs.nAnim);
#ifdef PCG
      if (gi.nRes == gs.nResHi && !gs.fJetTrail) {
        gi.nRes = gs.nResLo;
        BeginX();
        gs.xWin = xPcScreen;
        gs.yWin = yPcScreen;
        SquareX(&gs.xWin, &gs.yWin, fFalse);
        fResize = fTrue;
      }
#endif
    }

    /* Physically resize window if we've changed the size parameters. */

    if (fResize) {
      fResize = fFalse;
#ifdef X11
      XResizeWindow(gi.disp, gi.wind, gs.xWin, gs.yWin);
      XFreePixmap(gi.disp, gi.pmap);
      gi.pmap = XCreatePixmap(gi.disp, gi.wind, gs.xWin, gs.yWin, gi.depth);
#else
      if (xPcScreen > gs.xWin)
        gi.xOffset = (xPcScreen - gs.xWin) / 2;
      else {
        if (xcorner % 3 == 1)
          gi.xOffset = 0;
        else if (xcorner % 3 == 0)
          gi.xOffset = -gs.xWin + xPcScreen;
        else
          gi.xOffset = -(gs.xWin - xPcScreen) / 2;
      }
      if (yPcScreen > gs.yWin)
        gi.yOffset = (yPcScreen - gs.yWin) / 2;
      else {
        if (xcorner > 6)
          gi.yOffset = 0;
        else if (xcorner < 4)
          gi.yOffset = -gs.yWin + yPcScreen;
        else
          gi.yOffset = -(gs.yWin - yPcScreen) / 2;
      }
#endif
      fRedraw = fTrue;
    }

    /* Recast chart if the chart information has changed any. */

    if (fCast) {
      fCast = fFalse;
      ciCore = ciMain;
      if (us.nRel)
        CastRelation(fFalse);
      else
        CastChart(fTrue);
      fRedraw = fTrue;
    }
    if (gs.nAnim && !fPause)
      fRedraw = fTrue;

    /* Update the screen if anything has changed since last time around. */

    if (fRedraw) {
      fRedraw = fFalse;

      /* If we're in animation mode, change the chart info appropriately. */

      if (gs.nAnim && !fPause)
        Animate(gs.nAnim, dir);

      /* Clear the screen and set up a buffer to draw in. */

#ifdef X11
      XFillRectangle(gi.disp, gi.pmap, gi.pmgc, 0, 0, gs.xWin, gs.yWin);
#else /* PCG */
#ifdef MOUSE
      MouseShow(fFalse);
#endif
#ifdef MSG
      if (gi.cfg.numvideopages > 1)
        _setactivepage(_getactivepage() == gs.fJetTrail);
#else
      if (gi.nPages > 1) {
        gi.nPageCur = (gi.nPageCur == gs.fJetTrail);
        setactivepage(gi.nPageCur);
      }
#endif
#endif /* PCG */

      DrawChartX();

      /* Make the drawn chart visible in the current screen buffer. */

#ifdef X11
      XSync(gi.disp, 0);
      XCopyArea(gi.disp, gi.pmap, gi.wind, gi.gc,
        0, 0, gs.xWin, gs.yWin, 0, 0);
#else /* PCG */
#ifdef MSG
      if (gi.cfg.numvideopages > 1)
        _setvisualpage(_getactivepage());
#else
      if (gi.nPages > 1)
        setvisualpage(gi.nPageCur);
#endif
#ifdef MOUSE
      if (!gs.nAnim || fPause)
        MouseShow(fTrue);
#endif
#endif /* PCG */
    }  /* if */

    /* Now process what's on the event queue, i.e. any keys pressed, etc. */

#ifdef X11
    if (XEventsQueued(gi.disp, QueuedAfterFlush /*QueuedAfterReading*/) ||
      !gs.nAnim || fPause) {
      XNextEvent(gi.disp, &xevent);

      /* Restore what's on window if a part of it gets uncovered. */

      if (xevent.type == Expose && xevent.xexpose.count == 0) {
        XSync(gi.disp, 0);
        XCopyArea(gi.disp, gi.pmap, gi.wind, gi.gc,
          0, 0, gs.xWin, gs.yWin, 0, 0);
      }
      switch (xevent.type) {

      /* Check for a manual resize of window by user. */

      case ConfigureNotify:
        gs.xWin = xevent.xconfigure.width;
        gs.yWin = xevent.xconfigure.height;
        XFreePixmap(gi.disp, gi.pmap);
        gi.pmap = XCreatePixmap(gi.disp, gi.wind, gs.xWin, gs.yWin, gi.depth);
        fRedraw = fTrue;
        break;
      case MappingNotify:
        XRefreshKeyboardMapping((XMappingEvent *)&xevent);
        break;

#ifdef MOUSE
      /* Process any mouse buttons the user pressed. */

      case ButtonPress:
        mousex = xevent.xbutton.x; mousey = xevent.xbutton.y;
        if (xevent.xbutton.button == Button1) {
          DrawColor(gi.kiLite);
          DrawPoint(mousex, mousey);
          XSync(gi.disp, 0);
          XCopyArea(gi.disp, gi.pmap, gi.wind, gi.gc,
            0, 0, gs.xWin, gs.yWin, 0, 0);
        } else if (xevent.xbutton.button == Button2 && (gi.nMode ==
          gAstroGraph || gi.nMode == gWorldMap) && gs.nRot == 0) {
          Lon = DegToDec(rDegHalf -
            (real)(xevent.xbutton.x-1)/(real)(gs.xWin-2)*rDegMax);
          Lat = DegToDec(rDegQuad -
            (real)(xevent.xbutton.y-1)/(real)(gs.yWin-2)*181.0);
          sprintf(sz, "Mouse is at %s.", SzLocation(Lon, Lat));
          PrintNotice(sz);
        } else if (xevent.xbutton.button == Button3)
          fBreak = fTrue;
        break;

      /* Check for user dragging any of the mouse buttons across window. */

      case MotionNotify:
        DrawColor(coldrw);
        DrawLine(mousex, mousey, xevent.xbutton.x, xevent.xbutton.y);
        XSync(gi.disp, 0);
        XCopyArea(gi.disp, gi.pmap, gi.wind, gi.gc,
          0, 0, gs.xWin, gs.yWin, 0, 0);
        mousex = xevent.xbutton.x; mousey = xevent.xbutton.y;
        break;
#endif

      /* Process any keys user pressed in window. */

      case KeyPress:
        length = XLookupString((XKeyEvent *)&xevent, xkey, 10, &keysym, 0);
        if (length == 1) {
          key = xkey[0];
#else /* PCG */
#ifdef MOUSE
      if ((!gs.nAnim || fPause) && MouseStatus(&eventx, &eventy, &eventbtn)) {

        /* If the left button is down, draw on the screen. */
        if (eventbtn == mfLeft && mousex >= 0) {
          MouseShow(fFalse);
          DrawColor(coldrw);
          PcMoveTo(mousex, mousey);
          buttonx = eventx; buttony = eventy;
          PcLineTo(buttonx, buttony);

        /* If the right button is down, change the default location. */
        } else if (eventbtn == mfRight) {
          if (fMap && gs.nRot == 0 && !gs.fConstel && !gs.fMollewide) {
            Lon = rDegHalf-(real)(eventx-gi.xOffset)/(real)(gs.xWin-2)*rDegMax;
            if (Lon < -rDegHalf)
              Lon = -rDegHalf;
            else if (Lon > rDegHalf)
              Lon = rDegHalf;
            Lat =  rDegQuad-(real)(eventy-gi.yOffset)/(real)(gs.yWin-2)*181.0;
            if (Lat < -rDegQuad)
              Lat = -rDegQuad;
            else if (Lat > rDegQuad)
              Lat = rDegQuad;
            fCast = fTrue;

          /* Right button means draw lines if not in a world map mode. */
          } else if (buttonx >= 0) {
            MouseShow(fFalse);
            DrawColor(coldrw);
            PcMoveTo(buttonx, buttony);
            PcLineTo(eventx, eventy);
          }

        /* Middle button (which most PC's don't have) means exit program. */
        } else if (eventbtn == mfMiddle)
          fBreak = fTrue;

        mousex = eventx; mousey = eventy;
        MouseShow(fTrue);
      } else
#endif /* MOUSE */
        if (kbhit()) {
          key = getch();
#endif /* PCG */
LSwitch:
          switch (key) {
#ifdef PCG
          case chNull:
            key = NFromAltN(getch());
            goto LSwitch;
#endif
          case ' ':
            fRedraw = fTrue;
            break;
          case 'p':
            not(fPause);
            break;
          case 'r':
            neg(dir);
            break;
          case 'x':
            not(gs.fInverse);
            InitColorsX();
            fRedraw = fTrue;
            break;
          case 'm':
            if (!gi.fMono) {
              not(gs.fColor);
#ifdef MSG
              _getvideoconfig((struct videoconfig far *) &gi.cfg);
#endif
              InitColorsX();
              fRedraw = fTrue;
            }
            break;
          case 'B':
#ifdef X11
            XSetWindowBackgroundPixmap(gi.disp, gi.root, gi.pmap);
            XClearWindow(gi.disp, gi.root);
#else
            gs.xWin = xPcScreen;
            gs.yWin = yPcScreen;
            SquareX(&gs.xWin, &gs.yWin, fFalse);
            fResize = fTrue;
#endif
            break;
          case 't':
            not(gs.fText);
            fRedraw = fTrue;
            break;
          case 'i':
            not(gs.fAlt);
            fRedraw = fTrue;
            break;
          case 'b':
            not(gs.fBorder);
            fRedraw = fTrue;
            break;
          case 'l':
            not(gs.fLabel);
            fRedraw = fTrue;
            break;
          case 'j':
            not(gs.fJetTrail);
            break;
          case '<':
            if (gs.nScale > 100) {
              gs.nScale -= 100;
              fResize = fTrue;
            }
            break;
          case '>':
            if (gs.nScale < 400) {
              gs.nScale += 100;
              fResize = fTrue;
            }
            break;
          case '[':
            if (gi.nMode == gGlobe && gs.rTilt > -rDegQuad) {
              gs.rTilt = gs.rTilt > -rDegQuad ? gs.rTilt-11.25 : -rDegQuad;
              fRedraw = fTrue;
            }
            break;
          case ']':
            if (gi.nMode == gGlobe && gs.rTilt < rDegQuad) {
              gs.rTilt = gs.rTilt < rDegQuad ? gs.rTilt+11.25 : rDegQuad;
              fRedraw = fTrue;
            }
            break;
          case 'Q':
            SquareX(&gs.xWin, &gs.yWin, fTrue);
            fResize = fTrue;
            break;
          case 'R':
            for (i = oChi; i <= oVes; i++)
              not(ignore[i]);
            for (i = oLil; i <= oEP; i++)
              not(ignore[i]);
            fCast = fTrue;
            break;
          case 'C':
            not(us.fCusp);
            for (i = cuspLo; i <= cuspHi; i++)
              ignore[i] = !us.fCusp || !ignore[i];
            fCast = fTrue;
            break;
          case 'u':
            not(us.fUranian);
            for (i = uranLo; i <= uranHi; i++)
              ignore[i] = !us.fUranian || !ignore[i];
            fCast = fTrue;
            break;
          case 'U':
            us.nStar = !us.nStar;
            for (i = starLo; i <= starHi; i++)
              ignore[i] = !us.nStar || !ignore[i];
            fCast = fTrue;
            break;
          case 'c':
            if (!us.nRel) {
              us.nRel = rcDual;
              ciTwin = ciMain;
            } else
              us.nRel = 0;
            fCast = fTrue;
            break;
          case 's':
            not(us.fSiderial);
            fCast = fTrue;
            break;
          case 'h':
            us.objCenter = us.objCenter ? 0 : 1;
            fCast = fTrue;
            break;
          case 'f':
            not(us.fFlip);
            fCast = fTrue;
            break;
          case 'g':
            not(us.fDecan);
            fCast = fTrue;
            break;
          case '+':
            Animate(gs.nAnim, abs(dir));
            fCast = fTrue;
            break;
          case '-':
            Animate(gs.nAnim, -abs(dir));
            fCast = fTrue;
            break;
          case 'o':
            ciSave = ciMain;
            break;
          case 'O':
            ciMain = ciSave;
            fCast = fTrue;
            break;
#ifdef TIME
          case 'n':
            FInputData(szNowCore);
            ciMain = ciCore;
            fCast = fTrue;
            break;
#endif
          case 'N':                     /* The continuous update animation. */
            gs.nAnim = gs.nAnim ? 0 : -10;
            break;

          /* These are the nine different "add time to chart" animations. */
          case '!': gs.nAnim = -1; break;
          case '@': gs.nAnim = -2; break;
          case '#': gs.nAnim = -3; break;
          case '$': gs.nAnim = -4; break;
          case '%': gs.nAnim = -5; break;
          case '^': gs.nAnim = -6; break;
          case '&': gs.nAnim = -7; break;
          case '*': gs.nAnim = -8; break;
          case '(': gs.nAnim = -9; break;

          /* Should we go switch to a new chart type? */
          case 'V': gi.nMode = gWheel;      fRedraw = fTrue; break;
          case 'A': gi.nMode = gGrid;       fRedraw = fTrue; break;
          case 'Z': gi.nMode = gHorizon;    fRedraw = fTrue; break;
          case 'S': gi.nMode = gOrbit;      fRedraw = fTrue; break;
          case 'J': gi.nMode = gDisposit;   fRedraw = fTrue; break;
          case 'L': gi.nMode = gAstroGraph; fRedraw = fTrue; break;
          case 'K': gi.nMode = gCalendar;   fRedraw = fTrue; break;
          case 'E': gi.nMode = gEphemeris;  fRedraw = fTrue; break;
          case 'W': gi.nMode = gWorldMap;   fRedraw = fTrue; break;
          case 'G': gi.nMode = gGlobe;      fRedraw = fTrue; break;
          case 'P': gi.nMode = gPolar;      fRedraw = fTrue; break;
#ifdef BIORHYTHM
          case 'Y':            /* Should we switch to biorhythm chart? */
            if (!us.nRel)
              ciTwin = ciMain;
            us.nRel = rcBiorhythm;
            gi.nMode = gBiorhythm;
            fCast = fTrue;
            break;
#endif
#ifdef CONSTEL
          case 'F':
            if (!fMap && gi.nMode != gGlobe && gi.nMode != gPolar)
              gi.nMode = gWorldMap;
            not(gs.fConstel);
            fRedraw = fTrue;
            break;
#endif
          case '0':
            not(us.fPrimeVert);
            not(us.fCalendarYear);
            not(us.nEphemYears);
            not(gs.fMollewide);
            gi.nMode = (gi.nMode == gWheel ? gHouse :
              (gi.nMode == gHouse ? gWheel : gi.nMode));
            fRedraw = fTrue;
            break;
          case 'v': case 'H': case '?':
#ifdef MSG
            _setvideomode(_DEFAULTMODE);
            if (key != 'v')
              _settextrows(50);
#endif
#ifdef BGI
            restorecrtmode();
#ifdef ATARI						/* Ajout Abel PHILIPPE */
            Cur_on();
#else
            if (key != 'v')
              textmode(C4350);
#endif								/* Ajout Abel PHILIPPE */
#endif
            length = us.nScrollRow;
            us.nScrollRow = 0;
            if (key == 'v')
              ChartListing();
            else
              DisplayKeysX();
            us.nScrollRow = length;
#ifdef PCG
            while (!kbhit())
              ;
            key = getch();
            if (key == 'q' || key == chEscape || key == chBreak) {
              fBreak = fTrue;
              break;
            }
            BeginX();
            fResize = fTrue;
#endif
            break;
          case chReturn:
            CommandLineX();
            fResize = fCast = fTrue;
            break;
#ifdef PCG
          case chTab:
            if (gi.nRes == gs.nResHi)
              gi.nRes = gs.nResLo;
            else
              gi.nRes = gs.nResHi;
            BeginX();
            gs.xWin = xPcScreen;
            gs.yWin = yPcScreen;
            SquareX(&gs.xWin, &gs.yWin, fFalse);
            fResize = fTrue;
            break;
#endif
          case chDelete:
#ifdef PCG
#ifdef MOUSE
            MouseShow(fFalse);
#endif
#endif /* PCG */
            fT = gs.fJetTrail;
            gs.fJetTrail = fFalse;
            DrawClearScreen();
            gs.fJetTrail = fT;
            break;
#ifdef MOUSE
          case 'z'-'`': coldrw = kBlack;   break;
          case 'e'-'`': coldrw = kMaroon;  break;
          case 'f'-'`': coldrw = kDkGreen; break;
          case 'o'-'`': coldrw = kOrange;  break;
          case 'n'-'`': coldrw = kDkBlue;  break;
          case 'u'-'`': coldrw = kPurple;  break;
          case 'k'-'`': coldrw = kDkCyan;  break;
          case 'l'-'`': coldrw = kLtGray;  break;
          case 'd'-'`': coldrw = kDkGray;  break;
          case 'r'-'`': coldrw = kRed;     break;
          case 'g'-'`': coldrw = kGreen;   break;
          case 'y'-'`': coldrw = kYellow;  break;
          case 'b'-'`': coldrw = kBlue;    break;
          case 'v'-'`': coldrw = kMagenta; break;
          case 'j'-'`': coldrw = kCyan;    break;
          case 'a'-'`': coldrw = kWhite;   break;
#ifdef PCG
          case 't'-'`':
            MouseShow(fFalse);
            if (buttonx >= 0)
#ifdef MSG
              _rectangle(_GBORDER, buttonx, buttony, mousex, mousey);
#else
              DrawEdge(Min(buttonx, mousex) - gi.xOffset,
                Min(buttony, mousey) - gi.yOffset,
                Max(mousex, buttonx) - gi.xOffset,
                Max(mousey, buttony) - gi.yOffset);
#endif
            MouseShow(fTrue);
            break;
          case 'x'-'`':
            MouseShow(fFalse);
            if (buttonx >= 0)
#ifdef MSG
              _ellipse(_GBORDER, buttonx, buttony, mousex, mousey);
#else
              DrawEllipse(Min(buttonx, mousex) - gi.xOffset,
                Min(buttony, mousey) - gi.yOffset,
                Max(mousex, buttonx) - gi.xOffset,
                Max(mousey, buttony) - gi.yOffset);
#endif
            MouseShow(fTrue);
            break;
#endif /* PCG */
#endif /* MOUSE */
          case 'q': case chEscape: case chBreak:
            fBreak = fTrue;
            break;
          default:
            if (key > '0' && key <= '9') {
#ifdef PCG
              if (gs.nAnim && !fPause)
#endif
                /* Process numbers 1..9 signifying animation rate. */
                dir = (dir > 0 ? 1 : -1)*(key-'0');
#ifdef PCG
              else {
                /* If we aren't in animation mode, then 1..9 refers to the */
                /* clipping "quadrant" to use if chart size > screen size. */
                xcorner = key-'0';
                fResize = fTrue;
              }
#endif
              break;
            } else if (FBetween(key, 201, 248)) {
              is.fSzInteract = fTrue;
              if (szMacro[key-201]) {
                FProcessCommandLine(szMacro[key-201]);
                fResize = fCast = fTrue;
              }
              is.fSzInteract = fFalse;
              break;
            }
            putchar(chBell);    /* Any key not bound will sound a beep. */
          }  /* switch */
        }  /* if */
#ifdef X11
      default:
        ;
      }  /* switch */
    }  /* if */
#endif
  }  /* while */
}


/* This is called right before program termination to get rid of the window. */

void EndX()
{
#ifdef X11
  XFreeGC(gi.disp, gi.gc);
  XFreeGC(gi.disp, gi.pmgc);
  XFreePixmap(gi.disp, gi.pmap);
  XDestroyWindow(gi.disp, gi.wind);
  XCloseDisplay(gi.disp);
#endif
#ifdef MSG
  _setvideomode(_DEFAULTMODE);
#endif
#ifdef BGI
  restorecrtmode();
#ifdef ATARI						/* Ajout Abel PHILIPPE */
  Cur_on();
#endif
#endif
}
#endif /* ISG */


/*
******************************************************************************
** Main Graphics Processing.
******************************************************************************
*/

/* Process one command line switch passed to the program dealing with the    */
/* graphics features. This is just like the processing of each switch in the */
/* main program, however here each switch has been prefixed with an 'X'.     */

int NProcessSwitchesX(argc, argv, pos, fOr, fAnd, fNot)
int argc, pos;
bool fOr, fAnd, fNot;
char **argv;
{
  int darg = 0, i, j;
  real rT;
  char ch1;

  ch1 = argv[0][pos+1];
  switch (argv[0][pos]) {
  case chNull:
    break;

  case 'b':
    if (is.fSzInteract) {
      ErrorArgv("Xb");
      return tcError;
    }
    ch1 = ChCap(ch1);
    if (FValidBmpmode(ch1))
      gs.chBmpMode = ch1;
    SwitchF2(gs.fBitmap);
    gs.fPS = gs.fMeta = fFalse;
    break;

#ifdef PS
  case 'p':
    if (is.fSzInteract) {
      ErrorArgv("Xp");
      return tcError;
    }
    gs.fPS = fTrue + (ch1 != '0');
    gs.fBitmap = gs.fMeta = fFalse;
    break;
#endif

#ifdef META
  case 'M':
    if (is.fSzInteract) {
      ErrorArgv("XM");
      return tcError;
    }
    if (ch1 == '0')
      SwitchF(gs.fFont);
    SwitchF2(gs.fMeta);
    gs.fBitmap = gs.fPS = fFalse;
    break;
#endif

  case 'o':
    if (is.fSzInteract) {
      ErrorArgv("Xo");
      return tcError;
    }
    if (argc <= 1) {
      ErrorArgc("Xo");
      return tcError;
    }
    if (!gs.fBitmap && !gs.fPS && !gs.fMeta)
      gs.fBitmap = fTrue;
    gi.szFileOut = SzPersist(argv[1]);
    darg++;
    break;

#ifdef X11
  case 'B':
    if (is.fSzInteract) {
      ErrorArgv("XB");
      return tcError;
    }
    SwitchF(gs.fRoot);
    break;
#endif

  case 'm':
    SwitchF(gs.fColor);
    break;

  case 'r':
    SwitchF(gs.fInverse);
    break;

  case 'w':
    if (argc <= 1) {
      ErrorArgc("Xw");
      return tcError;
    }
    i = atoi(argv[1]);
    if (argc > 2 && ((j = atoi(argv[2])) || argv[2][0] == '0')) {
      argc--; argv++;
      darg++;
    } else
      j = i;
    if (!FValidGraphx(i)) {
      ErrorValN("Xw", i);
      return tcError;
    }
    if (!FValidGraphy(j)) {
      ErrorValN("Xw", j);
      return tcError;
    }
    gs.xWin = i; gs.yWin = j;
    darg++;
    break;

  case 's':
    if (argc <= 1) {
      ErrorArgc("Xs");
      return tcError;
    }
    i = atoi(argv[1]);
    if (i < 100)
      i *= 100;
    if (!FValidScale(i)) {
      ErrorValN("Xs", i);
      return tcError;
    }
    gs.nScale = i;
    darg++;
    break;

  case 'i':
    SwitchF(gs.fAlt);
    break;

  case 't':
    SwitchF(gs.fText);
    break;

  case 'u':
    SwitchF(gs.fBorder);
    break;

  case 'l':
    SwitchF(gs.fLabel);
    break;

  case 'j':
    SwitchF(gs.fJetTrail);
    break;

  case '1':
    if (argc <= 1) {
      ErrorArgc("X1");
      return tcError;
    }
    i = atoi(argv[1]);
    if (!FItem(i)) {
      ErrorValN("X1", i);
      return tcError;
    }
    gs.nLeft = i;
    darg++;
    break;

  case '2':
    if (argc <= 1) {
      ErrorArgc("X2");
      return tcError;
    }
    i = atoi(argv[1]);
    if (!FItem(i)) {
      ErrorValN("X2", i);
      return tcError;
    }
    gs.nLeft = -i;
    darg++;
    break;

  case 'd':
    if (is.fSzInteract) {
      ErrorArgv("Xd");
      return tcError;
    }
    if (argc <= 1) {
      ErrorArgc("Xd");
      return tcError;
    }
    gs.szDisplay = SzPersist(argv[1]);
    darg++;
    break;

  case 'W':
    if (argc > 1 && ((i = atoi(argv[1])) || argv[1][0] == '0')) {
      darg++;
      if (!FValidRotation(i)) {
        ErrorValN("XW", i);
        return tcError;
      }
      gs.nRot = i;
    }
    gi.nMode = gWorldMap;
    if (ch1 == '0')
      gs.fMollewide = fTrue;
    is.fHaveInfo = fTrue;
    break;

  case 'G':
    if (argc > 1 && ((i = atoi(argv[1])) || argv[1][0] == '0')) {
      darg++;
      if (!FValidRotation(i)) {
        ErrorValN("XG", i);
        return tcError;
      }
      gs.nRot = i;
      if (argc > 2 && ((rT = atof(argv[2])) || argv[2][0] == '0')) {
        darg++;
        if (!FValidTilt(rT)) {
          ErrorValR("XG", rT);
          return tcError;
        }
        gs.rTilt = rT;
      }
    }
    gi.nMode = gGlobe;
    is.fHaveInfo = fTrue;
    break;

  case 'P':
    if (argc > 1 && ((i = atoi(argv[1])) || argv[1][0] == '0')) {
      darg++;
      if (!FValidRotation(i)) {
        ErrorValN("XP", i);
        return tcError;
      }
    } else
      i = 0;
    gs.nRot = i;
    gi.nMode = gPolar;
    if (ch1 == '0')
      gs.fPrintMap = fTrue;
    is.fHaveInfo = fTrue;
    break;

#ifdef CONSTEL
  case 'F':
    if (!fMap && gi.nMode != gGlobe && gi.nMode != gPolar)
      gi.nMode = gWorldMap;
    not(gs.fConstel);
    is.fHaveInfo = fTrue;
    break;
#endif

#ifdef ISG
  case 'n':
    if (argc > 1 && (i = atoi(argv[1])))
      darg++;
    else
      i = 10;
    if (i < 1 || i > 10) {
      ErrorValN("Xn", i);
      return tcError;
    }
    gs.nAnim = i;
    break;
#endif

  default:
    ErrorSwitch(argv[0]);
    return tcError;
  }
  /* 'darg' contains the value to be added to argc when we return. */
  return darg;
}


/* Process one command line switch passed to the program dealing with more  */
/* obscure graphics options. This is structured very much like the function */
/* NProcessSwitchesX(), except here we know each switch begins with 'YX'.   */

int NProcessSwitchesRareX(argc, argv, pos)
int argc, pos;
char **argv;
{
  int darg = 0, i, j;
  char ch1;

  ch1 = argv[0][pos+1];
  switch (argv[0][pos]) {
  case chNull:
    if (argc <= 2) {
      ErrorArgc("YX");
      return tcError;
    }
#ifdef PCG
    i = atoi(argv[1]);
    if (!FValidResmode(i)) {
      ErrorValN("YX", i);
      return tcError;
    }
    gs.nResHi = i;
    i = atoi(argv[2]);
    if (!FValidResmode(i)) {
      ErrorValN("YX", i);
      return tcError;
    }
    gs.nResLo = i;
    gs.fBitmap = gs.fPS = gs.fMeta = fFalse;
#endif
    darg += 2;
    break;

  case 'G':
    if (argc <= 1) {
      ErrorArgc("YXG");
      return tcError;
    }
    i = atoi(argv[1]);
    if (!FValidGlyphs(i)) {
      ErrorValN("YXg", i);
      return tcError;
    }
    gs.nGlyphs = i;
    j = i/1000;
    if (FBetween(j, 1, 2))
      szDrawSign[sCap] = szDrawSign[cSign+j];
    j = (i/100)%10;
    if (FBetween(j, 1, 2))
      szDrawObject[oUra] = szDrawObject[oNorm+j];
    j = (i/10)%10;
    if (FBetween(j, 1, 2))
      szDrawObject[oPlu] = szDrawObject[oNorm+2+j];
    j = i%10;
    if (FBetween(j, 1, 3))
      szDrawObject[oLil] = szDrawObject[oNorm+4+j];
    darg++;
    break;

  case 'g':
    if (argc <= 1) {
      ErrorArgc("YXg");
      return tcError;
    }
    i = atoi(argv[1]);
    if (!FValidGrid(i)) {
      ErrorValN("YXg", i);
      return tcError;
    }
    gs.nGridCell = i;
    darg++;
    break;

  case 'f':
    if (argc <= 1) {
      ErrorArgc("YXf");
      return tcError;
    }
    gs.fFont = atoi(argv[1]);
    darg++;
    break;

#ifdef PS
  case 'p':
    if (ch1 == '0') {
      if (argc <= 2) {
        ErrorArgc("YXp0");
        return tcError;
      }
      gs.xInch = atof(argv[1]);
      gs.yInch = atof(argv[2]);
      darg += 2;
      break;
    }
    if (argc <= 1) {
      ErrorArgc("YXp");
      return tcError;
    }
    gs.nOrient = atoi(argv[1]);
    darg++;
    break;
#endif

  default:
    ErrorSwitch(argv[0]);
    return tcError;
  }
  /* 'darg' contains the value to be added to argc when we return. */
  return darg;
}


/* This is the main interface to all the graphics features. This routine     */
/* is called from the main program if any of the -X switches were specified, */
/* and it sets up for and goes and generates the appropriate graphics chart. */
/* We return fTrue if successfull, fFalse if some non-fatal error occurred.  */

bool FActionX()
{
  int i;

  /* Set up variables in preparation for graphics. Set the glyphs for     */
  /* certain objects to point to their current setting, and if doing      */
  /* constellations, give a couple stars more correct astronomical names. */

#ifdef CONSTEL
  if (gs.fConstel) {
    szObjName[starLo-1+10] = "Alnilam";  /* Normally "Orion"     */
    szObjName[starLo-1+47] = "M31";      /* Normally "Andromeda" */
  }
#endif
  i = gs.nGlyphs/1000;
  szDrawSign[sCap] = szDrawSign[cSign+(i == 2 ? 2 : 1)];
  i = (gs.nGlyphs/100)%10;
  szDrawObject[oUra] = szDrawObject[oNorm+(i == 2 ? 2 : 1)];
  i = (gs.nGlyphs/10)%10;
  szDrawObject[oPlu] = szDrawObject[oNorm+2+(i == 2 ? 2 : 1)];
  i = gs.nGlyphs%10;
  szDrawObject[oLil] =
    szDrawObject[oNorm+4+(FBetween(i, 2, 3) ? i : fSouthNode ? 3 : 1)];
/*  if (fSouthNode)
    szObjectFont[oSou] = '>';*/
  gi.fFile = (gs.fBitmap || gs.fPS || gs.fMeta);
#ifdef PS
  gi.fEps = gs.fPS > fTrue;
#endif

  /* First figure out what graphic mode to generate the chart in, based on */
  /* various non-X command switches, e.g. -L combined with -X, -g combined */
  /* with -X, and so on, and determine the size the window is to be, too.  */

  if (gi.nMode == gWheel) {
    if (us.fWheel)
      gi.nMode = gHouse;
    else if (us.fGrid || us.fMidpoint) {
      gi.nMode = gGrid;
      if (us.nRel <= rcDual && us.fMidpoint && !us.fAspList)
        us.fGridConfig = fTrue;
      gs.xWin = gs.yWin =
        (gs.nGridCell + (us.nRel <= rcDual))*CELLSIZE*gi.nScale + 1;
    } else if (us.fHorizon)
      gi.nMode = gHorizon;
    else if (us.fOrbit)
      gi.nMode = gOrbit;
    else if (us.fInfluence)
      gi.nMode = gDisposit;
    else if (us.fAstroGraph)
      gi.nMode = gAstroGraph;
    else if (us.fCalendar)
      gi.nMode = gCalendar;
    else if (us.fEphemeris)
      gi.nMode = gEphemeris;
    else if (us.nRel == rcBiorhythm)
      gi.nMode = gBiorhythm;
  }
  if (fMap) {
    gs.xWin = nDegMax*gi.nScale;
    gs.yWin = nDegHalf*gi.nScale;
  }
  gi.nScaleT = gs.fPS ? PSMUL : (gs.fMeta ? METAMUL : 1);

  if (gi.fFile) {
    if (gs.xWin == 0)
      gs.xWin = DEFAULTX;
    if (gs.yWin == 0)
      gs.yWin = DEFAULTY;
    if (fSidebar)
      gs.xWin += SIDESIZE;
    if (gs.xWin > BITMAPX)
      gs.xWin = BITMAPX;
    if (gs.yWin > BITMAPY)
      gs.yWin = BITMAPY;
    BeginFileX();
    if (gs.fBitmap) {
      gi.cbBmpRow = (gs.xWin + 1) >> 1;
      gi.yBand = gs.yWin;
      if (!FEnsureGrid())
        return fFalse;
      while ((gi.bm = PAllocate((long)gi.cbBmpRow * gi.yBand, fTrue, NULL)) ==
        NULL) {
        PrintWarning("The bitmap must be generated in multiple stages.");
        gi.yBand = (gi.yBand + 1) / 2;
        if (gi.yBand < 1 || gs.chBmpMode != 'B')
          return fFalse;
      }
      if (gi.yBand == gs.yWin)
        gi.yBand = 0;
      else {
        gi.yOffset = gs.yWin - gs.yWin % gi.yBand;
        if (gi.yOffset == gs.yWin)
          gi.yOffset -= gi.yBand;
      }
    }
#ifdef PS
    else if (gs.fPS)
      PsBegin();
#endif
#ifdef META
    else {
      if (!FEnsureGrid())
        return fFalse;
      for (gi.cbMeta = MAXMETA; gi.cbMeta > 0 &&
        (gi.bm = PAllocate(gi.cbMeta, fTrue, NULL)) == NULL;
        gi.cbMeta -= MAXMETA/8)
        PrintWarning("Attempting to get maximum memory for metafile.");
      if (gi.cbMeta == 0)
        return fFalse;
      gs.xWin   *= METAMUL;  /* Increase chart sizes and scales behind the */
      gs.yWin   *= METAMUL;  /* scenes to make graphics look smoother.     */
      gs.nScale *= METAMUL;
    }
#endif
    InitColorsX();
  }
#ifdef ISG
  else {
#ifdef PCG
    BeginX();
    if (gs.xWin == 0 || gs.yWin == 0) {
      if (gs.xWin == 0)
        gs.xWin = xPcScreen;
      if (gs.yWin == 0)
        gs.yWin = yPcScreen;
      SquareX(&gs.xWin, &gs.yWin, fFalse);
    } else if (fSidebar)
      gs.xWin += SIDESIZE;
#else
    if (gs.xWin == 0 || gs.yWin == 0) {
      if (gs.xWin == 0)
        gs.xWin = DEFAULTX;
      if (gs.yWin == 0)
        gs.yWin = DEFAULTY;
      SquareX(&gs.xWin, &gs.yWin, fFalse);
    } else if (fSidebar)
      gs.xWin += SIDESIZE;
    BeginX();
#endif
  }
#endif /* ISG */

  if (gi.fFile || gs.fRoot)    /* Go draw the graphic chart. */
    DrawChartX();
  if (gi.fFile) {    /* Write bitmap to file if in that mode. */
    EndFileX();
    while (gi.yBand) {
      gi.yOffset -= gi.yBand;
      DrawChartX();
      EndFileX();
    }
    if (!gs.fPS)
      DeallocateHuge(gi.bm);
  }
#ifdef ISG
  else {
#ifdef X11
    if (gs.fRoot) {                                         /* Process -XB. */
      XSetWindowBackgroundPixmap(gi.disp, gi.root, gi.pmap);
      XClearWindow(gi.disp, gi.root);

      /* If -Xn in effect with -XB, then enter infinite loop where we */
      /* calculate and animate chart, displaying on the root window.  */
      while (gs.nAnim) {
        Animate(gs.nAnim, 1);
        if (!gs.fJetTrail)
          XFillRectangle(gi.disp, gi.pmap, gi.pmgc, 0, 0, gs.xWin, gs.yWin);
        DrawChartX();
        XSetWindowBackgroundPixmap(gi.disp, gi.root, gi.pmap);
        XClearWindow(gi.disp, gi.root);
      }
    } else
#endif
      InteractX();    /* Window's up; process commands given to window now. */
    EndX();
  }
#endif /* ISG */
  return fTrue;
}
#endif /* GRAPH */

/* xscreen.c */
