/* ConDev.c

   richtet eine --- Shell --- fuer die Arbeit mit dem
   Console Device ein.

   Autor: E.G. Meyzis, Mai 1992

   Compile Instr. fuer DICE:
   dcc ConDev.c -c -o t:ConDev.o

   Link Instr. fuer DICE:
   dlink dlib:c.o t:ConDev.o dlib:c.lib+
   dlib:amigas20.lib dlib:auto.lib dlib:x.o -o ConDev
*/

#include <Devices/Console.h>
#include <Devices/ConUnit.h>
#include <Dos/Dos.h>
#include <Exec/Exec.h>
#include <Intuition/Intuition.h>
#include <stdlib.h>
#include <stdio.h>
#include <utility/TagItem.h>

/* +++++++++  Definitionen  ++++++++++++
*/
#define TRUE      1   /* um mit Datentyp BOOL zu arbeiten */
#define FALSE     0
#define CSI    0x9B /* Control Sequence Introducer, wie z.B.
                       fuer F-Tasten F1 bis F10 verwendet */
#define OS200    36                  /* mindestens OS 2.0 */


#define ConTitle " Con Dev "    /* Titel Console Window */

/* +++++++++  Datentypen  ++++++++++++
*/
typedef char                BOOL;
typedef char                *CharPtr;
typedef struct IOStdReq     *IOStdReqPtr;
typedef struct Window       *WindowPtr;
typedef struct MessagePort  *MsgPortPtr;


/* ++++++++ Routinen des Basisprogramms ++++++++++ */

BOOL RunningOS2();
void IORequestAbbauen(IOStdReqPtr reqPtr);
void Aufraeumen();
BOOL FensterAngelegt(WindowPtr *win);
BOOL ConsoleEingerichtet(IOStdReqPtr *wrPtr, *rdPtr,
                                  WindowPtr   winPtr);
BOOL IORequestAngelegt(IOStdReqPtr *conPtr);
void ConWriteStr(IOStdReqPtr conPtr, CharPtr str);
void ConWriteBuf(IOStdReqPtr conPtr,
                 CharPtr     bufPtr,
                 int        laenge);
int ConReadSync(IOStdReqPtr conPtr,
                CharPtr     bufPtr,
                int         laenge);
BOOL ConReadASync(IOStdReqPtr conPtr,
                  CharPtr     bufPtr,
                  int         laenge);
void IDCMPCloseClickAbholen(WindowPtr winPtr);

/* ++++++++  Demo- und Test-Routinen ++++++++++ */

void SchreibTest();
void LeseTest();
void LeseRawEvent();
void EventCloseClickAbholen(IOStdReqPtr conPtr);
void ANSICodeDemo();


/* +++++++++  globale Variablen  ++++++++++++
*/
WindowPtr       ourWinPtr;

/* Ein Device, zwei I/O Strukturen */

IOStdReqPtr     conWritePtr, /* Schreiben */
                conReadPtr;  /* Lesen */


int main()
{
  int returnVal = 0; /* erfolgreiche Ausfuehrung annehmen */

  if (RunningOS2())   /* dann haben wir mindestens OS 2.0 */
    {
     if(FensterAngelegt(&ourWinPtr))
       {
        if (ConsoleEingerichtet (&conWritePtr,
                                 &conReadPtr, ourWinPtr))
          {
           SchreibTest();
           ANSICodeDemo();

           LeseTest();
           LeseRawEvent();

           IDCMPCloseClickAbholen(ourWinPtr);  /*  warten */
          }
        else
          returnVal = 10;  /* Console Dev. nicht anlegbar */
       }                         /* Fenster ... */
     else
       returnVal = 11;           /* Window nicht anlegbar */
     Aufraeumen();
    }                            /* Running ... */
  else
    returnVal = 12;                         /* OS 1.2/1.3 */

  return returnVal;
}


BOOL RunningOS2()        /* lauft Programm unter OS 2.x ? */
{
  long            *systemBasis;
  struct ExecBase *execBasePtr;

  systemBasis = (APTR)4;  /* Adr ExecBase a. Adr. 4 holen */
  execBasePtr = (APTR)*systemBasis;
  if (execBasePtr->LibNode.lib_Version >= OS200) /* OS 2  */
     return TRUE;
  else
     return FALSE;
}


void Aufraeumen()                 /* Ressourcen freigeben */
{
  if (conReadPtr)                   /* Lese- I/O Struktur */
    {
     if (!CheckIO(conReadPtr))    /* laeuft ein I/O Req.? */
       {
        AbortIO(conReadPtr); /* falls ja, diesen abbrechen*/
        WaitIO(conReadPtr);  /* und auf Beendigung warten */
       }
     IORequestAbbauen(conReadPtr);   /* einschl. Msg Port */
    }
  if (conWritePtr)
    {
     if (conWritePtr->io_Device)
       CloseDevice(conWritePtr);
     IORequestAbbauen(conWritePtr);
    }
  if (ourWinPtr)
    {
     CloseWindow(ourWinPtr);
     ourWinPtr = 0;
    }
}


void IORequestAbbauen(IOStdReqPtr reqPtr)

{
  if (reqPtr->io_Message.mn_ReplyPort)
    {                                             /* OS 2 */
     DeleteMsgPort(reqPtr->io_Message.mn_ReplyPort);
  }
  DeleteIORequest(reqPtr);                        /* OS 2 */
  reqPtr = 0;
}


BOOL FensterAngelegt(WindowPtr *win)

{
  static struct TagItem tagBuffer[10] =           /* OS 2 */
                   {
                    WA_Left,      20,
  /* DICE ver- */   WA_Top,       20,
  /* langt es, */   WA_Title,    (ULONG*) ConTitle,
  /* die Struk-*/   WA_Width,    500,
  /* tur, glo- */   WA_Height,   200,
  /* bal oder  */   WA_MinWidth, 100,
  /* static an */   WA_MinHeight, 50,
  /* zulegen   */   WA_Flags,  WFLG_SIMPLE_REFRESH |
                               WFLG_CLOSEGADGET |
                               WFLG_DEPTHGADGET |
                               WFLG_DRAGBAR  |
                               WFLG_SIZEGADGET,
                    TAG_DONE
                   };

  *win = (WindowPtr)OpenWindowTagList (NULL, tagBuffer);
  return (*win != NULL);
}


BOOL ConsoleEingerichtet(IOStdReqPtr *wrPtr, *rdPtr,
                                  WindowPtr   winPtr)

{
  IOStdReqPtr writePtr,
              readPtr;
  BOOL done = FALSE;

  if (IORequestAngelegt(&writePtr))    /* 1. I/O struktur */
    {
     writePtr->io_Data = winPtr;      /* Intuition Window */
     writePtr->io_Length = sizeof(struct Window);
     OpenDevice("console.device", CONU_STANDARD,  /* OS 2 */
                 writePtr, CONFLAG_DEFAULT);
     if ((!writePtr->io_Error) &&     /* Device geoeffnet */
                              (IORequestAngelegt(&readPtr)))
       {
        readPtr->io_Unit = writePtr->io_Unit; /* 2. Strukt*/
        readPtr->io_Device = writePtr->io_Device; /* erbt */
        done = TRUE;
        *wrPtr = writePtr;   /* Zeiger auf I/O Strukturen */
        *rdPtr = readPtr;    /* ans rufende Prog. geben */
       }
     else
       Aufraeumen;                 /* mangelte es an RAM? */
    }
  return done;
}

BOOL IORequestAngelegt(IOStdReqPtr *conPtr)

{
  MsgPortPtr portPtr;
  BOOL       done = FALSE;

  portPtr = (MsgPortPtr)CreateMsgPort();           /* OS2 */
  if (portPtr)
    {
     *conPtr = (IOStdReqPtr)
              CreateIORequest(portPtr,            /* OS 2 */
                              sizeof(struct IOStdReq));
     if (*conPtr)
       done = TRUE;
     else
       DeleteMsgPort(portPtr);                    /* OS 2 */
    }
  return done;
}


void ConWriteStr(IOStdReqPtr conPtr, CharPtr str)
{
  conPtr->io_Data = str;
  conPtr->io_Length = -1; /* nur f. mit 0 termin. Strings */
  conPtr->io_Command = CMD_WRITE;
  DoIO(conPtr);
}


void ConWriteBuf(IOStdReqPtr conPtr,
                 CharPtr     bufPtr, /*m. auszugeb. Zeich.*/
                 int        laenge)      /* Zeichenanzahl */
{
  conPtr->io_Data = bufPtr;
  conPtr->io_Length = laenge;         /* beliebige Laenge */
  conPtr->io_Command = CMD_WRITE;
  DoIO(conPtr);
}


int ConReadSync(IOStdReqPtr conPtr,
                CharPtr     bufPtr,     /* dort hin lesen */
                int         laenge) /* Anzahl der Zeichen */
{
  conPtr->io_Data = bufPtr;
  conPtr->io_Length = laenge;
  conPtr->io_Command = CMD_READ;
  SendIO(conPtr);
  WaitIO(conPtr);                /* nur unter OS 2 sicher */
  return conPtr->io_Actual;
}


BOOL ConReadASync(IOStdReqPtr conPtr, /*asynchrones Lesen */
                  CharPtr     bufPtr, /* ist das empfohle-*/
                  int         laenge) /*ne Verfahren */
{
  conPtr->io_Data = bufPtr;
  conPtr->io_Length = laenge;
  conPtr->io_Command = CMD_READ;
  SendIO(conPtr);    /* bewirkt o. WaitIO  Asynchronitaet */
}



void IDCMPCloseClickAbholen(WindowPtr winPtr)

{
  ModifyIDCMP(winPtr, IDCMP_CLOSEWINDOW);/*UserPort einr. */
  WaitPort(winPtr->UserPort);
  ReplyMsg(GetMsg(winPtr->UserPort));
}

void SchreibTest()  /* Schreibfarbe waehlen u. Textumbruch*/
                    /* sowie Scrolling zeigen */
{
  int  zaehler;

  ConWriteStr(conWritePtr,
                  "\033[1;31mHallo Console! ");
/*                 ^   ^^ ^ ^
                   |   || | 0x6D = Terminator
                   |   || rot
                   |   |bold
                   |   0x5B \
                   ESC      / Ersatz fuer CSI
*/
  for (zaehler = 0; zaehler < 100; zaehler++)
   {
    ConWriteStr(conWritePtr,       /* bold + Farbe bleibt */
                "Hallo Console! ");
  }
}

void LeseTest()
{
  char buffer[10];
  int  laenge;

  ConWriteStr(conWritePtr,
              "\n\033[3;35m** Geben Sie 1 Zeichen ein ** ");
/*                ^  ^^ ^ ^
                  |  || | 0x6D = Terminator
                  |  || violett
                  |  |italic
                  |  0x5B
                  ESC
*/
  laenge = ConReadSync(conReadPtr, buffer, sizeof(buffer));
  if (buffer[0] == (char)CSI)
    {
     buffer[0] = '@'; /* CSI kann nicht ausgegeben werden */
    }
  ConWriteBuf(conWritePtr, buffer, laenge);
}

void LeseRawEvent()  /* spezialisiert auf Keyboard Events */

{
  char buffer[400];       /* Je nach Delay dimensionieren */
  int  index,
       laenge;

  ConWriteStr(conWritePtr,
   "\033[0;31m\n\n\033[1{cl;scl;key;qual;p1;p2;sec;mic|\n");
/*                 ^   ^^
                   |   |Terminator Raw-Event Anforderung
                   |   RAW-Keyboard Input
                   ESC
*/
  Delay(100);/*um mit Tastatur dev.-int. Puffer zu fuellen*/

  laenge = ConReadSync(conReadPtr, buffer, sizeof(buffer));

  for (index = 0; index < laenge; index++)
    {
     if (buffer[index] == (char)CSI)
        buffer[index] = (char)012;    /* neue Zeile bitte */
    }                                 /* anstelle des CSI */
  ConWriteBuf(conWritePtr, buffer, laenge); /* alle Zeilen*/
}


void EventCloseClickAbholen(IOStdReqPtr conPtr)

{ char closeEvent[40];
  BOOL  waitClose = TRUE;

  ConWriteStr(conPtr, "\033[1;15}");
/*                          ^ ^ ^
                            | | stop reporting events
                            | disk removed
                            raw keyboard inputs
*/
  ConWriteStr(conPtr, "\033[11{ \nClick CloseGadget\n");
/*                          ^ ^
                            | start reporting
                            Close Gadget Event
*/
  do
    {
     ConReadSync(conPtr, closeEvent, sizeof(closeEvent));
     if ((closeEvent[1] == '1') && (closeEvent[2] == '1'))
       waitClose = FALSE;
    }
  while (waitClose);
}

char TwoCharToNum(const char *charPtr)
{
 return (charPtr[0] -48) * 10 + (charPtr[1] - 48);
}


void FensterGroesse(char *zeilen, char *spalten)
{
  char  puffer[20];
  int   laenge;

  ConWriteStr(conWritePtr, "\033[0 q\n"); /*Status Request*/
  laenge = ConReadSync        /* Fenstergroesse empfangen */
           (conReadPtr, puffer, sizeof(puffer));
  *zeilen = TwoCharToNum(&puffer[5]);/* Zeilenanzahl holen */
  *spalten = TwoCharToNum(&puffer[8]);/* Spaltenanz. holen */

}

void NumToTwoChar(const char *charPtr, char number)
{
  charPtr[0] = (char)number / 10 + 48;
  charPtr[1] = (char)number % 10 + 48; /* Divisions Rest */
}


void CursorSetzen(char xPos, yPos)
{
  char puffer[15];

  strcpy(puffer, "\033[00;00H", 11);   /* CursPos Huelse */
/*                     ^  ^
                       |  5. Byte
                       2. Byte
*/
  NumToTwoChar(&puffer[2], yPos); /* Bytes 2 + 3 ersetzen */
  NumToTwoChar(&puffer[5], xPos); /* Bytes 5 + 6 ersetzen */

  ConWriteStr(conWritePtr, puffer); /* Cursor positionier.*/
}


void ANSICodeDemo()
{
  char  cursorReport[10];
  char  horiz,
        vert;
  int   laenge;

  ConWriteStr(conWritePtr, "\033[6n\n"); /* Status Report */
  laenge = ConReadSync                /* Report empfangen */
           (conReadPtr, cursorReport, sizeof(cursorReport));

  /* Report umsetzen in Cursor Positionierungs-Befehl */
  cursorReport[laenge - 1] = 'H';
  cursorReport[laenge] = (char)0;  /* in String umwandeln */

  FensterGroesse(&vert, &horiz);

  ConWriteStr(conWritePtr, "\033[3;34;47m"); /*blau/weiss */

  CursorSetzen(horiz / 2 - 6, vert / 2 - 1);
  ConWriteStr(conWritePtr, "           ");

  ConWriteStr(conWritePtr, "\033[1B");/*Cursor 1 line down*/
  ConWriteStr(conWritePtr, "\033[11D");  /*11 links bitte */
  ConWriteStr(conWritePtr, "   AMIGA   ");

  ConWriteStr(conWritePtr, "\033[1B");/*Cursor 1 line down*/
  ConWriteStr(conWritePtr, "\033[11D");  /* Cursor 11 left*/
  ConWriteStr(conWritePtr, "           ");

  /* vor und zurueck scrollen */

  ConWriteStr(conWritePtr, "\033[2T");
  ConWriteStr(conWritePtr, "\033[4S");
  ConWriteStr(conWritePtr, "\033[2T");

/* Cursor-Report modifiziert zurueckschreiben, um Cursor zu
   setzen, wo er anfangs war */
  ConWriteStr(conWritePtr,cursorReport);
}
