home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 10 Tools
/
10-Tools.zip
/
os2prgc.zip
/
comm.c
next >
Wrap
C/C++ Source or Header
|
1995-03-07
|
21KB
|
722 lines
/*****************************************************************************
COMM.C -- Sample code for handling a comm port, character I/O based.
Copyright (C) 1993,94,95 by Craig Morrison, All Rights Reserved.
You may use this code in your own projects, regardless of renumeration.
All I ask is that you prominently display the above copyright notice.
Should you need assistance, I can be contacted at the following addresses:
Fidonet: Craig Morrison, 1:201/60@fidonet.org
Internet: cam@wpc.cioe.com
Post: Craig Morrison
1316 Ferry St.
Lafayette, IN 47901-1533
USA
NOTES:
You'll notice the complete lack of any references to run-time
library functions. This was done on purpose so that *I* could
control what happens when a thread gets killed. This package
contains just about everything you'll need to do comm port/file
I/O, string manipulation and ordinal number conversions.
*****************************************************************************/
#define INCL_SUB
#define INCL_NOPM
#define INCL_BASE
#define INCL_DOS
#define INCL_DOSPROCESS
#define INCL_DOSDEVIOCTL
#include <os2.h>
#include <stdio.h>
#include "comm.h"
#include "str.h"
#include "dosmem.h"
#define COMIO_BLOCKSIZE 2048
#define COMIO_ERROR -1
#define KBSTATUS_EXTENDED_KEY_DOWN 0x02
#define UL 0
#define UR 1
#define LL 2
#define LR 3
#define HB 4
#define VB 5
static PSZ boxDefs[] = {"┌┐└┘─│",
"╔╗╚╝═║",
"╒╕╘╛═│",
"╓╖╙╜─║",
"++++-|",
" ",
NULL
};
/*****************************************************************************
This table consists of the data needed to switch the console video mode
using VioSetMode(). See: vidSetConsoleMode() to see how to use them.
*****************************************************************************/
static MODEDATA modeDefs[] = {
{sizeof(MODEDATA), 1, 4, 80, 25, 720, 400}, /* 0 */
{sizeof(MODEDATA), 1, 4, 80, 43, 720, 400}, /* 1 */
{sizeof(MODEDATA), 1, 4, 80, 50, 720, 400}, /* 2 */
{sizeof(MODEDATA), 1, 4, 132, 25, 720, 400}, /* 3 */
{sizeof(MODEDATA), 1, 4, 132, 43, 720, 400}, /* 4 */
{sizeof(MODEDATA), 5, 4, 40, 25, 320, 200}, /* 5 */
{sizeof(MODEDATA), 1, 4, 40, 25, 320, 200}, /* 6 */
{sizeof(MODEDATA), 5, 4, 80, 25, 640, 200}, /* 7 */
{sizeof(MODEDATA), 1, 4, 80, 25, 640, 200}, /* 8 */
{sizeof(MODEDATA), 3, 2, 40, 25, 320, 200}, /* 9 */
{sizeof(MODEDATA), 7, 2, 40, 25, 320, 200}, /* 10 */
{sizeof(MODEDATA), 3, 1, 80, 25, 640, 200}, /* 11 */
{sizeof(MODEDATA), 0, 0, 80, 25, 720, 350}, /* 12 */
{sizeof(MODEDATA), 3, 4, 40, 25, 320, 200}, /* 13 */
{sizeof(MODEDATA), 3, 4, 80, 25, 640, 200}, /* 14 */
{sizeof(MODEDATA), 2, 0, 80, 25, 640, 350}, /* 15 */
{sizeof(MODEDATA), 3, 4, 80, 25, 640, 350}, /* 16 */
{sizeof(MODEDATA), 3, 1, 80, 30, 640, 480}, /* 17 */
{sizeof(MODEDATA), 3, 4, 80, 30, 640, 480}, /* 18 */
{sizeof(MODEDATA), 3, 8, 40, 25, 320, 200} /* 19 */
};
#define MODE_COUNT (sizeof(modeDefs)/sizeof(MODEDATA))
static PID mainPID;
static TID commTID = (TID)-1;
static HMTX ioSem;
static HEV hev;
static int IObufPtr = 0;
static char IObuf[COMIO_BLOCKSIZE];
#ifdef __USE_SAMPLE_MAIN__
/*****************************************************************************
Sample main() that shows the steps to setup the necessary semaphores and
start the thread used to read from a comm port.
*****************************************************************************/
void main(int argc, char **argv)
{
int rc = 255;
PTIB ptib;
PPIB ppib;
HFILE port;
APIRET error;
/* grab our comm port handle from the first command line argument */
port = (HFILE) asctoul(argv[1]);
/* mutex semaphore to handle contentions for incoming data */
DosCreateMutexSem(NULL, &ioSem, DC_SEM_SHARED, FALSE);
/* event semaphore to keep us from polling for incoming data */
DosCreateEventSem(NULL, &hev, DC_SEM_SHARED, 0L);
/* Don't really need this, but this is how you get your PID */
DosGetInfoBlocks(&ptib, &ppib);
mainPID = ppib->pib_ulpid;
/* if user is remote, fire up a thread to read comm port */
if (port)
error = DosCreateThread(&commTID, (PFNTHREAD)ReadPortData,
(ULONG) port, 0L, 16384L);
else
error = (APIRET)0;
if (!error)
{
/* Do your processing here. */
/* What I generally do is fire up another thread and then */
/* DosWaitThread() on it. Then I can use a timer to kill */
/* it so time limits can be imposed on the user. */
rc = 0;
/* if user is remote and comm read thread got started, kill it */
if ((port) && (commTID != (TID)-1))
{
while (DosKillThread(commTID) == ERROR_BUSY);
DosWaitThread(&commTID, DCWW_WAIT);
}
}
/* clean up semaphores by closing their handles */
DosCloseEventSem(hev);
DosCloseMutexSem(ioSem);
DosExit(EXIT_PROCESS, rc);
}
#endif /* __USE_SAMPLE_MAIN__ */
/*****************************************************************************
Used to detect the state of the carrier for a port. As written it is VERY
relaxed in case of a flakey line connection.
*****************************************************************************/
BOOL CarrierDetect(HFILE port)
{
BYTE mdmFlags;
ULONG cbData;
if (port)
{
DosDevIOCtl(port, IOCTL_ASYNC, ASYNC_GETMODEMINPUT, NULL, 0L,
NULL, &mdmFlags, sizeof(mdmFlags), &cbData);
if ((mdmFlags & DCD_ON)!=DCD_ON) /* be forgiving */
{
DosSleep(8L);
DosDevIOCtl(port, IOCTL_ASYNC, ASYNC_GETMODEMINPUT, NULL,
0L, NULL, &mdmFlags, sizeof(mdmFlags), &cbData);
}
if ((mdmFlags & DCD_ON)!=DCD_ON) /* one more chance */
{
DosSleep(16L);
DosDevIOCtl(port, IOCTL_ASYNC, ASYNC_GETMODEMINPUT, NULL,
0L, NULL, &mdmFlags, sizeof(mdmFlags), &cbData);
}
return ((mdmFlags & DCD_ON) == DCD_ON);
}
else
return TRUE;
}
/*****************************************************************************
This is the thread that gets started to read incoming data from a comm
port. It only gets started up if the user is NOT local.
This could probably be done better, but for character I/O it does its
job quite well.
*****************************************************************************/
VOID ReadPortData(PVOID p)
{
HFILE port;
CHAR incoming;
ULONG cbRead;
APIRET error;
port = (HFILE)p;
for (;;)
{
error = DosRead(port, &incoming, 1L, &cbRead);
if ((!error || error==ERROR_MORE_DATA) && cbRead)
{
DosRequestMutexSem(ioSem, SEM_INDEFINITE_WAIT);
if (IObufPtr<COMIO_BLOCKSIZE)
IObuf[IObufPtr++] = incoming;
DosReleaseMutexSem(ioSem);
DosPostEventSem(hev);
}
else
DosSleep(32L);
if (!CarrierDetect(port))
DosPostEventSem(hev);
}
}
/*****************************************************************************
Read a single character of user input.
If the user is local, the character comes directly from the local console.
If the user is remote, the character comes from the buffer set up for
incoming port data. If the buffer is empty, we wait on an event semaphore
that will be posted to by the read port thread when data is present.
Either way, there is no polling done here, if no key is ready the thread
calling this function will block until something can be read or an error
occurs (if this happens, COMIO_ERROR is returned.)
*****************************************************************************/
INT CharFromPort(HFILE port)
{
INT c, cbWaiting;
ULONG evCount;
KBDKEYINFO kInfo;
static UCHAR key = 0;
c = COMIO_ERROR;
if (!port)
{
if (key)
{
c = key;
key = 0;
}
else
{
kInfo.chChar = 0;
kInfo.chScan = 0;
kInfo.fbStatus = 0;
kInfo.bNlsShift = 0;
kInfo.fsState = 0;
kInfo.time = 0;
if (!KbdCharIn(&kInfo, IO_WAIT, 0))
{
if (kInfo.fbStatus & KBSTATUS_EXTENDED_KEY_DOWN)
{
key = kInfo.chChar;
c = 0;
}
else
{
c = kInfo.chChar;
key = 0;
}
}
else
key = COMIO_ERROR;
}
}
else
{
if (CarrierDetect(port))
{
DosRequestMutexSem(ioSem, SEM_INDEFINITE_WAIT);
cbWaiting = IObufPtr;
DosReleaseMutexSem(ioSem);
if (!cbWaiting)
{
DosWaitEventSem(hev, SEM_INDEFINITE_WAIT);
DosResetEventSem(hev, &evCount);
}
if (IObufPtr)
{
DosRequestMutexSem(ioSem, SEM_INDEFINITE_WAIT);
c = IObuf[0];
IObufPtr--;
if (IObufPtr)
movemem(&IObuf[0], &IObuf[1], IObufPtr);
DosReleaseMutexSem(ioSem);
}
}
}
return(c);
}
/*****************************************************************************
Output an ASCIIZ string.
If the user is remote, the string is sent to both the port and the local
console.
*****************************************************************************/
void comPrint(char *s, HFILE port)
{
ULONG cbOut,
cbChars,
slice = 0;
PSZ p;
for (cbChars=0,p=s;*p!=0;p++,cbChars++);
if (cbChars)
{
if (port)
VioWrtTTY(s, cbChars, 0);
do
{
DosWrite(port, s, cbChars, &cbOut);
cbChars -= cbOut;
if (cbChars && (slice++ >= 8))
{
DosSleep(32L);
slice = 0;
}
} while (cbChars);
}
}
/*****************************************************************************
Example of how to use the port read functions to get a string of (n)
characters from the user.
Returns FALSE if the user drops carrier during input.
*****************************************************************************/
BOOL GetNString(PSZ s, INT l, HFILE port)
{
INT c, i;
CHAR o[2];
*s = '\0';
i = 0;
do {
c = CharFromPort(port);
if (c==COMIO_ERROR)
if (!CarrierDetect(port))
return FALSE;
if (c=='\b')
{
if (i)
{
comPrint("\b \b", port);
s--;
i--;
*s = 0;
}
}
else if ((c>=32) && (c!=127))
{
if (i<l)
{
*s = c;
s++;
*s = 0;
i++;
o[0] = c;
o[1] = 0;
comPrint(o, port);
}
}
} while (c!='\r');
return TRUE;
}
/*****************************************************************************
All the com...() functions below are for ANSI screen control.
It should be pretty obvious from the function names what they do.
*****************************************************************************/
VOID comGotoXY(HFILE port, PSZ x, PSZ y)
{
comPrint("\x1b[", port);
comPrint(y, port);
comPrint(";", port);
comPrint(x, port);
comPrint("H", port);
}
VOID comClrEol(HFILE port)
{
comPrint("\x1b[K", port);
}
VOID comCursorRight(HFILE port, PSZ cbMove)
{
comPrint("\x1b[", port);
comPrint(cbMove, port);
comPrint("C", port);
}
VOID comCursorLeft(HFILE port, PSZ cbMove)
{
comPrint("\x1b[", port);
comPrint(cbMove, port);
comPrint("D", port);
}
VOID comCursorUp(HFILE port, PSZ cbMove)
{
comPrint("\x1b[", port);
comPrint(cbMove, port);
comPrint("A", port);
}
VOID comCursorDown(HFILE port, PSZ cbMove)
{
comPrint("\x1b[", port);
comPrint(cbMove, port);
comPrint("B", port);
}
VOID comSaveCursorPos(HFILE port)
{
comPrint("\x1b[s", port);
}
VOID comRestoreCursorPos(HFILE port)
{
comPrint("\x1b[u", port);
}
VOID comClearScreen(HFILE port)
{
comPrint("\x1b[2J", port);
}
VOID comSetAttributes(HFILE port, PSZ attr, PSZ fg, PSZ bg)
{
CHAR attrStr[80];
PSZ p;
p = attrStr;
if (attr)
{
cpystr(p, attr);
catstr(p, ";");
p = attrStr;
p += lenstr(attrStr);
}
if (fg)
{
cpystr(p, fg);
catstr(p, ";");
p = attrStr;
p += lenstr(attrStr);
}
if (bg)
cpystr(p, bg);
if (attrStr[lenstr(attrStr)-1]==';')
attrStr[lenstr(attrStr)-1]=0;
comPrint("\x1b[", port);
comPrint(attrStr, port);
comPrint("m", port);
}
/*****************************************************************************
Display a box on the console using ANSI screen control.
All values are 1 based, the upper left corner of screen is 1, 1.
Maximums: cx = console columns - 2, cy = console rows - 2.
*****************************************************************************/
VOID DisplayBox(HFILE port, INT x, INT y, INT cx, INT cy, INT bType,
BOOL filled)
{
CHAR szX[32], szY[32], szCX[32], szLine[256];
PSZ b;
INT i, j;
switch(bType)
{
case SINGLE_LINE_BOX:
case DOUBLE_LINE_BOX:
case DOUBLE_TOP_BOX:
case DOUBLE_SIDE_BOX:
case NO_IBMCHAR_BOX:
case SPACE_BOX:
b = boxDefs[bType];
break;
default:
b = NULL;
break;
}
if (b)
{
ltoasc((LONG)cx, szCX);
comSaveCursorPos(port);
comGotoXY(port, ltoasc((LONG)x, szX), ltoasc((LONG)y, szY));
i = 0;
szLine[i++] = b[UL];
for(;i<=cx;i++)
szLine[i] = b[HB];
szLine[i++] = b[UR];
szLine[i] = 0;
comPrint(szLine, port);
for (i=0;i<cy;i++)
{
comGotoXY(port, szX, ltoasc((LONG)++y, szY));
if (filled)
{
j = 0;
szLine[j++] = b[VB];
for(;j<=cx;j++)
szLine[j] = ' ';
szLine[j++] = b[VB];
szLine[j] = 0;
comPrint(szLine, port);
}
else
{
szLine[0] = b[VB];
szLine[1] = 0;
comPrint(szLine, port);
comCursorRight(port, szCX);
comPrint(szLine, port);
}
}
comGotoXY(port, szX, ltoasc((LONG)++y, szY));
i = 0;
szLine[i++] = b[LL];
for(;i<=cx;i++)
szLine[i] = b[HB];
szLine[i++] = b[LR];
szLine[i] = 0;
comPrint(szLine, port);
comRestoreCursorPos(port);
}
}
/*****************************************************************************
Returns the maximum size of the console screen as a ULONG value.
rows = LOUSHORT(returnValue)
cols = HIUSHORT(returnValue)
*****************************************************************************/
ULONG vidGetConsoleXY(VOID)
{
VIOMODEINFO vmi;
setmem(&vmi, 0, sizeof(vmi));
vmi.cb = sizeof(VIOMODEINFO);
VioGetMode(&vmi, 0L);
return(MAKEULONG(vmi.row, vmi.col));
}
/*****************************************************************************
Returns the offset into our mode settings table above of the current
video mode for the console. On error, returns MODE_COUNT.
*****************************************************************************/
USHORT vidGetConsoleMode(VOID)
{
VIOMODEINFO vmi;
INT i;
setmem(&vmi, 0, sizeof(vmi));
vmi.cb = sizeof(VIOMODEINFO);
if (VioGetMode(&vmi, 0L))
return 0xFFFF;
for(i=0; i<MODE_COUNT; i++)
if ((vmi.fbType==modeDefs[i].fbType) &&
(vmi.color==modeDefs[i].color) && (vmi.col==modeDefs[i].col) &&
(vmi.row==modeDefs[i].row) && (vmi.hres==modeDefs[i].hres) &&
(vmi.vres==modeDefs[i].vres))
break;
return(i);
}
/*****************************************************************************
Sets the console video mode from the mode settings table above.
*****************************************************************************/
USHORT vidSetConsoleMode(USHORT modeIndex)
{
if (modeIndex<MODE_COUNT)
return(VioSetMode((PVIOMODEINFO)&modeDefs[modeIndex], 0L));
else
return 0xFFFF;
}
/*****************************************************************************
Writes a character string with a specified attribute (color) to the local
console at the specified coordinates. All characters are displayable. The
cursor position is NOT updated by this function.
*****************************************************************************/
VOID vidStrAtWAttr(INT x, INT y, PSZ s, CHAR attr)
{
VioWrtCharStrAtt(s, lenstr(s), y, x, &attr, 0L);
}
/*****************************************************************************
Writes a character string to the local console at the specified
coordinates. All characters are displayable. The cursor position is NOT
updated with this function.
*****************************************************************************/
VOID vidStrAt(INT x, INT y, PSZ s)
{
VioWrtCharStr(s, lenstr(s), y, x, 0L);
}
/*****************************************************************************
Writes a character string to the local console at the current cursor
position. CR, LF, BS, TAB and BELL are recognized as control characters.
ANSI escape sequences are also recognized.
*****************************************************************************/
VOID vidStr(PSZ s)
{
VioWrtTTY(s, lenstr(s), 0L);
}
/*****************************************************************************
Sets the cursor position on the local console.
*****************************************************************************/
VOID vidSetCursorPos(USHORT col, USHORT row)
{
VioSetCurPos(row, col, 0L);
}
/*****************************************************************************
Returns the cursor position on the local console as a ULONG value.
row = LOUSHORT(returnValue)
col = HIUSHORT(returnValue)
*****************************************************************************/
ULONG vidGetCursorPos(VOID)
{
USHORT col, row;
VioGetCurPos(&row, &col, 0L);
return(MAKEULONG(row, col));
}
/*****************************************************************************
Returns or queries the console keyboard for a character as a ULONG value.
The character and scan code are returned in the low USHORT of the return
value. The keyboard status flags are returned in the high USHORT of the
return value.
char = LOBYTE(LOUSHORT(returnValue))
scan code = HIBYTE(LOUSHORT(returnValue))
status = HIUSHORT(returnValue)
When polling to see if a key is waiting test bit 0 of status to see if a
key was returned. 0 = yes, 1 = no.
Test bit 1 of status to see if the character is an extended key, 1 = yes,
0 = no. Please do this instead of testing the character code for 0x00 or
0xE0 so those countries that use the character 0xE0 can benefit from your
code also.
ioWait controls whether or not to wait for a character, 0 = wait,
1 = don't wait.
*****************************************************************************/
ULONG kbdGetKey(USHORT ioWait)
{
KBDKEYINFO kInfo;
setmem(&kInfo, 0, sizeof(kInfo));
KbdCharIn(&kInfo, ioWait, 0);
return(MAKEULONG(MAKEUSHORT(kInfo.chChar, kInfo.chScan), kInfo.fbStatus));
}