home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / os2prgc.zip / comm.c next >
C/C++ Source or Header  |  1995-03-07  |  21KB  |  722 lines

  1. /*****************************************************************************
  2.  
  3.   COMM.C -- Sample code for handling a comm port, character I/O based.
  4.   Copyright (C) 1993,94,95 by Craig Morrison, All Rights Reserved.
  5.  
  6.   You may use this code in your own projects, regardless of renumeration.
  7.   All I ask is that you prominently display the above copyright notice.
  8.  
  9.   Should you need assistance, I can be contacted at the following addresses:
  10.  
  11.         Fidonet:        Craig Morrison, 1:201/60@fidonet.org
  12.         Internet:       cam@wpc.cioe.com
  13.         Post:           Craig Morrison
  14.                         1316 Ferry St.
  15.                         Lafayette, IN 47901-1533
  16.                         USA
  17.  
  18.   NOTES:
  19.  
  20.     You'll notice the complete lack of any references to run-time
  21.     library functions. This was done on purpose so that *I* could
  22.     control what happens when a thread gets killed. This package
  23.     contains just about everything you'll need to do comm port/file
  24.     I/O, string manipulation and ordinal number conversions.
  25.  
  26.  *****************************************************************************/
  27. #define INCL_SUB
  28. #define INCL_NOPM
  29. #define INCL_BASE
  30. #define INCL_DOS
  31. #define INCL_DOSPROCESS
  32. #define INCL_DOSDEVIOCTL
  33.  
  34. #include <os2.h>
  35. #include <stdio.h>
  36. #include "comm.h"
  37. #include "str.h"
  38. #include "dosmem.h"
  39.  
  40. #define COMIO_BLOCKSIZE             2048
  41. #define COMIO_ERROR                 -1
  42. #define KBSTATUS_EXTENDED_KEY_DOWN  0x02
  43. #define UL                          0
  44. #define UR                          1
  45. #define LL                          2
  46. #define LR                          3
  47. #define HB                          4
  48. #define VB                          5
  49.  
  50. static PSZ boxDefs[] = {"┌┐└┘─│",
  51.                         "╔╗╚╝═║",
  52.                         "╒╕╘╛═│",
  53.                         "╓╖╙╜─║",
  54.                         "++++-|",
  55.                         "      ",
  56.                         NULL
  57. };
  58.  
  59. /*****************************************************************************
  60.  
  61.   This table consists of the data needed to switch the console video mode
  62.   using VioSetMode(). See: vidSetConsoleMode() to see how to use them.
  63.  
  64.  *****************************************************************************/
  65. static MODEDATA modeDefs[] = {
  66.     {sizeof(MODEDATA), 1, 4, 80,  25, 720, 400},         /* 0  */
  67.     {sizeof(MODEDATA), 1, 4, 80,  43, 720, 400},         /* 1  */
  68.     {sizeof(MODEDATA), 1, 4, 80,  50, 720, 400},         /* 2  */
  69.     {sizeof(MODEDATA), 1, 4, 132, 25, 720, 400},         /* 3  */
  70.     {sizeof(MODEDATA), 1, 4, 132, 43, 720, 400},         /* 4  */
  71.     {sizeof(MODEDATA), 5, 4, 40,  25, 320, 200},         /* 5  */
  72.     {sizeof(MODEDATA), 1, 4, 40,  25, 320, 200},         /* 6  */
  73.     {sizeof(MODEDATA), 5, 4, 80,  25, 640, 200},         /* 7  */
  74.     {sizeof(MODEDATA), 1, 4, 80,  25, 640, 200},         /* 8  */
  75.     {sizeof(MODEDATA), 3, 2, 40,  25, 320, 200},         /* 9  */
  76.     {sizeof(MODEDATA), 7, 2, 40,  25, 320, 200},         /* 10 */
  77.     {sizeof(MODEDATA), 3, 1, 80,  25, 640, 200},         /* 11 */
  78.     {sizeof(MODEDATA), 0, 0, 80,  25, 720, 350},         /* 12 */
  79.     {sizeof(MODEDATA), 3, 4, 40,  25, 320, 200},         /* 13 */
  80.     {sizeof(MODEDATA), 3, 4, 80,  25, 640, 200},         /* 14 */
  81.     {sizeof(MODEDATA), 2, 0, 80,  25, 640, 350},         /* 15 */
  82.     {sizeof(MODEDATA), 3, 4, 80,  25, 640, 350},         /* 16 */
  83.     {sizeof(MODEDATA), 3, 1, 80,  30, 640, 480},         /* 17 */
  84.     {sizeof(MODEDATA), 3, 4, 80,  30, 640, 480},         /* 18 */
  85.     {sizeof(MODEDATA), 3, 8, 40,  25, 320, 200}          /* 19 */
  86. };
  87.  
  88. #define MODE_COUNT (sizeof(modeDefs)/sizeof(MODEDATA))
  89.  
  90. static PID  mainPID;
  91. static TID  commTID = (TID)-1;
  92. static HMTX ioSem;
  93. static HEV  hev;
  94. static int  IObufPtr = 0;
  95. static char IObuf[COMIO_BLOCKSIZE];
  96.  
  97. #ifdef __USE_SAMPLE_MAIN__
  98. /*****************************************************************************
  99.  
  100.   Sample main() that shows the steps to setup the necessary semaphores and
  101.   start the thread used to read from a comm port.
  102.  
  103.  *****************************************************************************/
  104. void main(int argc, char **argv)
  105. {
  106.     int rc = 255;
  107.     PTIB ptib;
  108.     PPIB ppib;
  109.     HFILE port;
  110.     APIRET error;
  111.  
  112.     /* grab our comm port handle from the first command line argument */
  113.     port = (HFILE) asctoul(argv[1]);
  114.  
  115.     /* mutex semaphore to handle contentions for incoming data */
  116.     DosCreateMutexSem(NULL, &ioSem, DC_SEM_SHARED, FALSE);
  117.  
  118.     /* event semaphore to keep us from polling for incoming data */
  119.     DosCreateEventSem(NULL, &hev, DC_SEM_SHARED, 0L);
  120.  
  121.     /* Don't really need this, but this is how you get your PID */
  122.     DosGetInfoBlocks(&ptib, &ppib);
  123.     mainPID = ppib->pib_ulpid;
  124.  
  125.     /* if user is remote, fire up a thread to read comm port */
  126.     if (port)
  127.         error = DosCreateThread(&commTID, (PFNTHREAD)ReadPortData,
  128.                                 (ULONG) port, 0L, 16384L);
  129.     else
  130.         error = (APIRET)0;
  131.  
  132.     if (!error)
  133.     {
  134.         /* Do your processing here.                               */
  135.         /* What I generally do is fire up another thread and then */
  136.         /* DosWaitThread() on it. Then I can use a timer to kill  */
  137.         /* it so time limits can be imposed on the user.          */
  138.  
  139.         rc = 0;
  140.  
  141.         /* if user is remote and comm read thread got started, kill it */
  142.         if ((port) && (commTID != (TID)-1))
  143.         {
  144.             while (DosKillThread(commTID) == ERROR_BUSY);
  145.             DosWaitThread(&commTID, DCWW_WAIT);
  146.         }
  147.     }
  148.  
  149.     /* clean up semaphores by closing their handles */
  150.     DosCloseEventSem(hev);
  151.     DosCloseMutexSem(ioSem);
  152.  
  153.     DosExit(EXIT_PROCESS, rc);
  154. }
  155. #endif /* __USE_SAMPLE_MAIN__ */
  156.  
  157. /*****************************************************************************
  158.  
  159.   Used to detect the state of the carrier for a port. As written it is VERY
  160.   relaxed in case of a flakey line connection.
  161.  
  162.  *****************************************************************************/
  163. BOOL CarrierDetect(HFILE port)
  164. {
  165.     BYTE mdmFlags;
  166.     ULONG cbData;
  167.  
  168.     if (port)
  169.     {
  170.         DosDevIOCtl(port, IOCTL_ASYNC, ASYNC_GETMODEMINPUT, NULL, 0L,
  171.                     NULL, &mdmFlags, sizeof(mdmFlags), &cbData);
  172.  
  173.         if ((mdmFlags & DCD_ON)!=DCD_ON) /* be forgiving */
  174.         {
  175.             DosSleep(8L);
  176.             DosDevIOCtl(port, IOCTL_ASYNC, ASYNC_GETMODEMINPUT, NULL,
  177.                         0L, NULL, &mdmFlags, sizeof(mdmFlags), &cbData);
  178.         }
  179.  
  180.         if ((mdmFlags & DCD_ON)!=DCD_ON) /* one more chance */
  181.         {
  182.             DosSleep(16L);
  183.             DosDevIOCtl(port, IOCTL_ASYNC, ASYNC_GETMODEMINPUT, NULL,
  184.                         0L, NULL, &mdmFlags, sizeof(mdmFlags), &cbData);
  185.         }
  186.  
  187.         return ((mdmFlags & DCD_ON) == DCD_ON);
  188.     }
  189.     else
  190.         return TRUE;
  191. }
  192.  
  193. /*****************************************************************************
  194.  
  195.   This is the thread that gets started to read incoming data from a comm
  196.   port. It only gets started up if the user is NOT local.
  197.  
  198.   This could probably be done better, but for character I/O it does its
  199.   job quite well.
  200.  
  201.  *****************************************************************************/
  202. VOID ReadPortData(PVOID p)
  203. {
  204.     HFILE port;
  205.     CHAR incoming;
  206.     ULONG cbRead;
  207.     APIRET error;
  208.  
  209.     port = (HFILE)p;
  210.     for (;;)
  211.     {
  212.         error = DosRead(port, &incoming, 1L, &cbRead);
  213.         if ((!error || error==ERROR_MORE_DATA) && cbRead)
  214.         {
  215.             DosRequestMutexSem(ioSem, SEM_INDEFINITE_WAIT);
  216.             if (IObufPtr<COMIO_BLOCKSIZE)
  217.                 IObuf[IObufPtr++] = incoming;
  218.             DosReleaseMutexSem(ioSem);
  219.             DosPostEventSem(hev);
  220.         }
  221.         else
  222.             DosSleep(32L);
  223.  
  224.         if (!CarrierDetect(port))
  225.             DosPostEventSem(hev);
  226.     }
  227. }
  228.  
  229. /*****************************************************************************
  230.  
  231.   Read a single character of user input.
  232.  
  233.   If the user is local, the character comes directly from the local console.
  234.  
  235.   If the user is remote, the character comes from the buffer set up for
  236.   incoming port data. If the buffer is empty, we wait on an event semaphore
  237.   that will be posted to by the read port thread when data is present.
  238.  
  239.   Either way, there is no polling done here, if no key is ready the thread
  240.   calling this function will block until something can be read or an error
  241.   occurs (if this happens, COMIO_ERROR is returned.)
  242.  
  243.  *****************************************************************************/
  244. INT CharFromPort(HFILE port)
  245. {
  246.     INT c, cbWaiting;
  247.     ULONG evCount;
  248.     KBDKEYINFO kInfo;
  249.     static UCHAR key = 0;
  250.  
  251.     c = COMIO_ERROR;
  252.  
  253.     if (!port)
  254.     {
  255.         if (key)
  256.         {
  257.             c = key;
  258.             key = 0;
  259.         }
  260.         else
  261.         {
  262.             kInfo.chChar = 0;
  263.             kInfo.chScan = 0;
  264.             kInfo.fbStatus = 0;
  265.             kInfo.bNlsShift = 0;
  266.             kInfo.fsState = 0;
  267.             kInfo.time = 0;
  268.  
  269.             if (!KbdCharIn(&kInfo, IO_WAIT, 0))
  270.             {
  271.                 if (kInfo.fbStatus & KBSTATUS_EXTENDED_KEY_DOWN)
  272.                 {
  273.                     key = kInfo.chChar;
  274.                     c = 0;
  275.                 }
  276.                 else
  277.                 {
  278.                     c = kInfo.chChar;
  279.                     key = 0;
  280.                 }
  281.             }
  282.             else
  283.                 key = COMIO_ERROR;
  284.         }
  285.     }
  286.     else
  287.     {
  288.         if (CarrierDetect(port))
  289.         {
  290.             DosRequestMutexSem(ioSem, SEM_INDEFINITE_WAIT);
  291.             cbWaiting = IObufPtr;
  292.             DosReleaseMutexSem(ioSem);
  293.             if (!cbWaiting)
  294.             {
  295.                 DosWaitEventSem(hev, SEM_INDEFINITE_WAIT);
  296.                 DosResetEventSem(hev, &evCount);
  297.             }
  298.             if (IObufPtr)
  299.             {
  300.                 DosRequestMutexSem(ioSem, SEM_INDEFINITE_WAIT);
  301.                 c = IObuf[0];
  302.                 IObufPtr--;
  303.                 if (IObufPtr)
  304.                     movemem(&IObuf[0], &IObuf[1], IObufPtr);
  305.                 DosReleaseMutexSem(ioSem);
  306.             }
  307.         }
  308.     }
  309.  
  310.     return(c);
  311. }
  312.  
  313. /*****************************************************************************
  314.  
  315.   Output an ASCIIZ string.
  316.  
  317.   If the user is remote, the string is sent to both the port and the local
  318.   console.
  319.  
  320.  *****************************************************************************/
  321. void comPrint(char *s, HFILE port)
  322. {
  323.     ULONG cbOut,
  324.           cbChars,
  325.           slice = 0;
  326.     PSZ   p;
  327.  
  328.     for (cbChars=0,p=s;*p!=0;p++,cbChars++);
  329.  
  330.     if (cbChars)
  331.     {
  332.         if (port)
  333.             VioWrtTTY(s, cbChars, 0);
  334.         do
  335.         {
  336.             DosWrite(port, s, cbChars, &cbOut);
  337.             cbChars -= cbOut;
  338.             if (cbChars && (slice++ >= 8))
  339.             {
  340.                 DosSleep(32L);
  341.                 slice = 0;
  342.             }
  343.         } while (cbChars);
  344.     }
  345. }
  346.  
  347. /*****************************************************************************
  348.  
  349.   Example of how to use the port read functions to get a string of (n)
  350.   characters from the user.
  351.  
  352.   Returns FALSE if the user drops carrier during input.
  353.  
  354.  *****************************************************************************/
  355. BOOL GetNString(PSZ s, INT l, HFILE port)
  356. {
  357.     INT c, i;
  358.     CHAR o[2];
  359.  
  360.     *s = '\0';
  361.     i = 0;
  362.  
  363.     do {
  364.         c = CharFromPort(port);
  365.  
  366.         if (c==COMIO_ERROR)
  367.             if (!CarrierDetect(port))
  368.                 return FALSE;
  369.  
  370.         if (c=='\b')
  371.         {
  372.             if (i)
  373.             {
  374.                 comPrint("\b \b", port);
  375.                 s--;
  376.                 i--;
  377.                 *s = 0;
  378.             }
  379.         }
  380.         else if ((c>=32) && (c!=127))
  381.         {
  382.             if (i<l)
  383.             {
  384.                 *s = c;
  385.                 s++;
  386.                 *s = 0;
  387.                 i++;
  388.                 o[0] = c;
  389.                 o[1] = 0;
  390.                 comPrint(o, port);
  391.             }
  392.         }
  393.     } while (c!='\r');
  394.     return TRUE;
  395. }
  396.  
  397. /*****************************************************************************
  398.  
  399.   All the com...() functions below are for ANSI screen control.
  400.  
  401.   It should be pretty obvious from the function names what they do.
  402.  
  403.  *****************************************************************************/
  404. VOID comGotoXY(HFILE port, PSZ x, PSZ y)
  405. {
  406.     comPrint("\x1b[", port);
  407.     comPrint(y, port);
  408.     comPrint(";", port);
  409.     comPrint(x, port);
  410.     comPrint("H", port);
  411. }
  412.  
  413. VOID comClrEol(HFILE port)
  414. {
  415.     comPrint("\x1b[K", port);
  416. }
  417.  
  418. VOID comCursorRight(HFILE port, PSZ cbMove)
  419. {
  420.     comPrint("\x1b[", port);
  421.     comPrint(cbMove, port);
  422.     comPrint("C", port);
  423. }
  424.  
  425. VOID comCursorLeft(HFILE port, PSZ cbMove)
  426. {
  427.     comPrint("\x1b[", port);
  428.     comPrint(cbMove, port);
  429.     comPrint("D", port);
  430. }
  431.  
  432. VOID comCursorUp(HFILE port, PSZ cbMove)
  433. {
  434.     comPrint("\x1b[", port);
  435.     comPrint(cbMove, port);
  436.     comPrint("A", port);
  437. }
  438.  
  439. VOID comCursorDown(HFILE port, PSZ cbMove)
  440. {
  441.     comPrint("\x1b[", port);
  442.     comPrint(cbMove, port);
  443.     comPrint("B", port);
  444. }
  445.  
  446. VOID comSaveCursorPos(HFILE port)
  447. {
  448.     comPrint("\x1b[s", port);
  449. }
  450.  
  451. VOID comRestoreCursorPos(HFILE port)
  452. {
  453.     comPrint("\x1b[u", port);
  454. }
  455.  
  456. VOID comClearScreen(HFILE port)
  457. {
  458.     comPrint("\x1b[2J", port);
  459. }
  460.  
  461. VOID comSetAttributes(HFILE port, PSZ attr, PSZ fg, PSZ bg)
  462. {
  463.     CHAR attrStr[80];
  464.     PSZ p;
  465.  
  466.     p = attrStr;
  467.  
  468.     if (attr)
  469.     {
  470.         cpystr(p, attr);
  471.         catstr(p, ";");
  472.         p = attrStr;
  473.         p += lenstr(attrStr);
  474.     }
  475.     if (fg)
  476.     {
  477.         cpystr(p, fg);
  478.         catstr(p, ";");
  479.         p = attrStr;
  480.         p += lenstr(attrStr);
  481.     }
  482.     if (bg)
  483.         cpystr(p, bg);
  484.  
  485.     if (attrStr[lenstr(attrStr)-1]==';')
  486.         attrStr[lenstr(attrStr)-1]=0;
  487.  
  488.     comPrint("\x1b[", port);
  489.     comPrint(attrStr, port);
  490.     comPrint("m", port);
  491. }
  492.  
  493. /*****************************************************************************
  494.  
  495.   Display a box on the console using ANSI screen control.
  496.  
  497.   All values are 1 based, the upper left corner of screen is 1, 1.
  498.   Maximums: cx = console columns - 2, cy = console rows - 2.
  499.  
  500.  *****************************************************************************/
  501. VOID DisplayBox(HFILE port, INT x, INT y, INT cx, INT cy, INT bType,
  502.                 BOOL filled)
  503. {
  504.     CHAR szX[32], szY[32], szCX[32], szLine[256];
  505.     PSZ b;
  506.     INT i, j;
  507.  
  508.     switch(bType)
  509.     {
  510.         case SINGLE_LINE_BOX:
  511.         case DOUBLE_LINE_BOX:
  512.         case DOUBLE_TOP_BOX:
  513.         case DOUBLE_SIDE_BOX:
  514.         case NO_IBMCHAR_BOX:
  515.         case SPACE_BOX:
  516.             b = boxDefs[bType];
  517.             break;
  518.  
  519.         default:
  520.             b = NULL;
  521.             break;
  522.     }
  523.  
  524.     if (b)
  525.     {
  526.         ltoasc((LONG)cx, szCX);
  527.         comSaveCursorPos(port);
  528.         comGotoXY(port, ltoasc((LONG)x, szX), ltoasc((LONG)y, szY));
  529.         i = 0;
  530.         szLine[i++] = b[UL];
  531.         for(;i<=cx;i++)
  532.             szLine[i] = b[HB];
  533.         szLine[i++] = b[UR];
  534.         szLine[i] = 0;
  535.         comPrint(szLine, port);
  536.         for (i=0;i<cy;i++)
  537.         {
  538.             comGotoXY(port, szX, ltoasc((LONG)++y, szY));
  539.             if (filled)
  540.             {
  541.                 j = 0;
  542.                 szLine[j++] = b[VB];
  543.                 for(;j<=cx;j++)
  544.                     szLine[j] = ' ';
  545.                 szLine[j++] = b[VB];
  546.                 szLine[j] = 0;
  547.                 comPrint(szLine, port);
  548.             }
  549.             else
  550.             {
  551.                 szLine[0] = b[VB];
  552.                 szLine[1] = 0;
  553.                 comPrint(szLine, port);
  554.                 comCursorRight(port, szCX);
  555.                 comPrint(szLine, port);
  556.             }
  557.         }
  558.         comGotoXY(port, szX, ltoasc((LONG)++y, szY));
  559.         i = 0;
  560.         szLine[i++] = b[LL];
  561.         for(;i<=cx;i++)
  562.             szLine[i] = b[HB];
  563.         szLine[i++] = b[LR];
  564.         szLine[i] = 0;
  565.         comPrint(szLine, port);
  566.         comRestoreCursorPos(port);
  567.     }
  568. }
  569.  
  570. /*****************************************************************************
  571.  
  572.   Returns the maximum size of the console screen as a ULONG value.
  573.  
  574.   rows = LOUSHORT(returnValue)
  575.   cols = HIUSHORT(returnValue)
  576.  
  577.  *****************************************************************************/
  578. ULONG vidGetConsoleXY(VOID)
  579. {
  580.     VIOMODEINFO vmi;
  581.  
  582.     setmem(&vmi, 0, sizeof(vmi));
  583.     vmi.cb = sizeof(VIOMODEINFO);
  584.     VioGetMode(&vmi, 0L);
  585.  
  586.     return(MAKEULONG(vmi.row, vmi.col));
  587. }
  588.  
  589. /*****************************************************************************
  590.  
  591.   Returns the offset into our mode settings table above of the current
  592.   video mode for the console. On error, returns MODE_COUNT.
  593.  
  594.  *****************************************************************************/
  595. USHORT vidGetConsoleMode(VOID)
  596. {
  597.     VIOMODEINFO vmi;
  598.     INT i;
  599.  
  600.     setmem(&vmi, 0, sizeof(vmi));
  601.     vmi.cb = sizeof(VIOMODEINFO);
  602.     if (VioGetMode(&vmi, 0L))
  603.         return 0xFFFF;
  604.  
  605.     for(i=0; i<MODE_COUNT; i++)
  606.         if ((vmi.fbType==modeDefs[i].fbType) &&
  607.            (vmi.color==modeDefs[i].color) && (vmi.col==modeDefs[i].col) &&
  608.            (vmi.row==modeDefs[i].row) && (vmi.hres==modeDefs[i].hres) &&
  609.            (vmi.vres==modeDefs[i].vres))
  610.             break;
  611.  
  612.     return(i);
  613. }
  614.  
  615. /*****************************************************************************
  616.  
  617.   Sets the console video mode from the mode settings table above.
  618.  
  619.  *****************************************************************************/
  620. USHORT vidSetConsoleMode(USHORT modeIndex)
  621. {
  622.     if (modeIndex<MODE_COUNT)
  623.         return(VioSetMode((PVIOMODEINFO)&modeDefs[modeIndex], 0L));
  624.     else
  625.         return 0xFFFF;
  626. }
  627.  
  628. /*****************************************************************************
  629.  
  630.   Writes a character string with a specified attribute (color) to the local
  631.   console at the specified coordinates. All characters are displayable. The
  632.   cursor position is NOT updated by this function.
  633.  
  634.  *****************************************************************************/
  635. VOID vidStrAtWAttr(INT x, INT y, PSZ s, CHAR attr)
  636. {
  637.     VioWrtCharStrAtt(s, lenstr(s), y, x, &attr, 0L);
  638. }
  639.  
  640. /*****************************************************************************
  641.  
  642.   Writes a character string to the local console at the specified
  643.   coordinates. All characters are displayable. The cursor position is NOT
  644.   updated with this function.
  645.  
  646.  *****************************************************************************/
  647. VOID vidStrAt(INT x, INT y, PSZ s)
  648. {
  649.     VioWrtCharStr(s, lenstr(s), y, x, 0L);
  650. }
  651.  
  652. /*****************************************************************************
  653.  
  654.   Writes a character string to the local console at the current cursor
  655.   position. CR, LF, BS, TAB and BELL are recognized as control characters.
  656.   ANSI escape sequences are also recognized.
  657.  
  658.  *****************************************************************************/
  659. VOID vidStr(PSZ s)
  660. {
  661.     VioWrtTTY(s, lenstr(s), 0L);
  662. }
  663.  
  664. /*****************************************************************************
  665.  
  666.   Sets the cursor position on the local console.
  667.  
  668.  *****************************************************************************/
  669. VOID vidSetCursorPos(USHORT col, USHORT row)
  670. {
  671.     VioSetCurPos(row, col, 0L);
  672. }
  673.  
  674. /*****************************************************************************
  675.  
  676.   Returns the cursor position on the local console as a ULONG value.
  677.  
  678.   row = LOUSHORT(returnValue)
  679.   col = HIUSHORT(returnValue)
  680.  
  681.  *****************************************************************************/
  682. ULONG vidGetCursorPos(VOID)
  683. {
  684.     USHORT col, row;
  685.  
  686.     VioGetCurPos(&row, &col, 0L);
  687.     return(MAKEULONG(row, col));
  688. }
  689.  
  690. /*****************************************************************************
  691.  
  692.   Returns or queries the console keyboard for a character as a ULONG value.
  693.  
  694.   The character and scan code are returned in the low USHORT of the return
  695.   value. The keyboard status flags are returned in the high USHORT of the
  696.   return value.
  697.  
  698.   char      = LOBYTE(LOUSHORT(returnValue))
  699.   scan code = HIBYTE(LOUSHORT(returnValue))
  700.   status    = HIUSHORT(returnValue)
  701.  
  702.   When polling to see if a key is waiting test bit 0 of status to see if a
  703.   key was returned. 0 = yes, 1 = no.
  704.  
  705.   Test bit 1 of status to see if the character is an extended key, 1 = yes,
  706.   0 = no. Please do this instead of testing the character code for 0x00 or
  707.   0xE0 so those countries that use the character 0xE0 can benefit from your
  708.   code also.
  709.  
  710.   ioWait controls whether or not to wait for a character, 0 = wait,
  711.   1 = don't wait.
  712.  
  713.  *****************************************************************************/
  714. ULONG kbdGetKey(USHORT ioWait)
  715. {
  716.     KBDKEYINFO kInfo;
  717.  
  718.     setmem(&kInfo, 0, sizeof(kInfo));
  719.     KbdCharIn(&kInfo, ioWait, 0);
  720.     return(MAKEULONG(MAKEUSHORT(kInfo.chChar, kInfo.chScan), kInfo.fbStatus));
  721. }
  722.