/**************************************************************************
 *  File name  :  thrd.c
 *
 *  Description:  This module contains the code for a background processing
 *                thread as well as the code for any routines used in that
 *                thread.
 *
 *                This thread uses an object window to facilitate
 *                communication with the main thread.  The main thread can
 *                communicate with the background thread through messages.
 *                You define your own window messages in main.h, post or send
 *                them to the background thread through the routines provided
 *                in this file, and then process the messages in the
 *                ObjectWndProc() routine.  The object window then sends
 *                other messages back to the main window to notify the main
 *                window as to the status of the task or with the final
 *                result.
 *
 *                An object window is used as the main window of the thread
 *                for several reasons.  The window is hidden, so it is
 *                never displayed on the screen.  Since it is hidden, the
 *                window does not have to process most of the window
 *                messages that are processed by visible windows, e.g.
 *                WM_PAINT, WM_SIZE, WM_SETFOCUS, etc.  This window will
 *                never receive the focus so it need not process input
 *                messages.  You can, however, send all mouse and keyboard
 *                messages to this window and have it process all input if
 *                you wish.  Since the window does not belong to any focus
 *                or activation chains, you can block this thread on a
 *                semaphore without having the system lock up or use the
 *                window manager semaphore routines such as
 *                WinMsgSemWait().
 *
 *                The BackgroundThread() routine is designed to create only
 *                one thread.  Should you try to create another one, the
 *                call will fail.  The thread uses several variables
 *                which are defined globally within this module.  In order
 *                to enable this routine to create multiple threads, you
 *                will need to make these variables local.  You will also
 *                need to differentiate between the different object
 *                windows created so that you can specify which thread will
 *                receive a posted message.
 *
 *                In order to enable the use of the second thread, define
 *                the BACKGROUND_THREAD constant found in main.h.
 *
 *                This source file contains the following functions:
 *
 *                BackgroundThread()
 *                CreateBackgroundThread()
 *                DestroyBackgroundThread()
 *                PostBkThreadMsg(msg, mp1, mp2)
 *                SendBkThreadMsg(msg, mp1, mp2)
 *                ObjectWndProc(hwnd, msg, mp1, mp2)
 *
 *  Concepts   :  background thread
 *
 *  API's      :  WinInitialize
 *                WinPostMsg
 *                DosExit
 *                WinCreateMsgQueue
 *                WinTerminate
 *                WinLoadString
 *                WinRegisterClass
 *                WinCreateWindow
 *                DosPostEventSem
 *                WinGetMsg
 *                WinDispatchMsg
 *                WinIsWindow
 *                WinDestroyWindow
 *                WinDestroyMsgQueue
 *                DosCreateEventSem
 *                DosCreateThread
 *                WinWaitEventSem
 *                DosWaitThread
 *                WinDefWindowProc
 *
 *  Required
 *    Files    :  OS2.H, MAIN.H, XTRN.H
 *
 *  Copyright (C) 1991 IBM Corporation
 *
 *      DISCLAIMER OF WARRANTIES.  The following [enclosed] code is
 *      sample code created by IBM Corporation. This sample code is not
 *      part of any standard or IBM product and is provided to you solely
 *      for  the purpose of assisting you in the development of your
 *      applications.  The code is provided "AS IS", without
 *      warranty of any kind.  IBM shall not be liable for any damages
 *      arising out of your use of the sample code, even if they have been
 *      advised of the possibility of such damages.                                                    *
 *************************************************************************/
/*
 *  Include files, macros, defined constants, and externs
 */

#define  INCL_DOSPROCESS
#define  INCL_DOSSEMAPHORES
#define  INCL_WINMESSAGEMGR
#include <os2.h>
#include "main.h"
#include "xtrn.h"

#define THREADSTACKSIZE     8192L
#define SEM_TIMEOUT        10000L

/*
 *  Global variables
 */
static HAB habBkThread;
static HMQ hmqBkThread;
static TID tidBkThread;
static HWND hwndObject;
static CHAR szObjectClass[MAXNAMEL];
static BOOL fThreadCreated = FALSE;
static HEV hevThreadInit;   /* semaphore for thread initialization */

VOID BackgroundThread (ULONG);

MRESULT EXPENTRY ObjectWndProc(HWND, ULONG, MPARAM, MPARAM);

/**************************************************************************
 *
 *  Name       : BackgroundThread(ulThreadParam)
 *
 *  Description: Routine is a background thread used for tasks to be
 *               completed in the background.
 *
 *  Concepts:    Routine is called at initialization time to create
 *               a background processing thread.
 *
 *               The routine initializes itself as a PM thread and creates
 *               a message queue.  It then creates an object window
 *               through which it will receive and send messages.
 *               It then polls through a message loop, processing any
 *               messages it receives in the object window's window
 *               procedure.  When the loop ends, it terminates.
 *
 *  API's      :  WinInitialize
 *                WinPostMsg
 *                DosExit
 *                WinCreateMsgQueue
 *                WinLoadString
 *                WinRegisterClass
 *                WinCreateWindow
 *                DosPostEventSem
 *                WinGetMsg
 *                WinDispatchMsg
 *                WinIsWindow
 *                WinDestroyWindow
 *                WinDestroyMsgQueue
 *
 *  Parameters :  ulThreadParam = thread parameter
 *
 *  Return     :  [none]
 *
 *************************************************************************/
VOID BackgroundThread(ULONG ulThreadParam)
{
   QMSG qmsg;

   /* create message queue for thread */
   habBkThread = WinInitialize(0);
   if(habBkThread == NULLHANDLE)
   {
      WinPostMsg(hwndMain, TM_THREADINITFAILED, (MPARAM)NULL, (MPARAM)NULL);
      DosExit(EXIT_THREAD, 0L);
   }

   hmqBkThread = WinCreateMsgQueue(habBkThread, 0);
   if(hmqBkThread == NULLHANDLE)
   {
      WinPostMsg(hwndMain, TM_THREADINITFAILED, (MPARAM)NULL, (MPARAM)NULL);

      WinTerminate(habBkThread);
      DosExit(EXIT_THREAD, 0L);
   }

   /* load the string for the object window class and register the class */
   if(WinLoadString(habBkThread,
                    (HMODULE)NULLHANDLE,
                    IDS_OBJECTCLASS,
                    MAXNAMEL,
                    (PSZ)szObjectClass)
      &&
              /* register the main client window class */
      WinRegisterClass(habBkThread,
                       (PSZ)szObjectClass,
                       (PFNWP)ObjectWndProc,
                       0L,
                       0L)
      &&
            /* create the object window */
      (NULLHANDLE != (hwndObject = WinCreateWindow(HWND_OBJECT,
                                    (PSZ)szObjectClass,
                                    (PSZ)NULL,
                                    0L,
                                    0L,
                                    0L,
                                    0L,
                                    0L,
                                    (HWND)NULLHANDLE,
                                    HWND_TOP,
                                    OBJECTID,
                                    (PVOID)NULL,
                                    (PVOID)NULL))))
   {
      /* set thread created flag so another thread of this type
       * cannot be created.
       */
      fThreadCreated = TRUE;

      /* clear initialization semaphore */
      DosPostEventSem(hevThreadInit);

      /* message loop */
      while(WinGetMsg(habBkThread, &qmsg, (HWND)NULLHANDLE, 0L, 0L))
         WinDispatchMsg(habBkThread, &qmsg);
   }
   else
   {
      WinPostMsg(hwndMain, TM_THREADINITFAILED, NULL, NULL);
   }

   /* destroy object window, clean up message queue and terminate */
   if(WinIsWindow(habBkThread, hwndObject))
      WinDestroyWindow(hwndObject);

   WinDestroyMsgQueue(hmqBkThread);
   WinTerminate(habBkThread);

   /* If termination is due to an error initializing the thread, then
       clear the initialization semaphore so that the main thread
       can continue. */
   if(!fThreadCreated)
      DosPostEventSem(hevThreadInit);

   DosExit(EXIT_THREAD, 0L);

   /* the thread parameter is not currently used */
   ulThreadParam;
}   /* End of BackgroundThread */


/**************************************************************************
 *
 *  Name       : CreateBackgroundThread()
 *
 *  Description: Creates the background thread
 *
 *  Concepts:    Routine is called at initialization time to create
 *               a background processing thread.
 *
 *               The routine calls DosCreateThread with the Background
 *               thread routine.
 *
 *  API's      :  DosCreateEventSem
 *                DosCreateThread
 *                WinWaitEventSem
 *
 *  Parameters :  [none]
 *
 *  Return     :  TRUE = thread is created successfully
 *                FALSE = not, or if the thread was already created
 *
 *************************************************************************/
BOOL CreateBackgroundThread(VOID)
{
   ULONG sRet;

   if(!fThreadCreated)
   {
      DosCreateEventSem((PSZ)NULL, &hevThreadInit, 0L, FALSE);
      sRet = (ULONG)DosCreateThread(&tidBkThread,
                                    (PFNTHREAD)BackgroundThread,
                                    0L,
                                    0L,
                                    THREADSTACKSIZE);
   }
   else
      return FALSE;

   /* wait until the thread has finished initialization */
   if(WinWaitEventSem(hevThreadInit, SEM_TIMEOUT))
      return FALSE;

   return((BOOL)(sRet == 0L));
}   /* End of CreateBackgroundThread   */

/**************************************************************************
 *
 *  Name       : DestroyBackgroundThread()
 *
 *  Description: Destroys the background thread
 *
 *  Concepts:    Routine is called at exit time to destroy the
 *               background processing thread.
 *
 *               The routine posts a WM_CLOSE message to the object window
 *               to end its message loop.  It then waits to make sure that
 *               the thread has been terminated before it returns.
 *
 *  API's      :  DosWaitThread
 *
 *  Parameters :  [none]
 *
 *  Return     :  [none]
 *
 *************************************************************************/
VOID DestroyBackgroundThread(VOID)
{
   PostBkThreadMsg(WM_CLOSE, (MPARAM)NULL, (MPARAM)NULL);
   DosWaitThread(&tidBkThread, 0L);
}   /* End of DestroyBackgroundThread   */


/**************************************************************************
 *
 *  Name       : PostBkThreadMsg(msg, mp1, mp2)
 *
 *  Description: Routine posts a message to the object window of the
 *               background thread.
 *
 *  Concepts:    Routine is called whenever a message is to be posted to
 *               the background processing thread.
 *
 *               The routine posts the message to the object window of the
 *               thread.
 *
 *  API's      :  WinPostMsg
 *
 *  Parameters :  msg = the message i.d.
 *                mp1 = first message parameter
 *                mp2 = second message parameter
 *
 *  Return     :  the return value from WinPostMsg().
 *
 *************************************************************************/
BOOL PostBkThreadMsg(ULONG msg, MPARAM mp1, MPARAM mp2)
{
   return(WinPostMsg(hwndObject, msg, mp1, mp2));
}   /* End of PostBkThreadMsg   */


/**************************************************************************
 *
 *  Name       : SendBkThreadMsg(msg, mp1, mp2)
 *
 *  Description: Routine sends a message to the object window of the
 *               background thread.
 *
 *  Concepts:    Routine is called whenever a message is to be sent to
 *               the background processing thread.
 *
 *               The routine sends the message to the object window of the
 *               thread.
 *
 *  API's      :  WinSendMsg
 *
 *  Parameters :  msg = the message i.d.
 *                mp1 = first message parameter
 *                mp2 = second message parameter
 *
 *  Return     :  the return value from WinSendMsg().
 *
 *************************************************************************/
MRESULT SendBkThreadMsg(ULONG msg, MPARAM mp1, MPARAM mp2)
{
   return(WinSendMsg(hwndObject, msg, mp1, mp2));
}   /* End of SendBkThreadMsg   */



/**************************************************************************
 *
 *  Name       : ObjectWndProc(hwnd, msg, mp1, mp2)
 *
 *  Description: Processes all messages sent to the Object window
 *
 *  Concepts:    Called for each message sent to the Object window.
 *
 *               The Object window processes the messages that tell
 *               the background thread what action to take.  Since
 *               the object window is not visible, it will not
 *               process any of the standard window messages.
 *
 *  API's      :  WinDefWindowProc
 *
 *  Parameters :  hwnd= window handle
 *                msg = the message i.d.
 *                mp1 = first message parameter
 *                mp2 = second message parameter
 *
 *  Return     :  dependent on message sent
 *
 *************************************************************************/
MRESULT EXPENTRY ObjectWndProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
{
   switch(msg)
   {
       /*
        *  Include any user-defined messages to determine which
        *  action the background thread should take.
        */

   default:
      return (WinDefWindowProc(hwnd, msg, mp1, mp2));
      break;
   }
   return (MRESULT)NULL;
}   /* End of ObjectWndProc   */
/***************************  End of thrd.c  ****************************/
