/*
 * PrtSc.c :
 * ---------
 * Auteur         : Xavier Mertens
 * Version        : 1 
 * Révision       : 12
 * Langage        : Lattice c/asm v5.0
 * Compatibilité  : 1.3 - 2.x - 3.0
 * Description    : Programme de copie d'écran graphique (CTRL-F10)
 *                  Idée de SelectArea() : La Bible Amiga p580
 *                                         (éd. Micro Application)
 * Compilation    : -v -mt0
 */

#include <exec/types.h>
#include <exec/memory.h>
#include <exec/execbase.h>
#include <devices/timer.h>
#include <devices/input.h>
#include <devices/inputevent.h>
#include <devices/printer.h>
#include <intuition/intuitionbase.h>
#include <libraries/dosextens.h>
#include <graphics/gfxbase.h>
#include <string.h>
#include <stdio.h>

#ifdef LATTICE
  int CXBRK(void) { return(0); }
  int chkabort(void) { return(0); }
#endif

#define PORT       "PRTSC_PORT"
#define INTERVALLE 1L
#define DEF_TIME   180
#define DEF_KEY    0x59 /* F10 */
#define PRINT(x)   Write(Output(),(x),strlen(x))
#define PRIORITE   -5

typedef struct{
  struct Task   *buddy;
  ULONG         printsig;
  ULONG         unblanksig;
  ULONG         noevents;
  short         creatsignum;
  short         blanksignum;
  short         key;
  struct Screen *blankscreen;
} GLOBAL_DATA;

struct MsgPort *FindPort(),
               *CreatePort();

struct OURMSG {
  struct Message msgpart;
  short  interval;
  short  key;
};

struct InputEvent *myhandler(ev, gptr)

struct InputEvent *ev; 
register GLOBAL_DATA *gptr;
{
  register struct InputEvent *ep, *laste;

  for(ep = ev, laste = NULL; ep != NULL; ep = ep->ie_NextEvent){
    if((ep->ie_Class == IECLASS_RAWKEY)&&(ep->ie_Code  == gptr->key)&&(ep->ie_Qualifier & IEQUALIFIER_CONTROL)){
      if(laste == NULL)
        ev = ep->ie_NextEvent;
      else
        laste->ie_NextEvent = ep->ie_NextEvent;
      Signal(gptr->buddy, gptr->printsig);
    }
    else
      laste = ep;

    if(ep->ie_Class != IECLASS_TIMER){
      gptr->noevents = 0;
      if(gptr->blankscreen != NULL)
        Signal(gptr->buddy, gptr->unblanksig);
    }
  }
  return(ev);
}

extern HandlerInterface();
extern struct ExecBase *SysBase;

/* 
  Définition d'un écran de profondeur 1 : Noir & Blanc.
  Ceci permet d'en récupérer la colormap SANS modifier les couleurs de
  l'écran à imprimer!
*/

static struct NewScreen NewBlankScreen = {
  0,0,320,200,1,0,1,NULL,CUSTOMSCREEN+SCREENBEHIND+SCREENQUIET,
  NULL,NULL,NULL,NULL
};

static USHORT BlankPalette[] = {
  0x0FFF,  /* Blanc */
  0x0000  /* Noir  */
};

/* Code généré par PowerWindows 2.5b */

static struct IntuiText IText6 = {
  3,0,JAM2,59,56,NULL,"à imprimer.",NULL
};

static struct IntuiText IText5 = {
  3,0,JAM2,18,46,NULL,"sélectionner la zone",&IText6
};

static struct IntuiText IText4 = {
  3,0,JAM2,50,36,NULL,"CTRL-F10 puis",&IText5
};

static struct IntuiText IText3 = {
  1,0,JAM2,54,26,NULL,"Septembre 92",&IText4
};

static struct IntuiText IText2 = {
  1,0,JAM2,11,16,NULL,"v1.12 © Xavier Mertens",&IText3
};

static struct IntuiText IText1 = {
  2,0,JAM2,56,5,NULL,"«« PrtSc »»",&IText2
};

static struct NewWindow NewInfosWindow = {
  220,80,200,70,0,1,MOUSEBUTTONS|RAWKEY,
  ACTIVATE+NOCAREREFRESH,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
  WBENCHSCREEN
};

struct Screen        *BlankScreen;
struct IntuitionBase *IntuitionBase;
struct GfxBase       *GfxBase;
struct timerequest   *timerreq;
struct IOStdReq      *inputRequestBlock;
struct MsgPort       *timerport;
struct Interrupt     handlerStuff;
struct Window        *InfosWindow;
struct MsgPort       *port,
                     *PrinterPort,
                     *inputDevPort;
struct IntuiMessage  *message; 
struct Task          *MyTask;

GLOBAL_DATA global;

union PrinterIO {
  struct IOStdReq     ios;
  struct IODRPReq     iodrp;
  struct IOPrtCmdReq  iopc;
};

union PrinterIO *request;

short key,
      timeout;

/**************************************************************************
 * Routine    : CreatePort()                                              *
 * Fonction   : Création d'un port de communication.                      *
 * Version    : 1.00                                                      *
 * Date       : 20 Sep 1992 - 12:09                                       *
 * Paramètres : char *name : Nom à donner au port.                        *
 *              int pri    : Sa priorité.                                 *
 * Sortie     : Un ptr sur le port créé.                                  *
 **************************************************************************/

struct MsgPort *CreatePort(char *name,int pri){
  UBYTE sigbit;
  register struct MsgPort *port;

  if((sigbit = AllocSignal(-1)) == -1)
    return((struct MsgPort *)0);

  if((port = (struct MsgPort *)AllocMem(sizeof(struct MsgPort),MEMF_CLEAR|MEMF_PUBLIC)) == 0){
    FreeSignal(sigbit);
    return((struct MsgPort *) (0));
  }
  port->mp_Node.ln_Name = name;
  port->mp_Node.ln_Pri = pri;
  port->mp_Node.ln_Type = NT_MSGPORT;
  port->mp_Flags = PA_SIGNAL;
  port->mp_SigBit = sigbit;
  port->mp_SigTask = (struct Task *)FindTask(0);
  AddPort(port);
  return(port);
}

/**************************************************************************
 * Routine    : CreateIOReq()                                             *
 * Fonction   : Création d'un request à partir d'un port de communication *
 * Version    : 1.00                                                      *
 * Date       : 20 Sep 1992 - 12:08                                       *
 * Paramètres : struct MsgPort *port : Port de communication concerné     *
 *              int size             : Taille du message                  *
 * Sortie     : Un pointeur sur le Request                                *
 **************************************************************************/

struct IOStdReq * CreateIOReq(struct MsgPort *port,int size){
  struct IOStdReq *ioReq;
  if((ioReq = (struct IOStdReq *)AllocMem(size, MEMF_CLEAR | MEMF_PUBLIC))){
    ioReq->io_Message.mn_Node.ln_Type = NT_MESSAGE;
    ioReq->io_Message.mn_Node.ln_Pri  = 0;
    ioReq->io_Message.mn_Length       = size;
    ioReq->io_Message.mn_ReplyPort    = port;
  }
  return(ioReq);
}

/**************************************************************************
 * Routine    : OpenPrinter()                                             *
 * Fonction   : Ouverture du printer.device.                              *
 * Version    : 1.00                                                      *
 * Date       : 20 Sep 1992 - 12:06                                       *
 * Paramètres : union PrinterIO *request : Ptr sur le request             *
 * Sortie     : TRUE/FALSE                                                *
 **************************************************************************/

int OpenPrinter(union PrinterIO *request){
  return(OpenDevice("printer.device",0,(struct IORequest *)request,0));
}

/**************************************************************************
 * Routine    : DumpRPort()                                               *
 * Fonction   : Sort sur l'imprimante le RastPort spécifié.               *
 * Version    : 1.00                                                      *  
 * Date       : 20 Sep 1992 - 11:44                                       *
 * Paramètres : union PrinterIO *request  : Ptr sur le request            *
 *              struct RastPort *rastport : Ptr sur le rp à imprimer      *
 *              struct ColorMap *colormap : Ptr sur la palette à utiliser *
 *              ULONG modes               : le mode d'écran à imprimer    *
 *              ULONG sx                  : Position x de départ (0)      *
 *              ULONG sy                  : Position y de départ (0)      *
 *              ULONG sw                  : Largeur de l'écran à imprimer *
 *              ULONG sh                  : Hauteur de l'écran à imprimer *
 *              ULONG dc                  : Fixe le format du Rp          *
 *              ULONG dr                  : Fixe le format du Rp          *
 *              UWORD s                   : Options d'impression          *
 * Sortie     : TRUE/FALSE                                                *
 **************************************************************************/

int DumpRPort(union PrinterIO *request,struct RastPort *rastport,struct ColorMap *colormap,ULONG modes,UWORD sx,UWORD sy,UWORD sw,UWORD sh,LONG dc,LONG dr,UWORD s){
  request->iodrp.io_Command   =PRD_DUMPRPORT;
  request->iodrp.io_RastPort  =rastport;
  request->iodrp.io_ColorMap  =colormap;
  request->iodrp.io_SrcX      =sx;
  request->iodrp.io_SrcY      =sy;
  request->iodrp.io_SrcWidth  =sw;
  request->iodrp.io_SrcHeight  =sh;
  request->iodrp.io_DestCols  =dc;
  request->iodrp.io_DestRows  =dr;
  request->iodrp.io_Special    =s;
  return(DoIO((struct IORequest *)request));
}

/**************************************************************************
 * Routine    : _main()                                                   *
 * Fonction   : Corps du programme.                                       *
 * Version    : 1.00                                                      *  
 * Date       : 20 Sep 1992 - 11:35                                       *
 * Paramètres : char *args : Liste des args passé par la ligne de cmd.    *
 * Sortie     : - (via CloseAll())                                        *
 **************************************************************************/

_main(char *args){

  struct OURMSG *msg;

  ULONG sig, timersig;

  ULONG StartX,
        StartY,
        EndX,
        EndY;

  global.creatsignum  = -1;
  global.blanksignum  = -1;
  timerreq            = NULL;
  timerport           = NULL;
  inputDevPort        = NULL;
  inputRequestBlock   = NULL;

  timeout=DEF_TIME;
  key=DEF_KEY;

  MyTask=SysBase->ThisTask;

  if ((port = CreatePort(PORT,NULL)) == NULL)
    CloseAll();

  if ((msg = (struct OURMSG *)AllocMem(sizeof(struct OURMSG), MEMF_CLEAR|MEMF_PUBLIC)) == NULL)
    CloseAll();

  msg->msgpart.mn_Length = sizeof(struct OURMSG);
  msg->interval          = timeout;
  msg->key                 = key;

  PutMsg(port,(struct Message *)msg);

  global.blankscreen = NULL;
  global.buddy       = (struct Task *)FindTask(0);
  global.noevents     = 0;

  if(((inputDevPort = CreatePort(0,0)) == NULL)                           ||
     ((inputRequestBlock =
          CreateIOReq(inputDevPort, sizeof(struct IOStdReq))) == NULL)    ||
     ((timerport = CreatePort(0,0)) == NULL)                              ||
     ((timerreq  = (struct timerequest *)
          CreateIOReq(timerport, sizeof(struct timerequest))) == NULL)    ||
     ((global.creatsignum = AllocSignal(-1)) == -1)                       ||
     ((global.blanksignum = AllocSignal(-1)) == -1)                       ||
     ((GfxBase = (struct GfxBase *)
          OpenLibrary("graphics.library", 0)) == NULL)                    ||
     ((IntuitionBase = (struct IntuitionBase *)
          OpenLibrary("intuition.library", 0)) == NULL)                   ||
     OpenDevice(TIMERNAME, UNIT_VBLANK, (struct IORequest *)timerreq, 0)  ||
     OpenDevice("input.device",0,(struct IORequest *)inputRequestBlock,0))
    CloseAll();

  if(((struct Process *)MyTask)->pr_CLI){ /* Lancé depuis le CLI */
    PRINT("PrtSc version 1.12 © Xavier Mertens - Septembre 92\n");
    PRINT("A n'importe quel moment,pressez CTRL-F10 et sélectionnez avec la\n");
    PRINT("souris la zone à imprimer...\n");
    PRINT("(CTRL-C interrompt le programme).\n");
  }    
  else{  /* Lancé depuis le Workbench */
    if(!(InfosWindow=(struct Window *)OpenWindow(&NewInfosWindow)))
      CloseAll();
    PrintIText(InfosWindow->RPort,&IText1,NULL,NULL);
    Wait(1<<InfosWindow->UserPort->mp_SigBit);
    CloseWindow(InfosWindow);
  }

  /* Fixer la priorité pour ne pas degrader les performances de la machine */
  SetTaskPri(MyTask,PRIORITE);

  handlerStuff.is_Data         = (APTR)&global;
  handlerStuff.is_Code         = HandlerInterface;
  handlerStuff.is_Node.ln_Pri = 51;

  timersig          = (1 << timerport->mp_SigBit);
  global.printsig   = 1 << global.creatsignum;
  global.unblanksig = 1 << global.blanksignum;

  inputRequestBlock->io_Command = IND_ADDHANDLER;
  inputRequestBlock->io_Data    = (APTR)&handlerStuff;

  DoIO((struct IORequest *)inputRequestBlock);

  QueueTimer(timerreq, INTERVALLE);

  FOREVER{

     sig = Wait( global.printsig | global.unblanksig | timersig | SIGBREAKF_CTRL_C);

    if((msg = (struct OURMSG *)GetMsg(port)) != NULL){
      global.key = msg->key;
      timeout    = msg->interval;
      FreeMem((char *)msg, msg->msgpart.mn_Length);
    }    

    if(sig & SIGBREAKF_CTRL_C)
      CloseAll(NULL);

    if(sig & global.printsig){
      SelectArea(IntuitionBase->ActiveWindow,&StartX,&StartY,&EndX,&EndY);
      if(BlankScreen=(struct Screen *)OpenScreen(&NewBlankScreen)){
        LoadRGB4(&BlankScreen->ViewPort,&BlankPalette[0],2);
        if(PrinterPort=(struct Port *)CreatePort(NULL,NULL)){
          if(request=(union PrinterIO *)CreateIOReq(PrinterPort,sizeof(union PrinterIO))){
            if(!OpenPrinter(request)){
              DumpRPort(request,IntuitionBase->ActiveWindow->RPort, /* Pour un écran : & !!!!! */
                                BlankScreen->ViewPort.ColorMap,
                                IntuitionBase->ActiveScreen->ViewPort.Modes,
                                (UWORD)StartX,
                                (UWORD)StartY,
                                (UWORD)(EndX-StartX),
                                (UWORD)(EndY-StartY),
                                (ULONG)(EndX-StartX),
                                (ULONG)((EndY-StartY)*2),
                                (UWORD)SPECIAL_FULLROWS|SPECIAL_FULLCOLS|SPECIAL_ASPECT|SPECIAL_DENSITY1);
              ClosePrinter(request);
            }
            DeleteIOReq(request);
          }
          DeletePort(PrinterPort);
        }
        CloseScreen(BlankScreen);
      }  
    }

    if(sig & timersig){
      GetMsg(timerport);
      QueueTimer(timerreq, INTERVALLE);
    }

  } 
}

/**************************************************************************
 * Routine    : SelectArea()                                              *
 * Fonction   : Détermine l'aire à imprimer                               *
 *              (idée : La Bible Amiga p580)                              *
 * Version    : 1.10                                                      *  
 * Date       : 24 Sep 1992 - 14:37                                       *
 * Paramètres : struct Screen *Screen : Adresse de l'écran sélectionné    *
 *              ULONG StartX           : Coord x de départ.               *
 *              ULONG StartY           : Coord y de départ.               *
 *              ULONG EndX             : Coord x de fin.                  *
 *              ULONG EndY             : Coord y de fin.                  *
 * Sortie     : Les coordonnées du rectangle à imprimer.                  *
 **************************************************************************/

SelectArea(Window,StartX,StartY,EndX,EndY)
  struct Window *Window;
  ULONG *StartX;
  ULONG *StartY;
  ULONG *EndX;
  ULONG *EndY;
{
  UBYTE *LBM=(UBYTE *)0xBFE001;
  ULONG Xold,
        Yold;
  *StartX=(ULONG)0;
  *StartY=(ULONG)0;
  *EndX   =(ULONG)0;
  *EndY  =(ULONG)0;

  SetDrMd(Window->RPort,COMPLEMENT);
  SetAPen(Window->RPort,3);

  while((*LBM & (UBYTE)0x40) == (UBYTE)0x40); /* On attend un clic de départ */

  *StartX=Window->MouseX;
  *StartY=Window->MouseY;  

  Delay(10);

  Xold=*StartX;
  Yold=*StartY;
  Move(Window->RPort,*StartX,*StartY);
  Draw(Window->RPort,Xold,*StartY);
  Draw(Window->RPort,Xold,Yold);
  Draw(Window->RPort,*StartX,Yold);
  Draw(Window->RPort,*StartX,*StartY);

  while((*LBM & (UBYTE)0x40) == (UBYTE)0x40){
    *EndX=(ULONG)Window->MouseX;
    *EndY=(ULONG)Window->MouseY;
    if((*EndX != Xold) || (*EndY != Yold)){
      Move(Window->RPort,*StartX,*StartY);
      Draw(Window->RPort,Xold,*StartY);
      Draw(Window->RPort,Xold,Yold);
      Draw(Window->RPort,*StartX,Yold);
      Draw(Window->RPort,*StartX,*StartY);

      Move(Window->RPort,*StartX,*StartY);
      Draw(Window->RPort,*EndX,*StartY);
      Draw(Window->RPort,*EndX,*EndY);
      Draw(Window->RPort,*StartX,*EndY);
      Draw(Window->RPort,*StartX,*StartY);

      Xold=*EndX;
      Yold=*EndY;
    }
  }
  Move(Window->RPort,*StartX,*StartY);
  Draw(Window->RPort,Xold,*StartY);
  Draw(Window->RPort,Xold,Yold);
  Draw(Window->RPort,*StartX,Yold);
  Draw(Window->RPort,*StartX,*StartY);
  SetDrMd(Window->RPort,JAM2);
  if(*StartX > *EndX){
    Xold=*StartX;
    *StartX=*EndX;
    *EndX=Xold;
  }
  if(*StartY > *EndY){
    Yold=*StartY;
    *StartY=*EndY;
    *EndY=Yold;
  }
}

/**************************************************************************
 * Routine    : CloseAll()                                                *
 * Fonction   : Libère toutes les ressources nécesaires au programme.     *
 * Version    : 1.00                                                      *  
 * Date       : 20 Sep 1992 - 11:40                                       *
 * Paramètres : -                                                         *
 * Sortie     : - (retour au Cli ou au Workbench)                         *
 **************************************************************************/

CloseAll(){
  if (timerreq != NULL){
     if (timerreq->tr_node.io_Device != NULL)
        CloseDevice((struct IORequest *)timerreq);
     DeleteIOReq((struct IOStdReq *)timerreq);
  }
  if (inputRequestBlock != NULL){
    if (inputRequestBlock->io_Device != NULL){
      inputRequestBlock->io_Command = IND_REMHANDLER;
      inputRequestBlock->io_Data = (APTR)&handlerStuff;
      DoIO((struct IORequest *)inputRequestBlock);
      CloseDevice((struct IORequest *)inputRequestBlock);
    }
    DeleteIOReq(inputRequestBlock);
  }
  if (timerport != NULL)          DeletePort(timerport);
  if (global.creatsignum != -1)   FreeSignal(global.creatsignum);
  if (global.blanksignum != -1)   FreeSignal(global.blanksignum);
  if (InfosWindow)                CloseWindow(InfosWindow);
  if (global.blankscreen != NULL) CloseScreen(global.blankscreen);
  if (IntuitionBase != NULL)      CloseLibrary((struct Library *)IntuitionBase);
  if (GfxBase != NULL)            CloseLibrary((struct Library *)GfxBase);
  if (inputDevPort != NULL)       DeletePort(inputDevPort);
  if (port != NULL)               DeletePort(port);
  DisplayBeep(NULL);
  Delay(50);
  Exit(0);
  return(TRUE);  /* Pour éviter un warning lors de la compilation. */
}

/**************************************************************************
 * Routine    : QueueTimer()                                              *
 * Fonction   : Détermine un delais d'attente au timer.device.            *
 * Version    : 1.00                                                      *  
 * Date       : 20 Sep 1992 - 11:39                                       *
 * Paramètres : struct timerequest *tr : Ptr sur le timer.                *
 *              ULONG secondes         : nbre de secondes d'attente.      *
 * Sortie     : TRUE                                                      *
 **************************************************************************/

QueueTimer(struct timerequest *tr,ULONG secondes){
  tr->tr_node.io_Command=TR_ADDREQUEST;
  tr->tr_time.tv_secs=secondes;
  tr->tr_time.tv_micro=0;
  SendIO((struct IORequest *)tr);
  return(TRUE);
}

/**************************************************************************
 * Routine    : DeletePort()                                              *
 * Fonction   : Libère la mémoire allouée pour un port de communication.  *
 * Version    : 1.00                                                      *  
 * Date       : 20 Sep 1992 - 11:38                                       *
 * Paramètres : struct MsgPort *port                                      *
 * Sortie     : TRUE                                                      *
 **************************************************************************/

DeletePort(struct MsgPort *port){
  RemPort(port);
  FreeSignal(port->mp_SigBit);
  FreeMem((char *)port,sizeof(struct MsgPort));
  return(TRUE);
}

/**************************************************************************
 * Routine    : DeleteIOReq()                                             *
 * Fonction   : Libère la mémoire nécessaire au Request.                  *
 * Version    : 1.00                                                      *  
 * Date       : 20 Sep 1992 - 11:37                                       *
 * Paramètres : struct IOStdReq *ioReq                                    *
 * Sortie     : TRUE                                                      *
 **************************************************************************/

DeleteIOReq(struct IOStdReq *ioReq){
  ioReq->io_Message.mn_Node.ln_Type=0xff;
  ioReq->io_Device=(struct Device *)-1;
  ioReq->io_Unit=(struct Unit *)-1;
  FreeMem((char *)ioReq,ioReq->io_Message.mn_Length);
  return(TRUE);
}

/**************************************************************************
 * Routine    : ClosePrinter()                                            *
 * Fonction   : Ferme le printer.device.                                  *
 * Version    : 1.00                                                      *  
 * Date       : 20 Sep 1992 - 11:35                                       *
 * Paramètres : union PrinterIO *request : IORequest.                     *
 * Sortie     : TRUE                                                      *
 **************************************************************************/

ClosePrinter(union PrinterIO *request){
  CloseDevice((struct IORequest *)request);
  return(TRUE);
}

/* Eof */
