/***************************************************************************
 *                                                                         *
 *   OPRLOW.C                                                              *
 *                                                                         *
 *   Copyright (c) 1992-1997 Galacticomm, Inc.      All Rights Reserved.   *
 *                                                                         *
 *   Low level operator console (version 6+) for dealing with virtual      *
 *   screens, screen saving, dynamic registration, etc.                    *
 *                                                                         *
 *                                            - Robert A. Rose 02/01/92    *
 *                                                                         *
 ***************************************************************************/

#include "gcomm.h"
#include "majorbbs.h"
#include "oprlow.h"

#define FILREV "$Revision: 13 $"

VOID scn2mai(INT scnhdl);
VOID scn2aux(INT scnhdl);
VOID mai2aux(VOID);

struct scrnid *scnids=NULL,*scncur;

static CHAR savscn[GVIDSCNSIZ];/* saved screen                             */
CHAR *auxcrtm=NULL;           /* pointer to aux. crt                       */
INT  mainx,mainy;             /* cursor storage spots                      */
INT  wasactv=0;               /* 1=cursor position is actvhdl, 0=mainhdl   */
INT  mainhdl=0;               /* handle of screen on main (0=none)         */
INT  auxhdl=0;                /* handle of screen on aux (0=none)          */
INT  savmai=0,savaux=0;       /* handle of saved screens                   */
INT  actvhdl=0;               /* what handle is active currently?          */
INT  lasmhdl=0;               /* handle before last scn2mai()              */
INT  lasahdl=0;               /* handle before last scn2aux()              */
INT  backhdl=0;               /* hdl for background (on actvscns < GVIDSCNSIZ)*/
INT  skcipg=0;                /* force all keys to background handler      */

INT  sec2mov=0;               /* seconds left before moving saver display  */
INT  sec2sav=0;               /* configured seconds to save screens 0=never*/
INT  cur2sav=16500;           /* how much longer before we save screens    */
                              /* cur2sav=0 means screens are saved         */
INT  occgran=60;              /* granularity of occrou cycling             */

INT  mxscns=0;                /* maximum number of screens to allow        */
INT  regscns=0;               /* number of registered screens              */
INT  dfltmscn=0;              /* default main screen to show               */
INT  dfltascn=0;              /* default aux screen to show                */

extern CHAR *tmpscn1;              /* temporary screen buffer for vidmov() */
extern CHAR *tmpscn2;              /* temporary screen buffer for vidmov() */

VOID
inilopr(                           /* initialize OPRLOW code               */
INT maxs,                          /* maximum number of screens to allow   */
INT auxena)                        /* aux crt support enabled              */
{
     mxscns=maxs;
     scnids=(struct scrnid *)alczer(mxscns*sizeof(struct scrnid));
     tmpscn1=(CHAR *)alcmem(GVIDSCNSIZ);
     tmpscn2=(CHAR *)alcmem(GVIDSCNSIZ);
#ifdef GCDOS
     if (auxena) {
          auxcrtm=auxcrt();
     }
#else
     sv.monmal=0;                  // no aux. crt support under NT
     (VOID)auxena;
#endif // GCDOS
     rtkick(2,occhdl);
}

VOID
endlopr(VOID)                      /* shutdown OPRLOW code                 */
{
     if (scnids != NULL) {
          free(scnids);
     }
}

CHAR *                             /* rtns pointer to screen buffer        */
actvscn(                      /* make scnhdl the active screen for printfs */
INT scnhdl)                        /* screen handle (returned by regscn)   */
{
     return(actvscnsp(scnhdl,
         scnids[scnhdl].x1,scnids[scnhdl].y1,
         scnids[scnhdl].x2,scnids[scnhdl].y2,
         scnids[scnhdl].scroll));
}

CHAR *                             /* rtns pointer to screen buffer        */
actvscnsp(                    /* make scnhdl the active screen for printfs */
INT scnhdl,                        /* screen handle (returned by regscn)   */
INT x1,INT y1,                     /* bounds and scroll (ovrids scnid sets)*/
INT x2,INT y2,INT scr)
{
     if (scnhdl == 0) {
          scnhdl=actvhdl;
     }
     scnids[actvhdl].cx=curcurx();
     scnids[actvhdl].cy=curcury();
     setwin(scnids[scnhdl].location,x1,y1,x2,y2,scr);
     cursact(0);
     locate(scnids[scnhdl].cx,scnids[scnhdl].cy);
     if (scnhdl == mainhdl) {
          if (scnids[scnhdl].curmove) {
               cursact(1);
               cursiz(GVIDLILCURS);
          }
          else {
               cursiz(GVIDNOCURS);
          }
     }
     actvhdl=scnhdl;
     return(scnids[scnhdl].location);
}

VOID
updbckgnd(VOID)               /* update visible piece of the background    */
{
     INT mainlen;

     mainlen=scnids[mainhdl].scnlen;
     if (mainlen < GVIDSCNSIZ) {
          if (scnids[backhdl].location != NULL) {
               mem2scn(scnids[backhdl].location+mainlen,mainlen,
                 GVIDSCNSIZ-mainlen);
          }
     }
}

VOID
conupd(VOID)                  /* Constant update (for displayed screens)   */
{
     VOID (*rouptr)(VOID);
     static ULONG oldhrt;

     if (hrtval()-oldhrt > 3855) {      /* 3855=1/17 sec                   */
          if ((rouptr=scnids[mainhdl].disrou) != NULL) {
               (*(rouptr))();
          }
          if ((rouptr=scnids[auxhdl].disrou) != NULL) {
               (*(rouptr))();
          }
          oldhrt=hrtval();
     }
}

VOID
occhdl(VOID)                       /* Occasional update routine            */
{
     INT i;
     VOID (*rouptr)(VOID);
     struct scrnid *occscn;

     for (i=1 ; i <= regscns ; i++) {
          occscn=scnids+i;
          if ((rouptr=occscn->occrou) != NULL) {
               occscn->secs2go-=occgran;
               if (occscn->secs2go <= 0) {
                    (*(rouptr))();
                    occscn->secs2go=occscn->secs;
               }
          }
     }
     if (sec2sav != 0) {
          if (cur2sav > 0) {            /* not saved, but getting there..  */
               cur2sav-=occgran;
               if (cur2sav <= 0) {      /* actively save these guys...     */
                    cur2sav=0;
                    sec2mov=60+occgran;
                    savscns();
               }
          }
          else if (cur2sav == 0) {      /* already saved...                */
               sec2mov-=occgran;
               if (sec2mov <= 0) {
                    sec2mov=60;
                    setwin(NULL,0,0,79,24,1);
                    shobox(0,1);
                    rstwin();
               }
          }
     }
     rtkick(occgran,occhdl);
}

UINT
keyhit(                       /* Process a key from the console            */
UINT scan)                         /* scan code of key hit                 */
{
     INT i;

     if (sec2sav > 0) {
          if (cur2sav > 0) {       /* reset counter if saver is turned on  */
               cur2sav=sec2sav;
          }
          else if (cur2sav == 0) {
               unsavscns();
               cur2sav=sec2sav;
               scan=0;
          }
     }
     if (scan != 0 && skcipg) {
          scan=(*(scnids[backhdl].keyrou))(scan);
          updbckgnd();
     }
     else {
          if (scan != 0 && scnids[mainhdl].keyrou != NULL) {
               scan=(*(scnids[mainhdl].keyrou))(scan);
          }
          if (scan != 0 && scnids[auxhdl].keyrou != NULL) {
               scan=(*(scnids[auxhdl].keyrou))(scan);
          }
          if (scan != 0 && scnids[backhdl].keyrou != NULL) {
               scan=(*(scnids[backhdl].keyrou))(scan);
               updbckgnd();
          }
          if (scan != 0) {
               for (i=1 ; i <= regscns ; i++) {
                    if (scan == scnids[i].selssc) {
                         scn2mai(i);
                         break;
                    }
               }
          }
     }
     return(scan);
}

VOID
scngoaway(                    /* screen wants to hide itself               */
INT scnhdl)                        /* screen handle                        */
{
     if (scnhdl == 0 || (scnhdl != mainhdl && scnhdl != auxhdl)) {
          return;
     }
     if (scnhdl == mainhdl && lasmhdl != 0) {
          scn2mai(lasmhdl);
     }
     else if (scnhdl == auxhdl && lasahdl != 0) {
          scn2aux(lasahdl);
     }
     else if (scnhdl == mainhdl && dfltmscn != 0) {
          scn2mai(dfltmscn);
     }
     else if (scnhdl == auxhdl && dfltascn != 0) {
          scn2aux(dfltascn);
     }
}

VOID
scn2mai(                      /* make screen visible on main crt           */
INT scnhdl)                        /* screen handle                        */
{
     INT i;
     VOID (*rouptr)(VOID);

     if ((rouptr=scnids[mainhdl].byerou) != NULL) {
          (*(rouptr))();
     }
     if (scnhdl == auxhdl) {
          scncur=scnids+auxhdl;              /* put back current main scn  */
          *((CHAR *)auxcrtm+scncur->scnlen-160)='Μ';
          *((CHAR *)auxcrtm+scncur->scnlen-2)='»';
          movmem(auxcrtm,scncur->buffer,scncur->scnlen);
          scncur->location=scncur->buffer;
          for (i=0 ; i < GVIDSCNSIZ ; i+=2) {
               *(auxcrtm+i)=' ';
               *(auxcrtm+i+1)=0x07;
          }
          auxhdl=0;
     }
     lasmhdl=mainhdl;
     scncur=scnids+mainhdl;             /* put back current main scn  */
     scn2mem(scncur->buffer,0,scncur->scnlen);
     scncur->location=scncur->buffer;
     mainhdl=scnhdl;
     actvscn(scnhdl);
     if ((rouptr=scnids[scnhdl].updrou) != NULL) {
          (*(rouptr))();
     }
     scncur=scnids+scnhdl;              /* put new guy up             */
     mem2scn(scncur->buffer,0,scncur->scnlen);
     if (scncur->scnlen < GVIDSCNSIZ) { /* if partial screen...       */
          if (scnids[backhdl].location != NULL) {
               mem2scn(scnids[backhdl].location+scncur->scnlen,
                       scncur->scnlen,GVIDSCNSIZ-scncur->scnlen);
          }
     }
     scncur->location=NULL;
     if (auxhdl != sv.monmal) {
          auxups(sv.monmal);
     }
}

VOID
scn2aux(                      /* make screen visible on aux. crt           */
INT scnhdl)                        /* screen handle                        */
{
     VOID (*rouptr)(VOID);
     INT i;

     if ((rouptr=scnids[scnhdl].updrou) != NULL) {
          (*(rouptr))();
     }
     if (auxcrtm != NULL && scnhdl != mainhdl) {
          lasahdl=auxhdl;
          if (auxhdl != 0 && (rouptr=scnids[auxhdl].byerou) != NULL) {
               (*(rouptr))();
          }
          if (auxhdl != 0) {
               scncur=scnids+auxhdl;         /* put back current main scn  */
               movmem(auxcrtm,scncur->buffer,scncur->scnlen);
               scncur->location=scncur->buffer;
          }
          if (scnhdl == 0) {
               for (i=0 ; i < GVIDSCNSIZ ; i+=2) {
                    *(auxcrtm+i)=' ';
                    *(auxcrtm+i+1)=0x07;
               }
               auxhdl=0;
               return;
          }
          scncur=scnids+scnhdl;              /* put new guy up             */
          movmem(scncur->buffer,auxcrtm,scncur->scnlen);
          if (scncur->scnlen < GVIDSCNSIZ) { /* if partial screen blank it */
               *((CHAR *)auxcrtm+scncur->scnlen-160)='Θ';
               *((CHAR *)auxcrtm+scncur->scnlen-2)='Ν';
               for (i=scncur->scnlen ; i < GVIDSCNSIZ ; i+=2) {
                    *(auxcrtm+i)=' ';
                    *(auxcrtm+i+1)=0x07;
               }
          }
          scncur->location=auxcrtm;
          auxhdl=scnhdl;
     }
}

VOID
mai2aux(VOID)                      /* swap main and aux. crts              */
{
     INT tint;
     CHAR *tstg;

     lasmhdl=mainhdl;
     lasahdl=auxhdl;
     scncur=scnids+mainhdl;
     if (scnids[mainhdl].scnlen < GVIDSCNSIZ
       || scnids[auxhdl].scnlen < GVIDSCNSIZ) {
          scn2mem(savscn,0,GVIDSCNSIZ);
          memswap(savscn,auxcrtm,min(scnids[mainhdl].scnlen,
                            scnids[auxhdl].scnlen));
          mem2scn(savscn,0,GVIDSCNSIZ);
     }
     else {
          scn2mem(savscn,0,GVIDSCNSIZ);
          memswap(savscn,auxcrtm,GVIDSCNSIZ);
          mem2scn(savscn,0,GVIDSCNSIZ);
     }
     tstg=scnids[auxhdl].location;
     scnids[auxhdl].location=scncur->location;
     scncur->location=tstg;
     if (actvhdl == mainhdl) {
          scncur->cx=curcurx();
          scncur->cy=curcury();
     }
     cursact(1);
     scncur=scnids+auxhdl;
     locate(scncur->cx,scncur->cy);
     cursact(scncur->curmove);
     tint=auxhdl;
     auxhdl=mainhdl;
     mainhdl=tint;
}

INT                                /* returns handle number                */
regscn(                       /* register a screen for display             */
struct scrnid *newscn)             /* screen id block                      */
{
     VOID (*rouptr)(VOID);

     regscns++;
     if (regscns == mxscns) {
          catastro("Too many screens (%d)! (increase option MAXSCNS)",regscns);
     }
     movmem(newscn,scnids+regscns,sizeof(struct scrnid));
     if (scnids[regscns].secs > 0 && scnids[regscns].secs < occgran) {
          occgran=scnids[regscns].secs;
     }
     scnids[regscns].location=alcmem(GVIDSCNSIZ);
     scnids[regscns].buffer=scnids[regscns].location;
     if (scnids[regscns].scnnam != NULL) {
       iniscn(scnids[regscns].scnnam,scnids[regscns].location);
     }
     else {
          scblank(scnids[regscns].location,0x70);
     }
     if ((rouptr=scnids[regscns].updrou) != NULL) {
          actvscn(regscns);
          (*(rouptr))();
          if ((rouptr=scnids[regscns].byerou) != NULL) {
               (*(rouptr))();
          }
     }
     return(regscns);
}

VOID
shobox(                       /* Message that walks during screen saving   */
INT w,                             /* Which screen coords to use (main,aux)*/
INT sho)                           /* 1=show boxe, 0=remove box            */
{
     INT i;
     static INT bbx[2]={40,5},     /* Location of last blanking box        */
                bby[2]={14,4};

     cursact(0);
     sstatr(0x0A);
     for (i=0 ; i < 4 ; i++) {
          printfat(bbx[w],bby[w]+i,"                            ");
     }
     if (sho) {
          bbx[w]=(bbx[w]+(w == 1 ? 12 : 56))%52;
          bby[w]=(bby[w]+(w == 1 ? 2 : 18))%20;
          printfat(bbx[w],bby[w],"ΙΝΝΝΫ " SVR_NAMER " %1d.%02d "
             "ΫΝΝΝ»",BBSVER/100,BBSVER%100);
          printfat(bbx[w],bby[w]+1,"Ί    %-5.5s  %10.10s    Ί",
             nctime(now()),ncdatel(today()));
          printfat(bbx[w],bby[w]+2,"Ί      Press any key      Ί");
          printfat(bbx[w],bby[w]+3,"ΘΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΝΌ");
          cursact(1);
          locate(bbx[0]+4,bby[0]);
          cursact(0);
     }
}

VOID
unsavscns(VOID)                    /* unsave screens that have been saved  */
{
     setwin(NULL,0,0,79,24,1);
     shobox(0,0);
     if (savmai != 0 && mainhdl == 0) {
          scncur=scnids+savmai;
          mem2scn(scncur->buffer,0,GVIDSCNSIZ);
          scncur->location=NULL;
          cursact(1);
          locate(scncur->cx,scncur->cy);
          cursact(scncur->curmove);
          mainhdl=savmai;
     }
     if (savaux != 0 && auxhdl == 0) {
          scncur=scnids+savaux;
          movmem(scncur->buffer,auxcrtm,GVIDSCNSIZ);
          scncur->location=auxcrtm;
          auxhdl=savaux;
     }
}

VOID
savscns(VOID)                      /* save screens to prevent burn-in      */
{
     if (mainhdl != 0) {
          scncur=scnids+mainhdl;
          scn2mem(scncur->buffer,0,GVIDSCNSIZ);
          scncur->location=scncur->buffer;
          scblank(0,0x0A);
          savmai=mainhdl;
          mainhdl=0;
     }
     if (auxhdl != 0) {
          scncur=scnids+auxhdl;
          movmem(auxcrtm,scncur->buffer,GVIDSCNSIZ);
          scncur->location=scncur->buffer;
          scblank(auxcrtm,0x0A);
          savaux=auxhdl;
          auxhdl=0;
     }
     setwin(NULL,0,0,79,24,1);
     shobox(0,1);
}

VOID
memswap(                      /* swap two memory regions (without buffer)  */
CHAR *source,                      /* source buffer                        */
CHAR *dest,                        /* destination buffer                   */
INT length)                        /* length in bytes to move              */
{
     INT i;
     INT tmp;

     for (i=0 ; i < length ; i++) {
          tmp=*source;
          *source=*dest;
          *dest=tmp;
          source++;
          dest++;
     }
}

INT
baudat(baud,blink)                 /* Set color attribute based on baud    */
ULONG baud;
INT blink;
{
     INT i,attr;
     static ULONG bauds[]={0L,1200L,2400L,4800L,9600L,19200L,38400L};
     static CHAR colors[]={0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F};

     for (i=sizeof(bauds)/sizeof(ULONG)-1 ; i >= 0 ; i--) {
          if (baud >= bauds[i]) {
               break;
          }
     }
     sstatr(attr=colors[i]+(blink ? 0x80 : 0));
     return(attr);
}

CHAR *
dbytes(                            /* convert # into xxxx, xxxK, or xxxMb  */
ULONG kbytes)
{
     static CHAR lbuff[30];

     if (kbytes < 10) {
          strcpy(lbuff,"< 10 K");
     }
     else if (kbytes < 1024L) {
          sprintf(lbuff,"%lu K",kbytes);
     }
     else if (kbytes < (10L*1024L*1024L)) {
          sprintf(lbuff,"%lu MB",kbytes/1024L);
     }
     else {
          strcpy(lbuff,"> 10 GB");
     }
     return(lbuff);
}
