
/*  JustLook!
    © Kamran Karimi
*/

#include <string.h>
#include <stdlib.h>
#include <exec/types.h>
#include <exec/memory.h>
#include <devices/input.h>
#include <intuition/intuitionbase.h>
#include <intuition/intuition.h>
#include <workbench/workbench.h>
#include <clib/intuition_protos.h>
#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#include <clib/icon_protos.h>
#include <clib/alib_protos.h>

#include "JustLook.h"

extern struct IntuitionBase *IntuitionBase;
extern struct Library *IconBase;

extern VOID DisableIE(),WaitIE();

UBYTE HandlerName[] = "JustLook's NoIE Handler";
UBYTE WaitName[] = "JustLook's WaitIE Handler";

struct MsgPort *NoMouse_Port = NULL,*NoKey_Port = NULL,*Global_IEPort = NULL;
struct Interrupt *Global_IEHandler = NULL;
struct IOStdReq *Global_IEReq = NULL;

struct Task *Me;
ULONG LeftMask,MidMask,RightMask;

    

ErrorCode GetRatio(struct ScrMap *SM,struct Screen *Scr)
{
 ULONG IBaseLock;
 SHORT DeltaX = 8,DeltaY = 8,CurrX,CurrY;
 ErrorCode ErrCode = NO_ERROR;
 struct MsgPort *IEPort = NULL;
 struct IOStdReq *IEReq = NULL;
 struct InputEvent *FakeEvent = NULL;

 if(IEPort = CreatePort(NULL,0)) 
 {
  if(FakeEvent = AllocMem(sizeof(struct InputEvent),MEMF_PUBLIC))
  {
   if(IEReq = (struct IOStdReq *)CreateExtIO(IEPort,sizeof(struct IOStdReq)))
   {
    if(!OpenDevice("input.device",0,(struct IORequest *)IEReq,0))
    { 
     FakeEvent->ie_NextEvent = NULL;
     FakeEvent->ie_Class = IECLASS_RAWMOUSE;
     FakeEvent->ie_Code = IECODE_NOBUTTON;
     FakeEvent->ie_Qualifier = IEQUALIFIER_RELATIVEMOUSE;
     FakeEvent->ie_X = DeltaX;
     FakeEvent->ie_Y = DeltaY; 
     FakeEvent->ie_SubClass = 0xFF; /* shows that we are generating this */
     IEReq->io_Command = IND_WRITEEVENT;
     IEReq->io_Data = (APTR)FakeEvent;
     IEReq->io_Flags = 0;
     IEReq->io_Length = sizeof(struct InputEvent);
     IBaseLock = LockIBase(0); 
      CurrX = Scr->MouseX;
      CurrY = Scr->MouseY;
      if(CurrX > Scr->LeftEdge + Scr->Width - 32) DeltaX = -8;
      if(CurrY > Scr->TopEdge + Scr->Height - 32) DeltaY = -8;
     UnlockIBase(IBaseLock); 
     FakeEvent->ie_X = DeltaX;
     FakeEvent->ie_Y = DeltaY;
     DoIO((struct IORequest *)IEReq);
     IBaseLock = LockIBase(0); 
      CurrX = Scr->MouseX - CurrX;
      CurrY = Scr->MouseY - CurrY;
      if(!CurrX) ErrCode |= NO_RATIO;
      else if(abs(CurrX) <= abs(DeltaX)) SM->XRatio = (DeltaX / CurrX);
      else SM->XRatio = -(CurrX / DeltaX);      
      if(!CurrY) ErrCode |= NO_RATIO;
      else if(abs(CurrY) <= abs(DeltaY)) SM->YRatio = (DeltaY / CurrY);
      else SM->YRatio = -(CurrY / DeltaY);
     UnlockIBase(IBaseLock);  
     CloseDevice((struct IORequest *)IEReq);
    }
    else ErrCode |= NO_DEVICE; 
    DeleteExtIO((struct IORequest *)IEReq);
   }
   else ErrCode |= NO_EXTIO;
   FreeMem(FakeEvent,sizeof(struct InputEvent));
  }
  else ErrCode |= NO_MEM;
  DeletePort(IEPort);
 }
 else ErrCode |= NO_PORT;
 return(ErrCode);
}


struct Screen *FindScreen(char *TitleToSearch)
{
 ULONG IBaseLock;
 struct Screen *CurrScreen,*ScreenToReturn = NULL;
 BOOLEAN ScrFound = FALSE;
 
 if(TitleToSearch == NULL) return(NULL);
 IBaseLock = LockIBase(0);
 CurrScreen = IntuitionBase->FirstScreen;
 while(CurrScreen)
 {
  if(!strcmp(CurrScreen->Title,TitleToSearch)) 
  {
   if(ScrFound) ScreenToReturn = SCR_REPEATED;
   else
   {
    ScrFound = TRUE;
    ScreenToReturn = CurrScreen;
   }
  }
  CurrScreen = CurrScreen->NextScreen;
 }
 UnlockIBase(IBaseLock);
 return(ScreenToReturn);
}


struct Window *FindWindow(char *TitleToSearch,struct Screen *ScreenToSearch)
{
 BOOLEAN ShouldLoop;
 ULONG IBaseLock;
 struct Screen *CurrScreen;
 struct Window *CurrWindow,*WindowToReturn = NULL;
 BOOLEAN WinFound = FALSE;

 ShouldLoop = (ScreenToSearch) ? FALSE : TRUE;
 CurrScreen = (ScreenToSearch) ? ScreenToSearch : IntuitionBase->FirstScreen; 
 IBaseLock = LockIBase(0);
 while(CurrScreen)
 {
  CurrWindow = CurrScreen->FirstWindow;
  while(CurrWindow)
  {
   if(!strcmp(CurrWindow->Title,TitleToSearch)) 
   {
    if(WinFound) WindowToReturn = WIN_REPEATED;
    else
    {
     WinFound = TRUE;
     WindowToReturn = CurrWindow;
    }
   }
   CurrWindow = CurrWindow->NextWindow;
  }
  CurrScreen = (ShouldLoop) ?  CurrScreen->NextScreen : NULL;
 }
 UnlockIBase(IBaseLock);
 return(WindowToReturn);
}

struct Gadget *FindGad(struct Window *Win,char *GadName,USHORT GadID)
{
 ULONG IBaseLock;
 struct Gadget *CurrGad;

 IBaseLock = LockIBase(0);
 CurrGad = Win->FirstGadget;
 while(CurrGad)
 {
  if(GadName) { if(!strcmp(CurrGad->GadgetText->IText,GadName)) break; }
  else if(CurrGad->GadgetID == GadID) break;
  CurrGad = CurrGad->NextGadget;
 }
 UnlockIBase(IBaseLock);
 return(CurrGad);
}


VOID Rel2Abs(struct ScrMap *SM,SHORT *x, SHORT *y)
{
 *x += SM->CurrX;
 *y += SM->CurrY;
}


VOID SetDest(struct ScrMap *SM,SHORT X,SHORT Y)
{
 SM->DestX = X;
 SM->DestY = Y;
}


ErrorCode InitSM(struct ScrMap *SM,struct Screen *Scr)
{
 ULONG IBaseLock;
 
 if(!SM) return(NO_SM);
 if(!Scr) return(NO_SCR);
 IBaseLock = LockIBase(0);
 SM->CurrX = Scr->MouseX;
 SM->CurrY = Scr->MouseY;
 SM->Scr = Scr;
 UnlockIBase(IBaseLock);
 return(GetRatio(SM,Scr));
}


ErrorCode IEWait(LONG Flag)
{
 BYTE LeftSig,MidSig,RightSig;
 ULONG Mask;
 struct MsgPort *WPort;
 struct Interrupt *WHandler = NULL;
 struct IOStdReq *WReq = NULL;

 ErrorCode ErrCode = NO_ERROR;

 if(!(Flag & MOUSE)) return(NO_ACT);
 
 Me = FindTask(NULL);
 
 if(WPort = CreatePort(NULL,0)) 
 {
  if(WHandler = AllocMem(sizeof(struct Interrupt),MEMF_PUBLIC|MEMF_CLEAR))
  {
   if(WReq = (struct IOStdReq *)CreateExtIO(WPort,sizeof(struct IOStdReq)))
   {
    if(!OpenDevice("input.device",NULL,(struct IORequest *)WReq,NULL))
    {
     LeftSig = AllocSignal(-1);
     MidSig = AllocSignal(-1);
     RightSig = AllocSignal(-1);
     if(LeftSig & MidSig & RightSig)
     {
      LeftMask = 1L << LeftSig;
      RightMask = 1L << RightSig;
      MidMask = 1L << MidSig;
      
      WHandler->is_Code = WaitIE;
      WHandler->is_Data = NULL;
      WHandler->is_Node.ln_Pri = 127;
      WHandler->is_Node.ln_Name = WaitName;
      WReq->io_Data = (APTR) WHandler;
      WReq->io_Command = IND_ADDHANDLER;
      DoIO((struct IORequest *)WReq);
      CloseDevice((struct IORequest *)WReq);
     }
     else ErrCode = NO_SIG; 
    }
    else ErrCode |= NO_DEVICE;
    if(ErrCode) DeleteExtIO((struct IORequest *)WReq);
   }
   else ErrCode |= NO_EXTIO;
   if(ErrCode) FreeMem(WHandler,sizeof(struct Interrupt));
  }
  else ErrCode |= NO_MEM;
 }
 else ErrCode |= NO_PORT;
 if(ErrCode) return(ErrCode);

 Mask = Wait(LeftMask | MidMask | RightMask);
 if(Mask & LeftMask) ErrCode = LBMASK;
 if(Mask & MidMask) ErrCode |= MBMASK;
 if(Mask & RightMask) ErrCode |= RBMASK;
 if(!OpenDevice("input.device",NULL,(struct IORequest *)WReq,NULL))
 {
  WReq->io_Data = (APTR) WHandler;
  WReq->io_Command = IND_REMHANDLER;
  DoIO((struct IORequest *)WReq);
  CloseDevice((struct IORequest *)WReq);
 }
 if(WReq) DeleteExtIO((struct IORequest *)WReq);
 if(WHandler) FreeMem(WHandler,sizeof(struct Interrupt));
 if(WPort) DeletePort(WPort);
 FreeSignal(LeftSig);
 FreeSignal(MidSig);
 FreeSignal(RightSig);
 return(ErrCode);
}

ErrorCode IEEnable(LONG  Flag)
{
 ErrorCode ErrCode = NO_ERROR;

 if(!(Flag & MOUSE) && !(Flag & KBD)) return(NO_ACT);
 if(!Global_IEHandler || !Global_IEReq || !Global_IEPort) return(NO_STRUCT);
 if(Flag & MOUSE)
  if(NoMouse_Port)
  {
   DeletePort(NoMouse_Port);
   NoMouse_Port = NULL;
  }
 if(Flag & KBD)
  if(NoKey_Port)
  {
   DeletePort(NoKey_Port);
   NoKey_Port = NULL;
  }
 if(!NoMouse_Port && !NoKey_Port)
 {
  if(!OpenDevice("input.device",NULL,(struct IORequest *)Global_IEReq,NULL))
  {
   Global_IEReq->io_Data = (APTR) Global_IEHandler;
   Global_IEReq->io_Command = IND_REMHANDLER;
   DoIO((struct IORequest *)Global_IEReq);
   CloseDevice((struct IORequest *)Global_IEReq);
  }
  else ErrCode |= NO_DEVICE;
  DeleteExtIO((struct IORequest *)Global_IEReq); Global_IEReq = NULL;
  FreeMem(Global_IEHandler,sizeof(struct Interrupt));
  Global_IEHandler = NULL;
  DeletePort(Global_IEPort);
  Global_IEPort = NULL;
 }
 return(ErrCode);
}

ErrorCode IEDisable(LONG Flag)
{
 ErrorCode ErrCode = NO_ERROR;
 BOOLEAN ShouldInstallHandler = FALSE;

 if(!(Flag & MOUSE) && !(Flag & KBD)) return(NO_ACT);
 if(!NoMouse_Port && !NoKey_Port) ShouldInstallHandler = TRUE; 
 if(Flag & MOUSE)
 {
  Forbid();
  if(!(NoMouse_Port = FindPort("JustLook's NoMouse Port!")))
  {
   if(!(NoMouse_Port = CreatePort("JustLook's NoMouse Port!",0)))
    ErrCode = NO_PORT;
  }
  else ErrCode = PORT_FOUND;
  Permit();
 }
 if(ErrCode) return(ErrCode);
 if(Flag & KBD)
 {
  Forbid();
  if(!(NoKey_Port = FindPort("JustLook's NoKey Port!")))
  {
   if(!(NoKey_Port = CreatePort("JustLook's NoKey Port!",0)))
    ErrCode = NO_PORT;
  }
  else ErrCode = PORT_FOUND;
  Permit();
 }
 if(ErrCode)
 {
  if(Flag & MOUSE)
  {
   DeletePort(NoMouse_Port);
   NoMouse_Port = NULL;
  }
  return(ErrCode);
 }
 if(ShouldInstallHandler)
 {
  if(Global_IEPort = CreatePort(NULL,0)) 
  {
   if(Global_IEHandler = AllocMem(sizeof(struct Interrupt),
                                                     MEMF_PUBLIC|MEMF_CLEAR))
   {
    if(Global_IEReq = (struct IOStdReq *)CreateExtIO(Global_IEPort,
                                                    sizeof(struct IOStdReq)))
    {
     if(!OpenDevice("input.device",NULL,
                                      (struct IORequest *)Global_IEReq,NULL))
     {
      Global_IEHandler->is_Code = DisableIE;
      Global_IEHandler->is_Data = NULL;
      Global_IEHandler->is_Node.ln_Pri = 126;
      Global_IEHandler->is_Node.ln_Name = HandlerName;
      Global_IEReq->io_Data = (APTR) Global_IEHandler;
      Global_IEReq->io_Command = IND_ADDHANDLER;
      DoIO((struct IORequest *)Global_IEReq);
      CloseDevice((struct IORequest *)Global_IEReq);
     }
     else ErrCode |= NO_DEVICE;
     if(ErrCode) 
     { 
      DeleteExtIO((struct IORequest *)Global_IEReq); 
      Global_IEReq = NULL;
     }
    }
    else ErrCode |= NO_EXTIO;
    if(ErrCode) 
    {
     FreeMem(Global_IEHandler,sizeof(struct Interrupt));
     Global_IEHandler = NULL;
    }
   }
   else ErrCode |= NO_MEM;
   if(ErrCode)
   {
    DeletePort(Global_IEPort);
    Global_IEPort = NULL;
   }
  }
  else ErrCode |= NO_PORT;
 }
 if(ErrCode)
 {
  if(Flag & MOUSE)
  {
   DeletePort(NoMouse_Port);
   NoMouse_Port = NULL;
  }
  if(Flag & KBD)
  {
   DeletePort(NoKey_Port);
   NoKey_Port = NULL;
  }
 }
 return(ErrCode);
}


ErrorCode RawType(struct RawInfo *RawKeys,ULONG Length,ULONG delay)
{
 ErrorCode ErrCode = NO_ERROR;
 struct MsgPort *IEPort = NULL;
 struct IOStdReq *IEReq = NULL;
 struct InputEvent *FakeEvent = NULL;
 ULONG count,count2;
 
 if(!RawKeys) return(NO_KEY);
 if(IEPort = CreatePort(NULL,0)) 
 {
  if(FakeEvent = AllocMem(sizeof(struct InputEvent),MEMF_PUBLIC))
  {
   if(IEReq = (struct IOStdReq *)CreateExtIO(IEPort,sizeof(struct IOStdReq)))
   {
    if(!OpenDevice("input.device",0,(struct IORequest *)IEReq,0))
    { 
     for(count = 0; count < Length; count++)
     {
      for(count2 = 0; count2 < 2; count2++)
      {
       FakeEvent->ie_NextEvent = NULL;
       FakeEvent->ie_Class = IECLASS_RAWKEY;
       FakeEvent->ie_Code = RawKeys[count].RawKey;
       if(count2 == 1) FakeEvent->ie_Class |= IECODE_UP_PREFIX;
       FakeEvent->ie_Qualifier = RawKeys[count].Qualifier;
       FakeEvent->ie_SubClass = 0xFF; /* shows that we are generating this */
       IEReq->io_Command = IND_WRITEEVENT;
       IEReq->io_Data = (APTR)FakeEvent;
       IEReq->io_Flags = 0;
       IEReq->io_Length = sizeof(struct InputEvent);

       DoIO((struct IORequest *)IEReq);
       Delay(delay / 3);
      }
      Delay(delay);
     }
     CloseDevice((struct IORequest *)IEReq);
    }
    else ErrCode |= NO_DEVICE; 
    DeleteExtIO((struct IORequest *)IEReq);
   }
   else ErrCode |= NO_EXTIO;
   FreeMem(FakeEvent,sizeof(struct InputEvent));
  }
  else ErrCode |= NO_MEM;
  DeletePort(IEPort);
 }
 else ErrCode |= NO_PORT;
 return(ErrCode);
}


ErrorCode ChooseMenu(struct ScrMap *SM,struct Window *Win,SHORT Menu, 
                                                    SHORT MItem,SHORT SItem)
{
 ULONG IBaseLock;
 ErrorCode ErrCode;
 struct Screen *Scr;
 struct Menu *CurrMenu;
 struct MenuItem *CurrItem,*CurrSub;
 SHORT XPos,YPos,TempYPos,count;

 if(!SM) return(NO_SM);
 if(Menu < 0) return(BAD_ITEM);
 ErrCode = Click(SM,RBUTTON,0,DOWN);
 if(ErrCode) return(ErrCode);
 IBaseLock = LockIBase(0);
 Scr = Win->WScreen;
 CurrMenu = Win->MenuStrip;
 count = 0; 
 while(CurrMenu && (count < Menu)) 
 {
  CurrMenu = CurrMenu->NextMenu;
  count++;
 }
 if(!CurrMenu) ErrCode = NO_MENU;
 else
 {
  XPos = CurrMenu->LeftEdge + Scr->WBorLeft + (CurrMenu->Width >> 1);
  YPos = Scr->WBorTop + (Scr->BarHeight >> 1); 
 }
 UnlockIBase(IBaseLock);
 if(ErrCode) return(ErrCode | Click(SM,RBUTTON,0,UP));
 Delay(15);
 SetDest(SM,XPos,YPos);
 ErrCode = MoveMouse(SM,ABSOLUTE,1);
 if(ErrCode) return(ErrCode | Click(SM,RBUTTON,0,UP));
 if(MItem != -1)
 {
  IBaseLock = LockIBase(0);
  CurrItem = CurrMenu->FirstItem;
  count = 0; 
  while(CurrItem && (count < MItem)) 
  { 
   CurrItem = CurrItem->NextItem;
   count++;
  }
  if(!CurrItem) ErrCode = NO_ITEM;
  else
  { 
   XPos = CurrMenu->LeftEdge + CurrItem->LeftEdge + (CurrItem->Width >> 1);
   YPos = Scr->BarHeight + CurrItem->TopEdge + (CurrItem->Height >> 1);
  }
  UnlockIBase(IBaseLock);
  if(ErrCode) return(Click(SM,RBUTTON,0,UP) | ErrCode);
  Delay(15);
  SetDest(SM,XPos,YPos);
  ErrCode = MoveMouse(SM,ABSOLUTE,1);
  if(ErrCode) return(ErrCode  | Click(SM,RBUTTON,0,UP));
 }

 if(SItem != -1)
 {
  IBaseLock = LockIBase(0);
  CurrSub = CurrItem->SubItem;
  count = 0; 
  while(CurrSub && (count < SItem)) 
  {
   CurrSub = CurrSub->NextItem;
   count++;
  }
  if(!CurrSub) ErrCode = NO_SUB;
  else
  { 
   XPos = CurrMenu->LeftEdge + CurrItem->LeftEdge + 
                                   CurrSub->LeftEdge + (CurrSub->Width >> 1);
   TempYPos = Scr->BarHeight + 
                CurrItem->TopEdge + CurrSub->TopEdge + (CurrSub->Height >> 1);
  }
  UnlockIBase(IBaseLock);
  if(ErrCode) return(ErrCode | Click(SM,RBUTTON,0,UP));
  Delay(15);
  SetDest(SM,XPos,YPos); /* just go in x direction */
  ErrCode = MoveMouse(SM,ABSOLUTE,1);
  if(ErrCode) return(ErrCode | Click(SM,RBUTTON,0,UP));
  Delay(15);
  SetDest(SM,XPos,TempYPos);
  ErrCode = MoveMouse(SM,ABSOLUTE,1);
  if(ErrCode) return(ErrCode | Click(SM,RBUTTON,0,UP));
 }
 Delay(25);
 return(Click(SM,RBUTTON,0,UP));
}


ErrorCode WinResize(struct ScrMap *SM,struct Window *Win,BOOLEAN IsRelative,
                                                             SHORT X,SHORT Y)
{
 ULONG IBaseLock,Flags;
 ErrorCode ErrCode;
 SHORT XPos,YPos;

 if(!SM) return(NO_SM);
 if(!Win) return(NO_WIN);
 IBaseLock = LockIBase(0);
 Flags = Win->Flags;
 XPos = Win->WScreen->LeftEdge + Win->LeftEdge + Win->Width - 3;
 YPos = Win->WScreen->TopEdge + Win->TopEdge + Win->Height - 3;
 UnlockIBase(IBaseLock);
 if(!(Flags & WFLG_SIZEGADGET)) return(NO_ACT);
 SetDest(SM,XPos,YPos);
 ErrCode = MoveMouse(SM,ABSOLUTE,1);
 if(ErrCode) return(ErrCode);
 ErrCode = Click(SM,LBUTTON,0,DOWN);
 if(ErrCode) return(ErrCode);
 Delay(15);
 SetDest(SM,X,Y);
 ErrCode = MoveMouse(SM,IsRelative,1);
 return(ErrCode | Click(SM,LBUTTON,0,UP));
}


ErrorCode WinAct(struct ScrMap *SM,struct Window *Win,LONG Act)
{
 ULONG IBaseLock,Flags;
 ErrorCode ErrCode = NO_ERROR; 
 SHORT XPos,YPos,Width; 

 if(!SM) return(NO_SM);
 if(!Win) return(NO_WIN);
 IBaseLock = LockIBase(0);
 Flags = Win->Flags;
 XPos = Win->WScreen->LeftEdge + Win->LeftEdge;
 YPos = Win->WScreen->TopEdge + Win->TopEdge + 2;
 Width = Win->Width;
 UnlockIBase(IBaseLock);
 switch(Act)
 {
  case CLOSEWIN:
                if(!(Flags & WFLG_CLOSEGADGET)) ErrCode =  NO_ACT;
                break;
  case DEPTHWIN:
                if(!(Flags & WFLG_DEPTHGADGET)) ErrCode =  NO_ACT;
                break;
  default: ErrCode = NO_ACT; 
 }
 if(ErrCode) return(ErrCode);
 if(Act == CLOSEWIN) XPos += 2;
 else if(Act == DEPTHWIN) XPos += (Width - 2);
 SetDest(SM,XPos,YPos);
 ErrCode = MoveMouse(SM,ABSOLUTE,1);
 if(ErrCode) return(ErrCode);
 return(Click(SM,LBUTTON,0,DOWN_UP));
}

ErrorCode WinDrag(struct ScrMap *SM,struct Window *Win,BOOLEAN IsRelative,
                                                           SHORT X, SHORT Y)
{
 ULONG IBaseLock,Flags;
 ErrorCode ErrCode = NO_ERROR;
 SHORT XPos,YPos;
 
 if(!SM) return(NO_SM);
 if(!Win) return(NO_WIN);
 IBaseLock = LockIBase(0);
 Flags = Win->Flags;
 XPos = Win->WScreen->LeftEdge + Win->LeftEdge + (Win->Width >> 1);
 YPos = Win->WScreen->TopEdge +Win->TopEdge + 2;
 UnlockIBase(IBaseLock);
 if(!(Flags & WFLG_DRAGBAR)) return(NO_ACT);
 SetDest(SM,XPos,YPos);
 ErrCode = MoveMouse(SM,ABSOLUTE,1);
 if(ErrCode) return(ErrCode);
 Delay(15);
 ErrCode = Click(SM,LBUTTON,0,DOWN);
 if(ErrCode) return(ErrCode);
 Delay(15);
 SetDest(SM,X,Y);
 ErrCode = MoveMouse(SM,IsRelative,1);
 return(ErrCode | Click(SM,LBUTTON,0,UP));
}
  

ErrorCode MoveMouse(struct ScrMap *SM,BOOLEAN IsRelative,SHORT Speed)
{   
 ULONG IBaseLock;
 SHORT AbsX,AbsY,CurrX,CurrY,PrevX,PrevY,CurrXStop,CurrYStop;
 SHORT Reps = 1;
 BOOL XDone = FALSE,YDone = FALSE;

 ErrorCode ErrCode = NO_ERROR;

 struct Screen *CurrScreen;
 struct MsgPort	*IEPort = NULL;
 struct IOStdReq *IEReq = NULL;
 struct InputEvent *FakeEvent = NULL;


 if(!SM) return(NO_SM);
 AbsX = SM->DestX; AbsY = SM->DestY;

 IBaseLock = LockIBase(0);
 CurrScreen = SM->Scr;
 CurrX = CurrScreen->MouseX;
 CurrY = CurrScreen->MouseY;
 UnlockIBase(IBaseLock); 
 if(IsRelative) Rel2Abs(SM,&AbsX,&AbsY);
 Speed = (Speed % SPEEDLEVELS);
 if(!Speed) Speed = 1;
 CurrXStop = PrevX = CurrX + 1;
 CurrYStop = PrevY = CurrY + 1; 

 if(IEPort = CreatePort(NULL,0)) 
 {
  if(FakeEvent = AllocMem(sizeof(struct InputEvent),MEMF_PUBLIC))
  {
   if(IEReq = (struct IOStdReq *)CreateExtIO(IEPort,sizeof(struct IOStdReq)))
   {
    if(!OpenDevice("input.device",0,(struct IORequest *)IEReq,0))
    { 
     for(;;)
     {
      FakeEvent->ie_NextEvent = NULL;
      FakeEvent->ie_Class = IECLASS_RAWMOUSE;
      FakeEvent->ie_Code = IECODE_NOBUTTON;
      FakeEvent->ie_Qualifier = IEQUALIFIER_RELATIVEMOUSE;
      FakeEvent->ie_SubClass = 0xFF; /* shows that we are generating this */  
      IEReq->io_Command = IND_WRITEEVENT;
      IEReq->io_Data = (APTR)FakeEvent;
      IEReq->io_Flags = 0;
      IEReq->io_Length = sizeof(struct InputEvent);
      
      if(CurrX < AbsX - (Speed >> 1)) FakeEvent->ie_X = Speed;
      else if(CurrX > AbsX + (Speed >> 1)) FakeEvent->ie_X = -Speed;
      else { XDone = TRUE; FakeEvent->ie_X = 0; }  
   
      if(CurrY < AbsY - (Speed >> 1)) FakeEvent->ie_Y = Speed;
      else if(CurrY > AbsY + (Speed >> 1)) FakeEvent->ie_Y = -Speed;
      else { YDone = TRUE; FakeEvent->ie_Y = 0; }        

      if(XDone && YDone) break;
    
      if((CurrX == PrevX) && (CurrY == PrevY))
      {
       if((CurrX == CurrXStop) && (CurrY == CurrYStop))  
        { if(Reps++ == 10) { ErrCode |= NO_MOVE; break; } }
       else { Reps = 1; CurrXStop = CurrX; CurrYStop = CurrY; }
      }
      PrevX = CurrX; PrevY = CurrY;
      DoIO((struct IORequest *)IEReq);

      IBaseLock = LockIBase(0);
      CurrX = CurrScreen->MouseX; 
      CurrY = CurrScreen->MouseY;
      UnlockIBase(IBaseLock);
     }
     SM->CurrX = AbsX;
     SM->CurrY = AbsY;
     CloseDevice((struct IORequest *)IEReq);
    }
    else ErrCode |= NO_DEVICE; 
    DeleteExtIO((struct IORequest *)IEReq);
   }
   else ErrCode |= NO_EXTIO;
   FreeMem(FakeEvent,sizeof(struct InputEvent));
  }
  else ErrCode |= NO_MEM;
  DeletePort(IEPort);
 }
 else ErrCode |= NO_PORT;
 return(ErrCode);
}    


ErrorCode GoOverIcon(struct ScrMap *SM,struct Window *Win,UBYTE *Name)
{
 ULONG IBaseLock;
 struct DiskObject *DiskObj;
 struct Gadget *CurrGad;
 SHORT XPos,YPos;

 if(!SM) return(NO_SM);
 if(!Win) return(NO_WIN);
 DiskObj = GetDiskObject(Name);
 if(!DiskObj) return(NO_OBJ);
 CurrGad = &(DiskObj->do_Gadget);
 if(!CurrGad) return(NO_OBJ);
 IBaseLock = LockIBase(0);
 XPos =  Win->LeftEdge + DiskObj->do_CurrentX + (CurrGad->Width >> 1);
 YPos = Win->TopEdge + Win->WScreen->BarHeight + DiskObj->do_CurrentY + 
                                                      (CurrGad->Height >> 1);
 UnlockIBase(IBaseLock);
 FreeDiskObject(DiskObj);
 SetDest(SM,XPos,YPos);
 return(MoveMouse(SM,ABSOLUTE,1));
}

 
ErrorCode ClickGad(struct ScrMap *SM,struct Window *Win,char *GadName,
                                                    USHORT GadID,SHORT Speed)
{
 ErrorCode ErrCode;

 ErrCode = GoOverGad(SM,Win,GadName,GadID,Speed);
 if(ErrCode) return(ErrCode);
 Delay(25);
 return(Click(SM,LBUTTON,0,DOWN_UP));
}


ErrorCode GoOverGad(struct ScrMap *SM,struct Window *Win,char *GadName,
                                                   USHORT GadID,SHORT Speed)
{
 struct Gadget *CurrGad; 
 ULONG IBaseLock;
 SHORT XPos,YPos,Width,Height;

 if(!SM) return(NO_SM);
 if(!Win) return(NO_WIN);
 if(!(CurrGad = FindGad(Win,GadName,GadID))) return(NO_GAD);
 
 IBaseLock = LockIBase(0);
 Width = CurrGad->Width;
 if(CurrGad->Flags & GFLG_RELWIDTH) Width += Win->Width;
 
 Height = CurrGad->Height;
 if(CurrGad->Flags & GFLG_RELHEIGHT) Height += Win->Height;

 XPos = Win->LeftEdge + CurrGad->LeftEdge + (Width >> 1);
 if(CurrGad->Flags & GFLG_RELRIGHT) XPos += Win->Width;
    
 YPos = Win->TopEdge + CurrGad->TopEdge + (Height >> 1);
 if(CurrGad->Flags & GFLG_RELBOTTOM) YPos += Win->Height;

 UnlockIBase(IBaseLock);
 SetDest(SM,XPos,YPos);
 return(MoveMouse(SM,ABSOLUTE,Speed));
}

ErrorCode Click(struct ScrMap *SM,WORD MouseButton,UWORD Qualifier,
                                                                 WORD UpDown)
{
 ULONG IBaseLock;
 SHORT XPos,YPos;

 ErrorCode ErrCode = NO_ERROR;

 struct MsgPort *IEPort = NULL;
 struct IOStdReq *IEReq = NULL;
 struct InputEvent *FakeEvent = NULL;

 if(MouseButton < 0 || MouseButton > 2) return(NO_BUTTON);
 if(UpDown < 0 || UpDown > 2) return(NO_ACT);
 if(!SM) return(NO_SM);
 if(IEPort = CreatePort(NULL,0)) 
 {
  if(FakeEvent = AllocMem(sizeof(struct InputEvent),MEMF_PUBLIC|MEMF_CLEAR))
  {
   if(IEReq = (struct IOStdReq *)CreateExtIO(IEPort,sizeof(struct IOStdReq)))
   {
    if(!OpenDevice("input.device",0,(struct IORequest *)IEReq,0))
    {
     FakeEvent->ie_Class = IECLASS_RAWMOUSE;
     FakeEvent->ie_NextEvent = NULL;
     FakeEvent->ie_Code = IECODE_LBUTTON + MouseButton;
     if(UpDown == UP) FakeEvent->ie_Code |= IECODE_UP_PREFIX; 
     FakeEvent->ie_Qualifier = Qualifier;  
     FakeEvent->ie_SubClass = 0xFF; /* shows that we are generating this */    
     IEReq->io_Command = IND_WRITEEVENT;
     IEReq->io_Data = (APTR)FakeEvent;
     IEReq->io_Flags = 0;
     IEReq->io_Length = sizeof(struct InputEvent);
     IBaseLock = LockIBase(0);
     XPos = SM->CurrX - (SM->Scr)->MouseX;
     YPos = SM->CurrY - (SM->Scr)->MouseY;
     XPos = (SM->XRatio >= 0) ? XPos * SM->XRatio : XPos / -(SM->XRatio);
     YPos = (SM->YRatio >= 0) ? YPos * SM->YRatio : YPos / -(SM->YRatio);
     FakeEvent->ie_X = XPos;
     FakeEvent->ie_Y = YPos;
     UnlockIBase(IBaseLock);
     DoIO((struct IORequest *)IEReq);  

     if(UpDown == DOWN_UP)  
     {
      Delay(10);
      FakeEvent->ie_Class = IECLASS_RAWMOUSE;
      FakeEvent->ie_NextEvent = NULL;
      FakeEvent->ie_Code = (IECODE_LBUTTON + MouseButton) | IECODE_UP_PREFIX;
      FakeEvent->ie_Qualifier = Qualifier;
      FakeEvent->ie_SubClass = 0xFF; /* shows that we are generating this */
      IEReq->io_Command = IND_WRITEEVENT;
      IEReq->io_Data = (APTR)FakeEvent;
      IEReq->io_Flags = 0;
      IEReq->io_Length = sizeof(struct InputEvent);
      IBaseLock = LockIBase(0);
      XPos = SM->CurrX - (SM->Scr)->MouseX;
      YPos = SM->CurrY - (SM->Scr)->MouseY;
      XPos = (SM->XRatio >= 0) ? XPos * SM->XRatio : XPos / -(SM->XRatio);
      YPos = (SM->YRatio >= 0) ? YPos * SM->YRatio : YPos / -(SM->YRatio);
      FakeEvent->ie_X = XPos;
      FakeEvent->ie_Y = YPos;
      UnlockIBase(IBaseLock);
      DoIO((struct IORequest *)IEReq); 
     }
     CloseDevice((struct IORequest *)IEReq);
    }
    else ErrCode |= NO_DEVICE; 
    DeleteExtIO((struct IORequest *)IEReq);
   }
   else ErrCode |= NO_EXTIO;
   FreeMem(FakeEvent,sizeof(struct InputEvent));
  }
  else ErrCode |= NO_MEM;
  DeletePort(IEPort);
 }
 else ErrCode |= NO_PORT;

 return(ErrCode);
}
