home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 7 / 07.iso / c / c034 / 4.ddi / SOURCE / SNAP.C$ / SNAP.bin
Encoding:
Text File  |  1989-11-30  |  14.9 KB  |  455 lines

  1. /* Snap - An OS/2 screen capture utility
  2.  *
  3.  * Snap starts a background process containing a keyboard monitor.
  4.  * The monitor checks for a hot key (ALT-*). If found, a thread is
  5.  * launched to write the screen to a file. Various command line options
  6.  * allow you to specify capture behavior or to deinstall the program.
  7.  *
  8.  * To compile, use the following command line:
  9.  *
  10.  *   cl /MT /G2s snap.c
  11.  */
  12.  
  13. /* Function prototypes */
  14. int  Monitor( void );
  15. void Snap( unsigned long _far *arg );
  16. void BackError( char *msgErr );
  17. void Syntax( void );
  18. void EvalOptions( int argc, char **argv );
  19.  
  20. /* Define constants to enable function groups in OS2 include files */
  21. #define INCL_NOCOMMON
  22. #define INCL_NOPM
  23. #define INCL_KBD            // KBDKEYINFO
  24. #define INCL_VIO            // Vio functions
  25. #define INCL_DOSMEMMGR      // DosGetShrSeg, DosAllocShrSeg,
  26. #define INCL_DOSMONITORS    // DosMon functions
  27. #define INCL_DOSMISC        // DosGetEnv
  28. #define INCL_DOSSEMAPHORES  // DosSem functions
  29. #define INCL_DOSPROCESS     // DosBeep, DosSetPrty
  30. #define INCL_DOSINFOSEG     // DosGetInfoSeg
  31. #include <os2.h>
  32.  
  33. #include <malloc.h>         // malloc, free
  34. #include <process.h>        // _beginthread, _endthread, exit, spawnl
  35. #include <string.h>         // strcpy, strcat
  36. #include <stdlib.h>         // atoi, itoa, _MAX_PATH
  37. #include <stddef.h>         // _threadid variable
  38. #include <stdio.h>          // puts, fopen, fwrite, etc.
  39. #include <conio.h>          // kbhit
  40.  
  41. #define CON  0              // Handle for the console device
  42. #define FAIL -1             // Fail to start thread
  43.  
  44. #define STAR    0x37        // Scan code for * on numeric keypad
  45. #define RELEASE 0x40        // Bit mask for key release
  46.  
  47. /* Name and structure for shared memory data */
  48. char szShrSeg[] = { "\\SHAREMEM\\SNAP.DAT" };
  49. struct SHARED
  50. {
  51.     BOOL  fSound;               // Sound flag
  52.     BOOL  fAppend;              // Append flag
  53.     BOOL  fInstall;             // Install flag
  54.     SHORT cScreen;              // Count of screens
  55.     LONG  lfWait;               // Wait semaphore
  56.     CHAR  achSnap[_MAX_PATH];   // Snap file name
  57. } _far *pshrSnap = 0;           // Initialize offset to 0. Segment will
  58.                                 // be initialized by system call.
  59.  
  60. /* Count in bytes of shared segment */
  61. #define C_SHARESEG sizeof( struct SHARED )
  62.  
  63. void main( int argc, char **argv )
  64. {
  65.     USHORT offCmd;              // Dummy for DosGetEnv
  66.     CHAR   *pchSnapExe = 0;     // Pointer to name of executable file
  67.                                 //   (offset initialized to 0)
  68.  
  69.     /* Try to get shared segment (note how selector value is placed
  70.      * directly in the segment word of the pointer address). There are
  71.      * three possibilities:
  72.      *
  73.      *   - We can't get memory. This means SNAP is not installed,
  74.      *     so we must allocate the memory and exec ourself in the
  75.      *     background to install the monitor.
  76.      *   - We can get memory and we are not installed. This means
  77.      *     we have been execed by previous process to install monitor.
  78.      *   - We can get memory and we are already installed. This means
  79.      *     we were just called to modify options.
  80.      */
  81.     if( DosGetShrSeg( szShrSeg, (PSEL)&pshrSnap + 1 ) )
  82.     {
  83.         /* Segment doesn't exist, so try to allocate it. */
  84.         if( DosAllocShrSeg( C_SHARESEG, szShrSeg, (PSEL)&pshrSnap + 1 ) )
  85.         {
  86.             puts( "Can't allocate shared memory" );
  87.             exit( 1 );
  88.         }
  89.         else
  90.         {
  91.             /* This is the first time through, so we must execute
  92.              * ourself to do installation. First set defaults, then
  93.              * modify for options.
  94.              */
  95.             pshrSnap->fSound = TRUE;
  96.             pshrSnap->fAppend = TRUE;
  97.             pshrSnap->cScreen = 0;
  98.             pshrSnap->fInstall = FALSE;
  99.             strcpy( pshrSnap->achSnap, "SNAP.IMG" );
  100.             DosSemSet( &pshrSnap->lfWait );
  101.             EvalOptions( argc, argv );
  102.  
  103.             /* Get our own path name from the end of the environment
  104.              * segment. This is the most reliable way to get the full
  105.              * path name of the current file, since the extension is
  106.              * ommitted from argv[0].
  107.              */
  108.             DosGetEnv( (PUSHORT)&pchSnapExe + 1, &offCmd );
  109.  
  110.             /* Adjust forward until we point to full path of .EXE file. */
  111.             while( *pchSnapExe++ || *pchSnapExe )
  112.                 ;
  113.             ++pchSnapExe;
  114.  
  115.             /* Spawn ourself as a background process. Can't install
  116.              * monitor now because we are in foreground. A background
  117.              * process needs to install the monitor.
  118.              */
  119.             if( spawnl( P_DETACH, pchSnapExe, pchSnapExe, NULL ) == -1 )
  120.             {
  121.                 puts( "Can't start background process" );
  122.                 exit( 1 );
  123.             }
  124.             puts( "Snap installed" );
  125.             Syntax();
  126.  
  127.             /* Wait for background child process to report receiving
  128.              * shared data.
  129.              */
  130.             DosSemWait( &pshrSnap->lfWait, SEM_INDEFINITE_WAIT );
  131.         }
  132.     }
  133.     else
  134.     {
  135.         /* Already installed. We are being run to evaluate options and
  136.          * modify behavior accordingly.
  137.          */
  138.         if( pshrSnap->fInstall )
  139.             if( argc == 1 )
  140.                 puts( "Snap already installed" );
  141.             else
  142.                 EvalOptions( argc, argv );
  143.         else
  144.         {
  145.             /* Not installed, so we were execed by original SNAP to
  146.              * install monitor. Tell parent we have received data, set
  147.              * install flag, and install monitor.
  148.              */
  149.             DosSemClear( &pshrSnap->lfWait );
  150.  
  151.             /* Set installed flag and start monitor. */
  152.             pshrSnap->fInstall = TRUE;
  153.  
  154.             exit( Monitor() );
  155.         }
  156.     }
  157. }
  158.  
  159. /* Monitor routine checks keystrokes as they occur and calls
  160.  * the Snap thread if the hot key is pressed.
  161.  *
  162.  * Params: None
  163.  *
  164.  * Return: 1 if error, 0 if deinstalled
  165.  *
  166.  * Uses:   pshrSnap - Shared memory structure
  167.  */
  168. int Monitor()
  169. {
  170.     #define BUFSIZE 128             // Size for monitor buffers:
  171.                                     //   64 minimum, 128 recommended
  172.     #define STACKSIZE 2048          // 2K minimum for any system call
  173.  
  174.     PMONIN pmnin;
  175.     PMONOUT pmnout;
  176.  
  177.     struct KEYPACKET                // KBD monitor data record
  178.     {
  179.         USHORT fMon;
  180.         KBDKEYINFO kki;
  181.         USHORT fDD;
  182.     } keyBuff;
  183.     USHORT ckeyBuff = sizeof( keyBuff );
  184.  
  185.     HMONITOR hKeyMon;               // Keyboard handle from monitor open
  186.     PGINFOSEG pGIS = 0, pLIS = 0;   // Information segment structures
  187.     LONG  lfSnap = FALSE;           // Semaphore for each Snap thread
  188.  
  189.     /* Allocate space for monitor read/write buffers and mark size. */
  190.     pmnin = (PMONIN)malloc( BUFSIZE );
  191.     pmnin->cb = BUFSIZE;
  192.     pmnout = (PMONOUT)malloc( BUFSIZE );
  193.     pmnout->cb = BUFSIZE;
  194.  
  195.     /* Register monitor to the keyboard device (KBD$). */
  196.     if( DosMonOpen( "KBD$", &hKeyMon ) )
  197.     {
  198.         BackError( "Can't open monitor" );
  199.         return 1;
  200.     }
  201.  
  202.     /* Get information segments (all we really need is ID of current
  203.      * screen group from Global Information Segment).
  204.      */
  205.     DosGetInfoSeg( (PSEL)&pGIS + 1, (PSEL)&pLIS + 1 );
  206.  
  207.     /* Register the monitor buffers to the current screen group */
  208.     if( DosMonReg( hKeyMon, (PBYTE)pmnin, (PBYTE)pmnout,
  209.                    MONITOR_DEFAULT, pGIS->sgCurrent ) )
  210.     {
  211.         BackError( "Can't register monitor" );
  212.         return 1;
  213.     }
  214.  
  215.     /* Make process time critical so keys are interpreted without delay. */
  216.     DosSetPrty( PRTYS_PROCESS, PRTYC_TIMECRITICAL, 0, 0 );
  217.  
  218.     /* Monitor loop - read into monitor buffer and examine. Take action
  219.      * if hot key, otherwise pass on to device driver.
  220.      */
  221.     while( pshrSnap->fInstall )
  222.     {
  223.         DosMonRead( (PBYTE)pmnin, IO_WAIT, (PBYTE)&keyBuff, &ckeyBuff );
  224.  
  225.         /* Snap if ALT+STAR is down. */
  226.         if( ((keyBuff.kki.chScan == STAR) || (keyBuff.kki.chScan == 0x2a)) &&
  227.             (keyBuff.kki.fsState & ALT) &&
  228.             (!(keyBuff.fDD & RELEASE)) )
  229.         {
  230.             /* Make sure last thread is finished */
  231.             DosSemWait( &lfSnap, SEM_INDEFINITE_WAIT );
  232.             if( (_beginthread( Snap, NULL, STACKSIZE,
  233.                                (PVOID)&lfSnap )) == FAIL )
  234.                 BackError( "Can't start screen capture thread" );
  235.             else
  236.                 DosSemSet( &lfSnap );
  237.         }
  238.         else
  239.             /* Pass the key through if it is not the hot key */
  240.             DosMonWrite( (PBYTE)pmnout, (PBYTE)&keyBuff, ckeyBuff );
  241.     }
  242.  
  243.     /* Close monitor */
  244.     free( pmnin );
  245.     free( pmnout );
  246.     DosMonClose( hKeyMon );
  247.     return 0;
  248. }
  249.  
  250. /* Screen capture routine (run as a thread). Does a pop-up to get access
  251.  * to the current screen. Reads characters from the screen into a buffer.
  252.  * Then filters trailing spaces as it writes buffer to a file.
  253.  *
  254.  * Params: plfSnap - pointer to flag indicated snap status
  255.  *
  256.  * Return: none
  257.  *
  258.  * Uses:   pshrSnap - Shared memory structure
  259.  */
  260. void Snap( ULONG _far *plfSnap )
  261. {
  262.     enum { NOTE_B = 494, NOTE_C = 523, NOTE_F = 698 };
  263.     CHAR   *pchScreen;              // Buffer for captured screen
  264.     USHORT cbScreen;                // Count of bytes in buffer
  265.     FILE   *sFile;                  // File stream
  266.     USHORT usLine, usPos, usWidth;
  267.     CHAR   ach[5];
  268.     USHORT fWait = VP_WAIT | VP_TRANSPARENT;
  269.     VIOMODEINFO vmi = { sizeof( vmi ) };
  270.  
  271.     if( pshrSnap->fSound )
  272.         DosBeep( NOTE_F, NOTE_C );
  273.  
  274.     /* Pop up to current screen and check its size. */
  275.     VioPopUp( &fWait, CON );
  276.     VioGetMode( &vmi, CON );
  277.  
  278.     /* Allocate memory for a full screen plus one byte */
  279.     cbScreen = vmi.col * vmi.row;
  280.     pchScreen = malloc( cbScreen + 1 );
  281.  
  282.     /* Read screen and end popup */
  283.     VioReadCharStr( pchScreen, &cbScreen, 0, 0, CON );
  284.     VioEndPopUp( 0 );
  285.  
  286.     /* Increment screen count (4 digits or less) and convert to string. */
  287.     pshrSnap->cScreen = (pshrSnap->cScreen + 1) % 9999;
  288.     itoa( pshrSnap->cScreen, ach, 10 );
  289.  
  290.     /* Make numbered file name if appropriate. */
  291.     if( !pshrSnap->fAppend )
  292.         strcat( strcat( strcpy( pshrSnap->achSnap, "SNAP" ), ach ), ".IMG" );
  293.  
  294.     /* Open file and write buffer to it a line at a time */
  295.     if( (sFile = fopen( pshrSnap->achSnap, "at" )) == NULL )
  296.     {
  297.         BackError( "Can't open file" );
  298.         --pshrSnap->cScreen;
  299.     }
  300.     else
  301.     {
  302.         if( pshrSnap->fAppend )
  303.         {
  304.             /* Not using fprintf reduces overhead. */
  305.             fputs( "**** Screen ", sFile );
  306.             fputs( ach, sFile );
  307.             fputs( " ****\n", sFile );
  308.         }
  309.  
  310.         for( usLine = 0, usPos = 0; usLine < vmi.row; usLine++ )
  311.         {
  312.             /* Throw away trailing spaces */
  313.             for( usWidth = vmi.col;
  314.                  (pchScreen[usPos + usWidth - 1] == ' ' ) && usWidth;
  315.                  usWidth-- )
  316.                 ;
  317.             /* Write line and newline */
  318.             fwrite( pchScreen + usPos, 1, usWidth, sFile );
  319.             fputc( '\n', sFile );
  320.             usPos += vmi.col;
  321.         }
  322.         fclose( sFile );
  323.     }
  324.     if( pshrSnap->fSound )
  325.         DosBeep( NOTE_C, NOTE_B );
  326.  
  327.     /* Free memory and let parent know we are done */
  328.     free( pchScreen );
  329.     DosSemClear( plfSnap );
  330. }
  331.  
  332. /* Displays an error message from within a background process or thread.
  333.  * The monitor is in the background and has no screen group, so it must
  334.  * use VioPopUp to get access to the screen.
  335.  *
  336.  * Params: msgErr - error message string
  337.  *
  338.  * Return: None
  339.  */
  340. void BackError( char *pchErr )
  341. {
  342.     USHORT fWait = VP_WAIT | VP_TRANSPARENT;
  343.  
  344.     VioPopUp( &fWait, CON );
  345.     puts( pchErr );
  346.     puts( "Press any key to continue . . ." );
  347.     while( !kbhit() )
  348.         ;
  349.     VioEndPopUp( CON );
  350. }
  351.  
  352. /* Displays syntax.
  353.  *
  354.  * Params: None
  355.  *
  356.  * Return: None
  357.  */
  358. void Syntax()
  359. {
  360.     puts( "\nOptions: " );
  361.     puts( "\t/H\t  Display help." );
  362.     puts( "\t/S\t  Turn sound on (default)." );
  363.     puts( "\t/Q\t  Turn sound off." );
  364.     puts( "\t/D\t  Deinstall." );
  365.     puts( "\t/A [path] Append each screen to a file (complete path allowed)." );
  366.     puts( "\t\t  If no file given, default is SNAP.IMG in current directory" );
  367.     puts( "\t\t  Resets screen number to 1." );
  368.     puts( "\t/N [num]  Create numbered file for each screen." );
  369.     puts( "\t\t  Example: SNAP1.IMG, SNAP2.IMG in current directory." );
  370.     puts( "\t\t  Resets screen number to 1 or to num if given." );
  371. }
  372.  
  373. /* Evaluate command-line options.
  374.  *
  375.  * Params: argc - Number of arguments
  376.  *         argv - Pointer to argument list
  377.  *
  378.  * Return: none
  379.  *
  380.  * Uses: Shared memory structure - pshrSnap
  381.  */
  382. void EvalOptions( int argc, char **argv )
  383. {
  384.     SHORT i;
  385.  
  386.     /* Look for and handle arguments */
  387.     for(  i = 1; i < argc; i++ )
  388.     {
  389.         if( argv[i][0] == '/' || argv[i][0] == '-' )
  390.         {
  391.             switch( argv[i][1] )
  392.             {
  393.                 case 'A':
  394.                 case 'a':
  395.                     pshrSnap->fAppend = TRUE;
  396.                     pshrSnap->cScreen = 0;
  397.                     if( (argv[++i]) &&
  398.                         (argv[i][0] != '/') &&
  399.                         (argv[i][0] != '-') )
  400.  
  401.                     {
  402.                         strcpy( pshrSnap->achSnap, argv[i] );
  403.                         puts( "Append mode - name set" );
  404.                     }
  405.                     else
  406.                         puts( "Append mode" );
  407.                     break;
  408.  
  409.                 case 'N':
  410.                 case 'n':
  411.                     pshrSnap->fAppend = FALSE;
  412.                     puts( "Numbered file mode" );
  413.                     if( (argv[++i]) &&
  414.                         (argv[i][0] != '/') &&
  415.                         (argv[i][0] != '-') )
  416.                     {
  417.                         pshrSnap->cScreen = (atoi( argv[i] ) % 9999) - 1;
  418.                     }
  419.                     else
  420.                         pshrSnap->cScreen = 0;
  421.                     break;
  422.  
  423.                 case 'Q':
  424.                 case 'q':
  425.                     pshrSnap->fSound = FALSE;
  426.                     puts( "Sound off" );
  427.                     break;
  428.  
  429.                 case 'S'  :
  430.                 case 's'  :
  431.                     pshrSnap->fSound = TRUE;
  432.                     puts( "Sound on" );
  433.                     break;
  434.  
  435.                 case 'D':
  436.                 case 'd':
  437.                     if( pshrSnap->fInstall )
  438.                     {
  439.                         pshrSnap->fInstall = FALSE;
  440.                         puts( "Deinstalling" );
  441.                     }
  442.                     else
  443.                         exit( 0 );
  444.                     break;
  445.  
  446.                 case 'H'  :
  447.                 case 'h'  :
  448.                     if( pshrSnap->fInstall )
  449.                         Syntax();
  450.                     break;
  451.             }
  452.         }
  453.     }
  454. }
  455.