home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 8 Other / 08-Other.zip / pmp016.zip / pmpause.c < prev    next >
C/C++ Source or Header  |  1998-01-13  |  28KB  |  799 lines

  1. /* PMPAUSE -- warns of long periods of typing/mouse use, and hints that
  2.  *            a pause might be desireable.
  3.  *
  4.  * Written by Ewen McNeill <ewen@naos.co.nz>, Dec 1997.  
  5.  * Ideas based on Xpause and Xpause2 by Julian Anderson, and Paul Thomas.
  6.  * Copyright Ewen McNeill, 1997-1998.  
  7.  *
  8.  * This program is free software; you can redistribute it and/or modify
  9.  * it under the terms of the GNU General Public License as published by
  10.  * the Free Software Foundation; either version 2 of the License, or
  11.  * (at your option) any later version.
  12.  *
  13.  * This program is distributed in the hope that it will be useful,
  14.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16.  * GNU General Public License for more details.
  17.  *
  18.  * You should have received a copy of the GNU General Public License
  19.  * along with this program; if not, write to the Free Software
  20.  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  21.  *
  22.  *---------------------------------------------------------------------------
  23.  *
  24.  * It's often been said that you have to build things twice: once to build
  25.  * it, and once to build it right.  This is the first attempt.  This version
  26.  * is fairly limited, and its design is perhaps sub optimal but it does work
  27.  * and provides a basis for further development/re-design.
  28.  *
  29.  * NOTE: OS/2 PM programs do have valid stdout/stderr streams which can
  30.  * be used, but by default the output is not displayed.  It is possible
  31.  * to display this output by piping it to another program (eg, less, more,
  32.  * cat).  For this reason if the usage information (eg error in options),
  33.  * then a beep is made before outputing the text.  Ideally a window should
  34.  * pop up with the information, but that'll have to wait for a later version :-)
  35.  *
  36.  *---------------------------------------------------------------------------
  37.  * $Id: pmpause.c 0.16 1998/01/13 02:41:32 ewen Exp $
  38.  */
  39.  
  40. /* OS/2 Header portions required */
  41. #define INCL_WIN
  42. #define INCL_GPI
  43. #define INCL_DOSPROCESS
  44. #define INCL_DOSMODULEMGR
  45. #include <os2.h>
  46. #include <stdio.h>
  47. #include <stdlib.h>
  48. #include <ctype.h>
  49.  
  50. #include "pmpause.h"
  51.  
  52. /* Prototypes */
  53. MRESULT EXPENTRY window_handler(HWND handle, ULONG msg, MPARAM p1, MPARAM p2);
  54. LONG decide_colour(int level);
  55. void usage(void);
  56. VOID APIENTRY ExeTrap(void);
  57.  
  58. /* Name of window class */
  59. static unsigned char winClass[] = "pause";
  60.  
  61. /* Exit codes */
  62. #define EXIT_OK          ( 0)
  63. #define EXIT_INIT_ERROR  (90)
  64. #define EXIT_USAGE       (99)
  65.  
  66. /* Colours, and thresholds for their use */
  67. #define COLOUR_NORMAL    (CLR_GREEN)
  68. #define COLOUR_NOTIFY    (CLR_YELLOW)
  69. #define COLOUR_WARN      (CLR_RED)
  70. #define COLOUR_BKGND     (CLR_BACKGROUND)
  71.  
  72. /* Message numbers to use internally     */
  73. #define MSG_GO_UP        (WM_USER + 1)
  74. #define MSG_GO_DOWN      (WM_USER + 2)
  75.  
  76. /* Runtime configurable information: global, static, to allow view from
  77.  * all functions (alternative for later: window words?)
  78.  */
  79.  
  80. /* Minimum and maximum levels   */
  81. static int min_level        =   0;
  82. static int max_level        = 180;
  83.  
  84. /* Transitition levels          */
  85. static int threshold_notify =  90;
  86. static int threshold_warn   = 150;
  87.  
  88. /* Step amounts                 */
  89. static int step_up          =   4;
  90. static int step_down        =  12;
  91.  
  92. /* Orientation -- horizontal or vertical (vertical if not horizontal) */
  93. static int horizontal       =FALSE;
  94.  
  95. /* How often to check if there has been activity recently */
  96. static int check_seconds    =   4;
  97.  
  98. /* Width and Height (can be overridden from command line) */
  99. static long width           =  10;
  100. static long height          = 234;
  101.  
  102. /* Position of window:
  103.  * Without -x:  positive relative to bottom, left,
  104.  *              negative relative to top, right
  105.  * With    -x:  positive relative to top, left,
  106.  *              negative relative to bottom, right
  107.  */
  108. static long xoffset         =   0;
  109. static int  xoffsetneg      =TRUE;
  110. static long yoffset         =   0;
  111. static int yoffsetneg       =FALSE;
  112.  
  113. static int reltopleft       =FALSE;
  114.  
  115. /* Beep when reaching top level (only on way up, and if stepped up)        */
  116. static int beepattop        =FALSE;
  117.  
  118. #define ARERT_BEEP_FREQ    (1440)      /* Hz */
  119. #define ALERT_BEEP_LEN     (  10)      /* ms */
  120.  
  121. /* Anchor block and message queue, global because several things need them */
  122. static HAB   ab       = NULLHANDLE;    /* Anchor block   */ 
  123. static HMQ   mq       = NULLHANDLE;    /* Message queue  */
  124.  
  125. /* Define to use the DLL routines to hook the message queue */
  126. #define USE_DLL
  127.  
  128. /*========================================================================*/
  129.  
  130. int main(int argc, char **argv)
  131. {
  132.   QMSG  msg;                      /* Window message */
  133.   ULONG winFlags = 0UL;           /* Window flags   */
  134.   HWND  frame    = NULLHANDLE;    /* Window frame   */
  135.   HWND  client   = NULLHANDLE;    /* Window client  */
  136.   HMODULE hmDll  = NULLHANDLE;    /* Handle to our own module (DLL) */
  137.   char  **args   = NULL;          /* To parse command line arguments */
  138.   int   show_usage = 0;           /* show usage & exit if set to 1 */
  139.   long  sx = 0L, sy = 0L;         /* Screen x, y maximums */
  140.  
  141.   /* Parse the command line arguments */
  142.   /* Ideally something like getopt() would be used here, but at present
  143.    * a hand coded approach is just slightly easier.
  144.    */
  145.   args = (argv + 1);
  146.   while(*args != NULL)
  147.   {
  148.     const char *arg = *args;
  149.   
  150.     if (*arg == '-')
  151.     {
  152.       while(*(++arg) != '\0')
  153.         switch(*arg)
  154.         {
  155.         case 'h':                   /* Horizontal meter */
  156.           horizontal = TRUE;
  157.           break;
  158.         case 'v':                   /* Vertical meter */
  159.       horizontal = FALSE;
  160.           break;
  161.         case 'x':                   /* Relative to top left, like X */
  162.           reltopleft = TRUE;
  163.           break;
  164.         case 'b':                   /* Beep on reaching top level */
  165.           beepattop = TRUE;
  166.           break;
  167.         case 'm':                   /* Maximum value */
  168.           if (*(args + 1) != NULL)
  169.             max_level = atoi(*(++args));
  170.           else
  171.             show_usage = 1;         /* Insufficient arguments */
  172.           break;
  173.         case 'n':                   /* Notify level */
  174.           if (*(args + 1) != NULL)
  175.             threshold_notify = atoi(*(++args));
  176.           else
  177.             show_usage = 1;         /* Insufficient arguments */
  178.           break;
  179.         case 'w':                   /* Warn level */
  180.           if (*(args + 1) != NULL)
  181.             threshold_warn = atoi(*(++args));
  182.           else
  183.             show_usage = 1;         /* Insufficient arguments */
  184.           break;
  185.         case 'u':                   /* Step up amount */
  186.           if (*(args + 1) != NULL)
  187.             step_up = atoi(*(++args));
  188.           else
  189.             show_usage = 1;         /* Insufficient arguments */
  190.           break;
  191.         case 'd':                   /* Step down amount */
  192.           if (*(args + 1) != NULL)
  193.             step_down = atoi(*(++args));
  194.           else
  195.             show_usage = 1;         /* Insufficient arguments */
  196.           break;
  197.         case 'c':                   /* Check frequency */
  198.           if (*(args + 1) != NULL)
  199.             check_seconds = atoi(*(++args));
  200.           else
  201.             show_usage = 1;         /* Insufficient arguments */
  202.           break;
  203.         case '?':
  204.           /* FALL THROUGH */
  205.         default:
  206.           show_usage = 1;           /* Unknown argument */
  207.         break;
  208.       }
  209.     } /* Started with "-" */
  210.     else if (isdigit(*arg))
  211.     { /* This looks like it might be a position string, so try to parse it   */
  212.       long w = 0L, h = 0L, x = 0L, y = 0L;      /* Width, Height, xoff, yoff */
  213.       char xp = ' ', yp = ' ';                  /* X off positive, y off pos */
  214.       int rc = 0;
  215.  
  216.       rc = sscanf(arg, "%ldx%ld%1c%ld%1c%ld", &w, &h, &xp, &x, &yp, &y);
  217.  
  218.       if (rc == 6)
  219.       { /* Scanned all the arguments that we wanted, use them                */
  220.         width      = w;             height     = h;
  221.         xoffset    = x;             yoffset    = y;
  222.         xoffsetneg = (xp == '-');   yoffsetneg = (yp == '-');
  223.  
  224.         fprintf(stderr, "Got: width = %ld, height = %ld, "
  225.                         "xoffset = %c%ld, yoffset = %c%ld\n",
  226.                 width, height, (xoffsetneg ? '-' : '+'), xoffset,
  227.                                (yoffsetneg ? '-' : '+'), yoffset);
  228.         fflush(stderr);
  229.       }
  230.       else if (rc == 2)
  231.       { /* Got two arguments, so just width and height                       */
  232.         width      = w;             height     = h;
  233.       }
  234.       else
  235.         show_usage = 1;
  236.     }
  237.     else /* Unexpected argument */
  238.       show_usage = 1;
  239.  
  240.     args++;
  241.   }
  242.  
  243.   if (show_usage ||               /* Invalid arguments/requested help */
  244.                                   /* Sanity checks: */
  245.       (! (min_level < max_level))                                           ||
  246.       (! (min_level < threshold_notify && threshold_notify < max_level))    ||
  247.       (! (threshold_notify < threshold_warn && threshold_warn < max_level)) ||
  248.       (! (step_up   <= (max_level - min_level)))                            ||
  249.       (! (step_down <= (max_level - min_level)))                            ||
  250.       (! ((width > 0) && (height > 0))))
  251.   {
  252.     usage();
  253.     exit(EXIT_USAGE);
  254.   }
  255.  
  256.   /* Figure out how big the screen is, so we can decide on positions      */
  257.   sx = WinQuerySysValue(HWND_DESKTOP, SV_CXSCREEN);
  258.   sy = WinQuerySysValue(HWND_DESKTOP, SV_CYSCREEN);
  259.   fprintf(stderr, "Window size is %ld x %ld\n", sx, sy); fflush(stderr);
  260.  
  261.   /* Position the window -- two positionings are applicable:
  262.    *   OS/2 style:  positive relative bottom, left; negative top, right
  263.    *   X style:     positive relative top, left; negative bottom, right
  264.    */
  265.   if (reltopleft)
  266.   { /* X style */
  267.     if (xoffsetneg)
  268.       xoffset = (sx - xoffset - width);
  269.  
  270.     /* Need to flip Y axis.  For negative values this means we need:
  271.      * - negative value specifies bottom edge, relative to bottom, which
  272.      *   is exactly the OS/2 semantics, so we do nothing
  273.      * - positive value specifies top edge, relative to top, which means
  274.      *   we need to flip the values over.
  275.      */
  276.     if (! yoffsetneg)
  277.       yoffset = (sy - yoffset - height);
  278.   }
  279.   else
  280.   { /* OS/2 style */
  281.     if (xoffsetneg)
  282.       xoffset = (sx - xoffset - width);
  283.  
  284.     if (yoffsetneg)
  285.       yoffset = (sy - yoffset - height);   
  286.   }
  287.  
  288.   xoffsetneg = FALSE; yoffsetneg = FALSE;  /* They're not negative any longer*/
  289.  
  290.   fprintf(stderr, "Window to be created at: %ld, %ld, of size %ld x %ld\n",
  291.           xoffset, yoffset, width, height);
  292.   fflush(stderr);
  293.   
  294.   /* Frame contents -- we want a little border, and to be on the tasklist,
  295.    *                   but we don't want any buttons or things. 
  296.    */
  297.   winFlags = FCF_BORDER | FCF_TASKLIST | FCF_NOBYTEALIGN | FCF_AUTOICON;
  298.  
  299.   /* Get anchor block, and message queue ---------------------------------*/
  300.   ab = WinInitialize(0);
  301.   if (ab == NULLHANDLE)
  302.     exit(EXIT_INIT_ERROR);
  303.  
  304.   mq = WinCreateMsgQueue(ab, 0);
  305.   if (mq == NULLHANDLE)
  306.   {
  307.     WinTerminate(ab);
  308.     exit(EXIT_INIT_ERROR);
  309.   }
  310.  
  311.   /* Trap exceptions so we can release our hook */
  312.   DosExitList(EXLST_ADD, (PFNEXITLIST)ExeTrap);
  313.  
  314.   /* Register window class -----------------------------------------------*/
  315.   if (!WinRegisterClass(ab, (PSZ)winClass, (PFNWP)window_handler, 
  316.             CS_SIZEREDRAW | CS_SYNCPAINT, 0))
  317.   {
  318.     WinDestroyMsgQueue(mq);
  319.     WinTerminate(ab);
  320.     exit(EXIT_INIT_ERROR);
  321.   }
  322.  
  323.   /* Create window -------------------------------------------------------*/
  324.  
  325.   /* Creates a window of our class, sizing/visibility done later */
  326.   frame = WinCreateStdWindow(HWND_DESKTOP, 0, &winFlags,
  327.                  (PSZ)winClass, (PSZ)"PM Pause", 0,
  328.                  0, 0, &client);
  329.   if (frame == NULLHANDLE)
  330.   {
  331.     WinDestroyMsgQueue(mq);
  332.     WinTerminate(ab);
  333.     exit(EXIT_INIT_ERROR);
  334.   }
  335.  
  336.   /* Now position the window, and make it visible */
  337. #ifdef DEBUG  
  338.   fprintf(stderr, "Calling WinSetWindowPos(..., %ld, %ld, %ld, %ld,....)\n",
  339.           xoffset, yoffset, width, height); fflush(stderr);
  340. #endif          
  341.   if (WinSetWindowPos(frame, HWND_TOP, xoffset, yoffset, width, height,
  342.                         (SWP_SIZE|SWP_MOVE|SWP_ZORDER|SWP_SHOW)))
  343.   {
  344. #ifdef DEBUG      
  345.     fprintf(stderr, "Set window position.\n");
  346.     fflush(stderr);
  347. #endif
  348.   }
  349.   else
  350.   {
  351. #ifdef DEBUG
  352.     fprintf(stderr, "Window position set failed (%lx).\n", WinGetLastError(ab));
  353.     fflush(stderr);
  354. #endif    
  355.   }
  356.  
  357. #ifdef USE_DLL  
  358.   /* Hook the input queue */
  359.   if (PMPauseInit(client))
  360.   {
  361.   #ifdef DEBUG
  362.     fprintf(stderr, "Hooked message queue.\n"); fflush(stderr);
  363.   #endif
  364.   }
  365. #endif  
  366.  
  367.   /* Message Loop --------------------------------------------------------*/
  368.   while(WinGetMsg(ab, &msg, 0, 0, 0))
  369.     WinDispatchMsg(ab, &msg);
  370.  
  371.   /* Cleanup -------------------------------------------------------------*/
  372.   /* NB: Window Class is deregistered when program exits.                 */
  373.  
  374. #ifdef USE_DLL  
  375.   PMPauseKill();
  376. #endif  
  377.   WinDestroyWindow(frame);
  378.   WinDestroyMsgQueue(mq);
  379.   WinTerminate(ab);
  380.   
  381. #ifdef USE_DLL
  382.   /* Last act -- we free the module */
  383.   if(! DosQueryModuleHandle(DLLNAME, &hmDll))
  384.     DosFreeModule(hmDll);            /* Free DLL, to force release of mem */
  385. #endif    
  386.  
  387.   return(EXIT_INIT_ERROR);
  388. }
  389.  
  390. /*========================================================================*/
  391.  
  392. /* Pause window -- presents a thermometer style display that goes up and 
  393.  * down as requested, changing colours when it is over particular thresholds
  394.  */
  395.  
  396. MRESULT EXPENTRY window_handler(HWND handle, ULONG msg, MPARAM p1, MPARAM p2)
  397. {
  398.   static int level = 0;       /* Level of meter */
  399.   static unsigned long timer = 0UL;    /* Number of our timer                */
  400.  
  401.   switch(msg)
  402.   {
  403.   case WM_CHAR:               /* Keyboard characters, we respond to arrows   */
  404.     {
  405.       int processed = 0;      /* Flag that message has been processed        */
  406.       
  407.       if (SHORT1FROMMP(p1) & KC_KEYUP)
  408.         return WinDefWindowProc(handle, msg, p1, p2);  /* Ignore releases    */
  409.       else
  410.       {
  411.         switch(SHORT2FROMMP(p2))/* Updates done via messages to ourselves for*/
  412.         {                       /* ease of extension to other triggers.      */
  413.         case VK_LEFT:           /* Step down if horizontal*/
  414.           if (horizontal)       WinSendMsg(handle, MSG_GO_DOWN, 0, 0);
  415.           processed = 1;
  416.           break;
  417.  
  418.         case VK_RIGHT:          /* Step up if horizontal */
  419.           if (horizontal)       WinSendMsg(handle, MSG_GO_UP,   0, 0);
  420.           processed = 1;
  421.           break;
  422.  
  423.         case VK_DOWN:           /* Step down if vertical */
  424.           if (! horizontal)     WinSendMsg(handle, MSG_GO_DOWN, 0, 0);
  425.           processed = 1;
  426.           break;
  427.  
  428.         case VK_UP:             /* Step up if vertical */
  429.           if (! horizontal)     WinSendMsg(handle, MSG_GO_UP,   0, 0);
  430.           processed = 1;
  431.           break;
  432.  
  433.         default:
  434.           break;
  435.         } /* Switch (virtual key) */
  436.  
  437.         /* Check for a quit keystroke; if found post ourselves quit msg */
  438.         if ((SHORT1FROMMP(p1) & KC_CHAR) &&
  439.             (SHORT1FROMMP(p2) == 'q' ||
  440.              SHORT1FROMMP(p2) == 'Q'))
  441.           WinPostMsg(handle, WM_CLOSE, 0, 0);
  442.         else
  443.           if (! processed)
  444.             return WinDefWindowProc(handle, msg, p1, p2);
  445.       } /* Keydown */
  446.     } /* Keystroke */
  447.     break;
  448.  
  449.   case MSG_GO_UP:           /* Handle updating the meter up/down together */
  450.   case MSG_GO_DOWN:
  451.     {
  452.       RECTL  updrect;         /* Update rectangle */
  453.       int    oldlevel = level;/* Old level value */
  454.  
  455.       /* Adjust level depending on message (note: relies on being able to
  456.        * overstep the value and correct it later)
  457.        */
  458.       if (msg == MSG_GO_UP)   level += step_up;
  459.       else                    level -= step_down;
  460.  
  461.       if (level > max_level)  level = max_level;
  462.       if (level < min_level)  level = min_level;
  463.         
  464. #if defined(DEBUG)
  465.       fprintf(stderr, "Level changed: %d -> %d\n", oldlevel, level);
  466. #endif
  467.       if (level == max_level && (oldlevel < max_level) && beepattop)
  468.       { /* We've reached the top level with this move, so we should beep  */
  469.         DosBeep(ARERT_BEEP_FREQ, ALERT_BEEP_LEN);
  470.       }
  471.  
  472.       /* If the value changed, we need to figure out what to update       */
  473.       /* Then invalidate that rectangle, for repainting                   */
  474.       /* Repainting done by general paint code for simplicity.            */
  475.       if (level != oldlevel)
  476.       {
  477.     LONG oldcolour;
  478.     LONG newcolour;
  479.  
  480.     WinQueryWindowRect(handle, &updrect);   /* Get whole window size  */
  481. #if defined(DEBUG)
  482.     fprintf(stderr, "Window is: %ld, %ld, %ld, %ld\n",
  483.         updrect.xLeft, updrect.xRight, updrect.yBottom, updrect.yTop);
  484.     fflush(stderr);
  485. #endif
  486.  
  487.     /* Figure out colours that would have been used (to detect crossing
  488.          * a boundary)
  489.          */
  490.     oldcolour = decide_colour(oldlevel);
  491.     newcolour = decide_colour(level);
  492.  
  493.     if (horizontal)
  494.     { /* horizontal window, we want to set X values */
  495.       LONG old, new;
  496.       old = (((updrect.xRight - updrect.xLeft) * (LONG)oldlevel)
  497.          / (LONG)max_level);
  498.       new = (((updrect.xRight - updrect.xLeft) * (LONG)level)
  499.          / (LONG)max_level);
  500.  
  501.       if (level > oldlevel)
  502.       { /* Level is going up, want fraction from oldlevel to new level */
  503.             /* Unless colour changed, then want up to new level */
  504.         if (oldcolour == newcolour)
  505.           updrect.xLeft  = old;
  506.  
  507.         updrect.xRight = new;
  508.       }
  509.       else
  510.       { /* Level is going down, want fraction from new level to old    */
  511.             /* Unless colour changed, then want up to new level */
  512.         if (oldcolour == newcolour)
  513.           updrect.xLeft  = new;
  514.  
  515.         updrect.xRight = (old+1);
  516.       }
  517.     }
  518.     else 
  519.     { /* Vertical window, we want to set Y values */
  520.       LONG old, new;
  521.       old = (((updrect.yTop - updrect.yBottom) * (LONG)oldlevel)
  522.          / (LONG)max_level);
  523.       new = (((updrect.yTop - updrect.yBottom) * (LONG)level)
  524.          / (LONG)max_level);
  525.  
  526.       if (level > oldlevel)
  527.       { /* Level is going up, want fraction from oldlevel to new level */
  528.             /* Unless colour changed, then want up to new level */
  529.         if (oldcolour == newcolour)
  530.           updrect.yBottom = old;
  531.  
  532.         updrect.yTop    = new;
  533.       }
  534.       else
  535.       { /* Level is going down, want fraction from new level to old    */
  536.             /* Unless colour changed, then want up to new level */
  537.         if (oldcolour == newcolour)
  538.           updrect.yBottom = new;
  539.  
  540.         updrect.yTop    = (old+1);
  541.       }
  542.     }
  543.  
  544. #if defined(DEBUG)
  545.     fprintf(stderr, "Triggering update of: %ld, %ld, %ld, %ld\n",
  546.         updrect.xLeft, updrect.xRight, updrect.yBottom, updrect.yTop);
  547.     fflush(stderr);
  548. #endif
  549.     WinInvalidateRect(handle, &updrect, FALSE);
  550.       }
  551.     } /* Meter adjusted */
  552.     break;
  553.  
  554.   case WM_PAINT:              /* Paint portions of the window */
  555.     {
  556.       RECTL   updrect;        /* Rectangle to update */
  557.       RECTL   window;         /* Window rectangle */
  558.       RECTL   box;            /* Rectangle to draw into */
  559.       HPS     ps = NULLHANDLE;/* Presentation space  */
  560.       LONG    fgcolour = CLR_WHITE;
  561.             
  562.       WinQueryWindowRect(handle, &window);   /* Get whole window size  */
  563.  
  564.       ps = WinBeginPaint(handle, NULLHANDLE, &updrect);
  565.       if (ps == NULLHANDLE)
  566.       { /* Oh dear, we can't get a handle to write to the window */
  567.     DosBeep(600,50);
  568.     break;
  569.       }
  570.  
  571. #if defined(DEBUG)
  572.       fprintf(stderr, "Update triggered for: %ld, %ld, %ld, %ld\n",
  573.           updrect.xLeft, updrect.xRight, updrect.yBottom, updrect.yTop);
  574.       fflush(stderr);
  575. #endif
  576.  
  577.       /* Figure out the colour to use when filling rectangle */
  578.       fgcolour = decide_colour(level);
  579.  
  580.       /* Need to fill in two bits: from start to level, from level to top */
  581.       /* NOTE: WinFillRect will paint into the left/bottom edges, but not */
  582.       /* into the top/right edges, unless left=right, or bottom=top. Thus */
  583.       /* we do not need to add an offset between fill/empty in, because it*/
  584.       /* will be done for us automatically.  The only exception is filling*/
  585.       /* strips one pixel wide, and that shouldn't happen too often.      */
  586.       if (horizontal)
  587.       { /* horizontal window, fill across */
  588.     LONG levelpos;
  589.  
  590.     /* Figure out the middle point */
  591.     levelpos = (((window.xRight - window.xLeft) *  (LONG)level) 
  592.             / (LONG)max_level);    
  593.     if (levelpos == 0)
  594.       levelpos = -1;        /* Hack: so that it gets completely empty */
  595.  
  596. #if defined(DEBUG)
  597.     fprintf(stderr, "Division is at: %ld\n", levelpos);
  598.     fflush(stderr);
  599. #endif
  600.  
  601.     /* Update the foreground colour bit as required */
  602.     if (levelpos >= updrect.xLeft && levelpos <= updrect.xRight)
  603.     {
  604.       box.xLeft   = updrect.xLeft;   box.xRight = levelpos;
  605.       box.yBottom = updrect.yBottom; box.yTop   = updrect.yTop;
  606. #if defined(DEBUG)
  607.       fprintf(stderr, "Filling: %ld, %ld, %ld, %ld\n",
  608.           box.xLeft,box.xRight, box.yBottom, box.yTop);
  609.       fflush(stderr);
  610. #endif
  611.       if (WinFillRect(ps, &box, fgcolour) != TRUE)
  612.       {
  613.         DosBeep(1200,50);
  614. #ifdef DEBUG        
  615.         fprintf(stderr, "*ERROR*, %08lx\n", WinGetLastError(ab)); 
  616.         fflush(stderr);
  617. #endif        
  618.       }
  619.     }
  620.  
  621.     /* Update the background colour bit as required */
  622.     if (updrect.xRight >= levelpos)
  623.     {
  624.       box.xLeft   = levelpos;        box.xRight = updrect.xRight;
  625.       box.yBottom = updrect.yBottom; box.yTop   = updrect.yTop;
  626. #if defined(DEBUG)
  627.       fprintf(stderr, "Emptying: %ld, %ld, %ld, %ld\n",
  628.           box.xLeft,box.xRight, box.yBottom, box.yTop);
  629.       fflush(stderr);
  630. #endif
  631.       if (WinFillRect(ps, &box, COLOUR_BKGND) != TRUE)
  632.       {
  633.         DosBeep(1300,100);
  634. #ifdef DEBUG        
  635.         fprintf(stderr, "<ERROR>, %08lx\n", WinGetLastError(ab));
  636.         fflush(stderr);
  637. #endif        
  638.       }
  639.     }
  640.       }
  641.       else 
  642.       { /* Vertical window, fill up */
  643.     LONG levelpos;
  644.  
  645.     /* Figure out the middle point */
  646.     levelpos = (((window.yTop - window.yBottom) * (LONG)level)
  647.             / (LONG)max_level);    
  648.     if (levelpos == 0)
  649.       levelpos = -1;        /* Hack: so that it gets completely empty */
  650.  
  651.     /* Update the foreground colour bit as required */
  652.     if (levelpos >= updrect.yBottom && levelpos <= updrect.yTop)
  653.     {
  654.       box.xLeft   = updrect.xLeft;   box.xRight = updrect.xRight;
  655.       box.yBottom = updrect.yBottom; box.yTop   = levelpos;
  656.       WinFillRect(ps, &box, fgcolour);
  657.     }
  658.  
  659.     /* Update the background colour bit as required */
  660.     if (updrect.yTop >= levelpos)
  661.     {
  662.       box.xLeft   = updrect.xLeft;   box.xRight = updrect.xRight;
  663.       box.yBottom = levelpos;        box.yTop   = updrect.yTop;
  664.       WinFillRect(ps, &box, COLOUR_BKGND);
  665.     }
  666.       }
  667.  
  668.       WinEndPaint(ps);        /* Release presentation space                 */
  669.     }
  670.  
  671.     break;
  672.  
  673.   case WM_CREATE:             /* Create window -- we use this to start our  */
  674.                               /* timer up.                                  */
  675.                               /* NOTE: I _hope_ this timer goes away when   */
  676.                               /* our function ends.  I can only see a way   */
  677.                               /* to stop the timer, not to destroy it.      */
  678.      timer = WinStartTimer(ab, handle, 1UL, check_seconds * 1000UL);
  679. #ifdef DEBUG
  680.      fprintf(stderr, "Started timer every %ld seconds (# %ld)\n",
  681.                      (check_seconds * 1000UL), timer);
  682.      fflush(stderr);                     
  683. #endif
  684.  
  685.      return (MRESULT)FALSE;   /* Continue window creation, we're done here. */
  686.      break;
  687.  
  688.   case WM_CLOSE:              /* Close the window down                      */
  689.      if (WinStopTimer(ab, handle, 1UL))
  690.      {
  691. #ifdef DEBUG
  692.        fprintf(stderr, "Stopped timer\n"); fflush(stderr);
  693. #endif         
  694.      }
  695.      
  696.      WinPostMsg(handle, WM_QUIT, 0, 0);   /* Now we've cleaned up, can quit */
  697.      return (MRESULT)FALSE;
  698.      break;
  699.  
  700.   case WM_TIMER:              /* Timer triggered                            */
  701.      if (SHORT1FROMMP(p1) == timer)
  702.      { /* It's our timer, we can do our stuff.                              */
  703. #ifdef USE_DLL
  704.        if (PMPauseGetCount() > 0)
  705.        { /* Yes, something has happened recently                            */
  706.             PMPauseClearCount();
  707.             WinPostMsg(handle, MSG_GO_UP, 0, 0);
  708.        }
  709.        else
  710.        { /* No, all quiet on the home front                                 */
  711.          WinPostMsg(handle, MSG_GO_DOWN, 0, 0);
  712.        }
  713. #endif       
  714. #ifdef DEBUG
  715.        fprintf(stderr, "Timer tick received.\n");
  716.        fflush(stderr);
  717. #endif       
  718.        return (MRESULT)FALSE;
  719.      }
  720.      else                     /* Someone else's timer, pass it on to default*/
  721.        return WinDefWindowProc(handle, msg, p1, p2);
  722.      break;
  723.   
  724.   case WM_ERASEBACKGROUND:    /* Yes, we would like our background erased   */
  725.     fprintf(stderr, "Asked for background to be erased\n"); fflush(stderr);
  726.     return (MRESULT)TRUE;
  727.  
  728.   default:
  729.     return WinDefWindowProc(handle, msg, p1, p2);
  730.   } 
  731.  
  732.   return (MRESULT)0;
  733. }
  734.  
  735. /* Figure out what colour to use for a given level */
  736. LONG decide_colour(int level)
  737. {
  738.   if      (level < threshold_notify)
  739.     return COLOUR_NORMAL;
  740.   else if (level < threshold_warn)
  741.     return COLOUR_NOTIFY;
  742.   else 
  743.     return COLOUR_WARN;
  744. }
  745.  
  746. /* Release the hook on the message queue, if we're suddenly killed */
  747. VOID APIENTRY ExeTrap(void)
  748. {
  749. #ifdef USE_DLL
  750.   PMPauseKill();
  751.   #ifdef DEBUG
  752.     fprintf(stderr, "Released hook on message queue.\n");  fflush(stderr);
  753.   #endif
  754. #endif
  755. #ifdef DEBUG
  756.   fprintf(stderr, "All done in exe trap.\n");   fflush(stderr);
  757. #endif  
  758.   DosExitList(EXLST_EXIT, (PFNEXITLIST)ExeTrap);
  759. }
  760.  
  761. /* Display usage information
  762.  * Two beeps are output before displaying the information since this is a
  763.  * PM program, and tricks are required to capture the stdout error (eg,
  764.  * program | less); the beeps alert to this information being available.
  765.  * (A later version will pop up a window with the information in it.)
  766.  */
  767. void usage(void)
  768. {
  769.   DosBeep(750, 75);  DosBeep(0, 75);  DosBeep(750, 75);
  770.   
  771.   printf("PMPAUSE $Revision: 0.16 $ -- typing/mouse usage monitor\n");
  772.   printf("Copyright Ewen McNeill, 1997.  All rights reserved.\n\n");
  773.  
  774.   printf("PMPAUSE comes with ABSOLUTELY NO WARRANTY; it is free software\n");
  775.   printf("and you are welcome to redistribute it under certain conditions.\n");
  776.   printf("For further information see the GNU Copyright Licence, version 2.\n\n");
  777.   
  778.   printf("Runtime options (all optional):\n");
  779.   printf("  -h      meter grows horizontally   \\___ mutually exclusive\n");
  780.   printf("  -v      meter grows vertically     /    (default vertical)\n");
  781.   printf("  -m <n>  maximum meter value (default 180)\n");
  782.   printf("  -n <n>  notify threshold (change to notify colour) (default 90)\n");
  783.   printf("  -w <n>  warn threshold (change to warn colour) (default 150)\n");
  784.   printf("  -u <n>  go UP this many units if busy during period (default 4)\n");
  785.   printf("  -d <n>  go DOWN this many units if idle during period (default 12)\n");
  786.   printf("  -c <s>  check every <s> seconds to see if busy/idle (default 4)\n\n");
  787.   printf("  -b      beep when reaching top level, as an alert.\n");
  788.   
  789.   printf("Note: 0 < notify threshold < warn threshold < maximum meter value\n\n");
  790.  
  791.   printf("  -?      output this help and exit (catch by piping to less, more, etc)\n\n");
  792.  
  793.   printf("Size/Position is specified with a standard X Windowing System command line\n");
  794.   printf("argument, of the form:\n");
  795.   printf("  <width>x<height>{+-}<xoffset>{+-}yoffset\n");
  796.   printf("Positive offsets relative to bottom, left, negative top, right (OS/2 style)\n");
  797.   printf("  -x       positive relative to top, left negative to bottom, right (like X)\n");
  798. }
  799.