/*
 * FocusWindow      (C) 1992 Software Sculptors
 *                         All Rights Under Copyright Reserved
 *
 * Version 4.0
 *
 * Written using SAS/C 6.2
 *
 * PROGRAM AND SOURCE CODE ARE FREELY (RE)DISTRIBUTABLE.
 *
 * By John M. Haubrich, Jr.
 */
#include <exec/types.h>
#include <exec/libraries.h>
#include <libraries/commodities.h>
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#include <dos/dos.h>
#include <dos/rdargs.h>
#include <clib/dos_protos.h>
#include <clib/exec_protos.h>
#include <clib/alib_protos.h>
#include <clib/commodities_protos.h>
#include <clib/intuition_protos.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

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

/*
 * VERSION STRING ===================================
 */
char *vers="$VER: FocusWindow 1.0 (10.06.93)";


/*
 * STRUCTURE DEFINITIONS ----------------------------
 */
struct NewBroker newbroker = {
   NB_VERSION,
   "FocusWindow",
   "Cycles active window and brings",
   "window into view on virtual screen.",
   NBU_UNIQUE | NBU_NOTIFY,
   0, 0, 0, 0
};

struct MyGlobals {
   struct MsgPort *broker_mp;
   CxObj          *broker;
   ULONG           cxsigflag;  // our combined message port signal FLAG

   BOOL            fRunFromWB;
   BOOL            fBeQuiet;
   BOOL            fFocusBackdrops;
};


/*
 * GLOBAL VARIABLES ---------------------------------
 */
struct MyGlobals  MG;


/*
 * FUNCTION PROTOTYPES ------------------------------
 */
void main( int, char ** );
void ProcessMsg(void);
int  JMH_EasyReq( char *, char *, char *);
void JMH_Complain(char *);
void JMH_Info(char *);
int  JMH_Query(char *, char *);
void JMH_Quit(char *);
VOID JMH_InitVars(void);


/*
 * DEFINES ------------------------------------------
 */
#define NUM_ARGS              3
#define ARG_NEXTWINDOW        0
#define ARG_FOCUSBACKDROPS    1
#define ARG_QUIET             2

#define EVT_HOTKEY      1L
#define BASENAME        "FocusWindow"


/*
 * LIBRARY VARIABLES ---------------------------------
 */
struct Library       *CxBase        = NULL;
struct IntuitionBase *IntuitionBase = NULL;
struct IconBase      *IconBase      = NULL;


/*
 * FUNCTIONS ----------------------------------------
 */

/*
 * JMH_EasyReq(): pop up JMH_EasyRequest() with a custom title bar, message and
 *            response buttons.
 -------------------------------------------------------------------------*/
int JMH_EasyReq( char *title, char *text, char *resp ) {
   static struct EasyStruct es = {
      sizeof(struct EasyStruct),
      NULL,
      NULL,
      NULL,
      NULL
   };
   int stat;


   if ( MG.fBeQuiet == FALSE )
   {
      if ( MG.fRunFromWB == TRUE )
      {
         es.es_Title = title;
         es.es_TextFormat = text;
         es.es_GadgetFormat = resp;

         stat = EasyRequest(NULL, &es, NULL, TAG_DONE);
      }
      else
      {
         printf( "%s\n", text );
         stat = -1;
      }
   }
   return( stat );
}

/*
 * JMH_Complain(): pop up JMH_EasyRequest() with a JMH_Complaint message
 -------------------------------------------------------------------------*/
void JMH_Complain(char *t)
{
   char buf[100];


   if (t) {
      sprintf( buf, "%s Complaint...", BASENAME );
      JMH_EasyReq( buf, t, "OK" );
   }
}

/*
 * JMH_Info(): pop up JMH_EasyRequest() with a (nice) informative message
 *             if WB flag set else send output to stderr
 -------------------------------------------------------------------------*/
void JMH_Info(char *t)
{
   char buf[100];


   if (t) {
      sprintf( buf, "%s Information...", BASENAME );
      JMH_EasyReq( buf, t, "OK" );
   }
}

/*
 * JMH_Query(): are you sure... ? (1=yes, 0=no)
 -------------------------------------------------------------------------*/
int JMH_Query(char *t, char *response) {
   char  buf[100];
   char *resp;
   int   stat;


   if (t) {
      sprintf( buf, "%s Request...", BASENAME );

      if (response)
         resp = response;
      else
         resp = "Yes|No";
      stat = JMH_EasyReq( buf, t, resp );
      return(stat);
   }
}

/*
 * JMH_Quit(): close down everything we've opened and JMH_Quit
 -------------------------------------------------------------------------*/
void JMH_Quit( char *t )
{
   char  szQuitBuf[256+20];


   if ( t )
   {
      sprintf( szQuitBuf, "%s\nExiting...", t );
      JMH_Complain( szQuitBuf );
   }

   if (MG.broker)       DeleteCxObjAll(MG.broker);
   if (MG.broker_mp)    DeletePort(MG.broker_mp);

   if (CxBase)          CloseLibrary(CxBase);
   if (IconBase)        CloseLibrary((struct Library *)IconBase);
   if (IntuitionBase)   CloseLibrary((struct Library *)IntuitionBase);

   exit( ( t ? TRUE : FALSE ) );
}


/*
 * JMH_InitVars(): initialize variables to default values
 -------------------------------------------------------------------------*/
VOID JMH_InitVars()
{
   //
   // initialize variables...
   // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   MG.broker            = NULL;
   MG.broker_mp         = NULL;
   MG.fRunFromWB        = TRUE;
   MG.fBeQuiet          = FALSE;
   MG.fFocusBackdrops   = FALSE;
}


/**
 ** M A I N   P R O G R A M   -----------------------
 **/
void main( int argc, char *argv[] )
{
   UBYTE            **ttypes,
                     *pszHotkey,
                     *temp,
                      szHotkey[256];
   CxMsg             *msg;
   CxObj             *filter;
   LONG               errorcode;
   LONG               rda_Arguments[NUM_ARGS];
   USHORT             us;
   char               errbuf[256];
   struct RDArgs     *rda_Args;


   for ( us=0; us < NUM_ARGS; us++ )   rda_Arguments[us] = 0;

   //
   // open libraries
   // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   IntuitionBase = (struct IntuitionBase *)
      OpenLibrary("intuition.library",39L);
   if (!IntuitionBase) JMH_Quit("Cannot open intuition.library 39+");

   IconBase = (struct IconBase *)OpenLibrary("icon.library",37L);
   if (!IconBase) JMH_Quit("Cannot open icon.library 37+");

   CxBase = OpenLibrary("commodities.library",37L);
   if (!CxBase)   JMH_Quit("Cannot open commodities.library 37+");

   //
   // initialize variables
   // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   JMH_InitVars();

   if (!(MG.broker_mp = CreateMsgPort()))
      JMH_Quit( "Cannot create broker message port." );

   //
   // run from Workbench or CLI/Shell?
   // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   if ( argc > 0 )      // run from shell
   {
      MG.fRunFromWB = FALSE;

      // set defaults
      strcpy( szHotkey, "lcommand b" );
      MG.fBeQuiet          = FALSE;
      MG.fFocusBackdrops   = FALSE;

      // get opts from command line
      if (rda_Args = ReadArgs( "NEXTWINDOW/K,FOCUSBACKDROPS/S,QUIET/S", rda_Arguments, NULL ))
      {
         if ( rda_Arguments[ARG_NEXTWINDOW] )
         {
            strcpy( szHotkey, (char *)rda_Arguments[ARG_NEXTWINDOW] );
         }
         if ( rda_Arguments[ARG_FOCUSBACKDROPS] )
         {
            MG.fFocusBackdrops = TRUE;
         }
         else
         {
            MG.fFocusBackdrops = FALSE;
         }
         if ( rda_Arguments[ARG_QUIET] )
         {
            MG.fBeQuiet = TRUE;
         }
         FreeArgs( rda_Args );
      }
      else
      {
         Fault(IoErr(), "FocusWindow failed because ", errbuf, sizeof( errbuf ));
         JMH_Complain( errbuf );
      }
   }
   else                 // run from Workbench
   {
      MG.fRunFromWB = TRUE;

      // get opts from TOOLTYPES in icon
      ttypes = ArgArrayInit( argc, argv );

      // user-specified priority for the commodity
      newbroker.nb_Pri = (BYTE)ArgInt( ttypes, "CX_PRIORITY", 0 );

      // key to cycle windows
      pszHotkey = ArgString( ttypes, "NEXTWINDOW", "lcommand b" );
      strcpy( szHotkey, pszHotkey );

      // skip over backdrop windows in focus chain?
      temp = ArgString( ttypes, "FOCUSBACKDROPS", "FALSE" );
      if ( strcmpi( temp, "TRUE" ) == 0 )
      {
         MG.fFocusBackdrops = TRUE;
      }
      else
      {
         MG.fFocusBackdrops= FALSE;
      }

      // report errors?
      temp = ArgString( ttypes, "QUIET", "FALSE" );
      if ( strcmpi( temp, "TRUE" ) == 0 )
      {
         MG.fBeQuiet = TRUE;
      }
      else
      {
         MG.fBeQuiet = FALSE;
      }

      ArgArrayDone();
   }

   newbroker.nb_Port = MG.broker_mp;
   MG.cxsigflag = 1L << MG.broker_mp->mp_SigBit;

   if ( MG.broker = CxBroker(&newbroker, &errorcode) )
   {
      //
      // NEXTWINDOW filter
      //
      if (filter = HotKey( szHotkey, MG.broker_mp, EVT_HOTKEY ))
      {
         AttachCxObj( MG.broker, filter );
         if ( !CxObjError( filter ) )
         {
            ActivateCxObj( MG.broker, TRUE );
            ProcessMsg();
         }
         else
         {
            JMH_Quit( "CX Object creation error.\nCan't create input filter." );
         }
      }
      else
      {
         JMH_Quit( "Invalid NEXTWINDOW key specified." );
      }
   }
   else
   {
      //
      // an error has occured.. show user!
      //
      switch ( errorcode )
      {
         case CBERR_SYSERR:
            JMH_Quit( "Broker SYSTEM error.\nNo memory?" );
            break;
         case CBERR_DUP:
            //
            // this message is displayed in ProcessMsg(): 'already running...'
            //
            break;
         case CBERR_VERSION:
            JMH_Quit( "Broker VERSION error." );
            break;
      }
   }

   //
   // Empty message port of all remaining messages
   //
   while ( msg = (CxMsg *)GetMsg( MG.broker_mp ) )
      ReplyMsg( (struct Message *)msg );

   JMH_Quit( NULL );
}

void ProcessMsg()
{
   CxMsg             *msg;
   ULONG              sigrcvd,
                      msgid,
                      msgtype;
   BOOL               fKeepGoing = TRUE;
   struct Window     *thiswdw, *nextwdw;


   while ( fKeepGoing == TRUE )
   {
      sigrcvd = Wait( SIGBREAKF_CTRL_C | MG.cxsigflag );

      while ( msg = (CxMsg *)GetMsg( MG.broker_mp ) )
      {
         msgid = CxMsgID( msg );
         msgtype = CxMsgType( msg );
         ReplyMsg( (struct Message *)msg );

         switch ( msgtype )
         {
            case CXM_IEVENT:
               switch ( msgid )
               {
                  case EVT_HOTKEY:
                     //
                     // find active window then get next window;
                     // activate, bring to front and bring into view
                     // (useful on virtual screen) *IF* nextwindow and
                     // the current window aren't the same!
                     //
                     if ( MG.fFocusBackdrops == TRUE )
                     {
                        thiswdw = IntuitionBase->ActiveWindow;
                        nextwdw = thiswdw->NextWindow;
                        if ( nextwdw == NULL )
                        {
                           // we reached the end of the list; take the first
                           // window!
                           nextwdw = thiswdw->WScreen->FirstWindow;
                        }
                     }
                     else
                     {
                        thiswdw = IntuitionBase->ActiveWindow;

                        // keep searching for the next non-BACKDROP window
                        // until we've searched all windows.

                        for ( nextwdw = thiswdw->NextWindow;
                              ( ( nextwdw != thiswdw ) &&
                                ( nextwdw->Flags & WFLG_BACKDROP ) &&
                                ( nextwdw != NULL )
                              );
                            )
                        {
                           nextwdw = nextwdw->NextWindow;

                           // wrap-around to first screen at end of list...
                           if ( nextwdw == NULL )
                           {
                              nextwdw = thiswdw->WScreen->FirstWindow;
                           }
                        }
                     }
                     if ( ( nextwdw != thiswdw ) && ( nextwdw != NULL ) )
                     {
                        ActivateWindow( nextwdw );
                        WindowToFront( nextwdw );
         		         ScreenPosition( nextwdw->WScreen,
         		            SPOS_MAKEVISIBLE | SPOS_FORCEDRAG,
	         		         nextwdw->LeftEdge,
	         		         nextwdw->TopEdge,
	         		         nextwdw->LeftEdge + nextwdw->Width,
   	      		         nextwdw->TopEdge  + nextwdw->Height
   	      		      );
                     }
                     break;
               }
               break;

            case CXM_COMMAND:
               switch ( msgid )
               {
                  case CXCMD_DISABLE:
                     ActivateCxObj( MG.broker, FALSE );
                     break;

                  case CXCMD_ENABLE:
                     ActivateCxObj( MG.broker, TRUE );
                     break;

                  case CXCMD_KILL:
                     fKeepGoing = FALSE;
                     break;

                  case CXCMD_UNIQUE:
                     JMH_Complain( "FocusWindow is already running!" );
                     break;
               }
               break;
         }
         if ( fKeepGoing == FALSE ) break;
      }
      if ( sigrcvd & SIGBREAKF_CTRL_C )
      {
         fKeepGoing = FALSE;
      }
   }
}
