/****************************************************************************
**
**  Name:  pvtask.c
**
**  Description:
**     Background tasking kernel
**
**  $Log:   S:/tbird/mt2_186/sds/pvtask.c_v  $
** 
**    Rev 1.0   16 Dec 1996 13:46:04   Judy
** Initial revision.
** 
**    Rev 1.0   27 Aug 1996 15:12:10   Judy
** Initial revision.
** 
**    Rev 1.0   27 Aug 1996 15:06:56   Judy
** Initial revision.
** 
**    Rev 1.0   14 Jun 1996 13:51:00   Judy
** Initial revision.
** 
**    Rev 1.6   14 Oct 1993 12:16:04   nghia
** Fixed bug in CheckAbort().
** Flush all key messages of the current window when acknowledge there is an
** ESC key press.  This will fix the latency problem of ESC key effects across
** PV windows.
** 
**    Rev 1.5   14 Oct 1993 09:23:38   marilyn
** Load BWCC.DLL from PVTASK.
** 
**    Rev 1.4   30 Sep 1993 17:40:28   marilyn
** Add the call to BWCCGetVersion() to LibMain to force this Borland DLL
** to be loaded for support of custom controls and the DEWCC.DLL
** 
**    Rev 1.3   16 Jul 1993 11:59:32   ernie
** Removed obsolete includes error.h and sdserror.h
** 
**    Rev 1.2   25 Jun 1993 16:04:14   paul
** Add support for <esc> keypress detection.  This is
** functions TskCheckAbort and TskSetActiveAppState
** 
**    Rev 1.1   09 Jun 1993 09:13:04   ernie
** Completed implementation
** 
**    Rev 1.0   28 May 1993 15:49:16   ernie
** Initial revision.
** 
**  $Header:   S:/tbird/mt2_186/sds/pvtask.c_v   1.0   16 Dec 1996 13:46:04   Judy  $
**
*****************************************************************************/

                       /****************************
                        *                          *
                        *       INCLUDE FILES      *
                        *                          *
                        ****************************/




/* Tbird includes */
#ifndef _BASEWIND_
#include "basewind.h"
#endif

#ifndef _PVTASK_
#include "pvtask.h"
#endif

#ifndef _HOSTERRS_
#include "hosterrs.h"
#endif

/* BORLANDC INCLUDES */
#ifndef __STDIO_H_
#include <stdio.h>
#endif

// BWCC.H requires windows.h for 3.1
// we can include it when we are compatible but for
// now just use the following extern from the same file.
//#ifndef __BWCC_H_
//#include <bwcc.h>
//#endif

// extern DWORD FAR EXPORT PASCAL BWCCGetVersion( void);

                       /****************************
                        *                          *
                        *     LOCAL DEFINITIONS    *
                        *                          *
                        ****************************/

typedef struct TASK_NODE {
   TASK task;
   U16 priority;
   LPSTR name;
   struct TASK_NODE *next;
} TASK_NODE;

#define MAX_TASKS 100

static TASK_NODE *taskHead;     /* Head of task queue */
static TASK_NODE *taskFree;     /* Head of list of free task nodes */

static TASK_NODE taskTable[MAX_TASKS];


// holds last value passed in from WM_ACTIVATEAPP message
static BOOLEAN appIsActive;



                       /****************************
                        *                          *
                        *    EXTERNAL VARIABLES    *
                        *                          *
                        ****************************/

                       /****************************
                        *                          *
                        *     LOCAL PROTOTYPES     *
                        *                          *
                        ****************************/

                       /****************************
                        *                          *
                        *      EXECUTABLE CODE     *
                        *                          *
                        ****************************/

#pragma argsused
int FAR PASCAL LibMain(HANDLE hInstance, WORD wDataSeg, WORD cbHeapSize,
      LPSTR lpszCmdLine) {
   U16 i;
   for (i=1; i<MAX_TASKS; i++) taskTable[i-1].next = &taskTable[i];
   taskTable[MAX_TASKS-1].next = NULL;
   taskHead = NULL;
   taskFree = taskTable;
   appIsActive = FALSE;
//   BWCCGetVersion();  // this forces the BWCC.DLL to be loaded if required
   return(TRUE);
}

/*****************************************************************************
**
**    TaskExecute
**
*****************************************************************************/
RETCODE EXPORT TaskExecute(VOID) {
   if (taskHead != NULL) {
      taskHead->task();
      return(GOOD);
   } else {
      return(ER_NO_TASKS);
   }
}

/*****************************************************************************
**
**    TaskRegister
**
*****************************************************************************/
RETCODE EXPORT TaskRegister(TASK task, U16 priority, LPSTR name) {
   TASK_NODE *taskNode, *taskComp;
   if (taskFree == NULL) return(ER_TASK_TABLE_FULL);
   taskNode = taskFree;       /* Remove task node from head of free list */
   taskFree = taskFree->next;
   taskNode->task = task;
   taskNode->priority = priority;
   taskNode->name = name;
   if ((taskHead == NULL) || (taskHead->priority <= priority)) {
      taskNode->next = taskHead;
      taskHead = taskNode;
   } else {
      for (taskComp=taskHead; taskComp!=NULL; taskComp=taskComp->next) {
         if (taskComp->next == NULL) {          /* Append to end of queue */
            taskNode->next = NULL;
            taskComp->next = taskNode;
            break;
         } else if (taskComp->next->priority <= priority) {   /* Insert */
            taskNode->next = taskComp->next;
            taskComp->next = taskNode;
            break;
         }
      }
   }
   return(GOOD);
}

/*****************************************************************************
**
**    TaskUnregister
**
*****************************************************************************/
RETCODE EXPORT TaskUnregister(TASK task) {
   TASK_NODE *taskNode, *taskComp;
   if (taskHead==NULL) return(ER_TASK_NOT_IN_TABLE);
   if (taskHead->task == task) {
      taskNode = taskHead;
      taskHead = taskHead->next;   /* remove node from task queue */
   } else for (taskComp=taskHead; taskComp; taskComp=taskComp->next) {
      if (taskComp->next == NULL) return(ER_TASK_NOT_IN_TABLE);
      if (taskComp->next->task == task) {
         taskNode = taskComp->next;
         taskComp->next = taskNode->next;   /* unlink from task queue */
         break;
      }
   }
   taskNode->next = taskFree;
   taskFree = taskNode;              /* Add node to head of free list */
   return(GOOD);
}

/*****************************************************************************
**
**    TaskWalk
**
*****************************************************************************/
RETCODE EXPORT TaskWalk(VOID **state, LPSTR text) {
   TASK_NODE *taskNode = (TASK_NODE *)*state;
   if (*state==NULL) taskNode = taskHead;
   else taskNode = taskNode->next;
   if (taskNode != NULL) {
      sprintf(text,"%5d: %s", taskNode->priority, taskNode->name);
   }
   *state = taskNode;
   return(GOOD);
}




/*********************************************************
 *
 *   TskCheckAbort(BOOLEAN *aborted)
 *
 * Acts as replacement for Windows API function GetAsyncKeyState(VK_ESCAPE)
 * However, we hide its implementation by returning TRUE of FALSE
 * This new version only tests when the app is active, as
 * defined by the "appIsActive" flag.  This is a crude
 * indication of focus.
 *
 * Note that it is NOT necessary to check for CONTROL-ESC here because
 * if CONTROL-ESC has been pressed we are no longer the active
 * app...the TASK LIST app is active.
 * (The CONTROL-ESC sequence brings up the TASK LIST application.
 * Before TASK LIST is active, our app has been sent WM_ACTIVATEAPP
 * with wParam==0).
 */
RETCODE EXPORT TskCheckAbort(BOOLEAN FAR *abort) {
   HWND currentWindow;
   MSG  dummyMsg;
   
   *abort = FALSE;    
   if (appIsActive == TRUE) {
      // Return the state of ESC key at interrupt level - TRUE if pressed.  
      if (GetAsyncKeyState(VK_ESCAPE) != 0) {
         *abort = TRUE;
         // Flush all keyboard messages for the current window
         if ((currentWindow = GetFocus()) != NULL) 
            // NOTES: if currentWindow is NULL, we will flush all key messages
            // of all windows in the current application.
            // Return when there is no more key messages for currentWindow. 
            while(PeekMessage(&dummyMsg, currentWindow,
                        WM_KEYFIRST, WM_KEYLAST, PM_REMOVE));
      }
   }
   return GOOD;
}



/********************************************************
 *
 *  TskSetActiveAppState(WORD wParam)
 *
 *
 * Passes value of wParam of the WM_ACTIVATEAPP message
 * to local static variable.
 *
 * The following is extracted from Windows Help for WM_ACTIVATEAPP:
 *  The WM_ACTIVATEAPP message is sent when a window
 *  is about to be activated and that window belongs to a
 *  different task than the active window.
 *  The message is sent to all top-level windows of the task
 *  being activated and to all top-level windows of the
 *  task being deactivated.
 *
 *   wParam   A nonzero value means the window is being activated.
 *            A zero value means the window is being deactivated. 
 *   (we ignore lparam)
 *
 * We call GetAsyncKeyState to reset the flag for esc key held in
 * the key state array because it may have been set while the
 * other app was active.
 *
 */
RETCODE EXPORT TskSetActiveAppState(WORD wParam)
{

   if(wParam==0)
      appIsActive = FALSE;
   else
      appIsActive = TRUE;

   GetAsyncKeyState(VK_ESCAPE);   // reset state
   return GOOD;
}




