[comp.sys.amiga] Set your console to raw mode

cmcmanis%pepper@Sun.COM (Chuck McManis) (06/16/87)

Many times I have wanted to set 'stdin' in my programs to what is often
referred to as 'CBREAK' mode. Basically, that means that every time you
call getchar() you get a character. Up until now there was no easy way
to do that. The enclosed shar file contains three files, testraw.c
raw.c and sendpacket.c. The interesting one is raw.c which contains the
functions raw() and cooked(). These take a file pointer (FILE *) and
convert it to raw mode or cooked mode respectively. The testraw.c file
shows how it is done. Note that this will probably only work for Lattice
users since it uses the Lattice ios1.h file to find the DOS FileHandle
associated with a 'Level 2' file. Anyway, it was short so it is included
here complete.

--Chuck McManis
[Watch out for the signature at the end ]
------------------------cut here---------------------

#	This is a shell archive.
#	Remove everything above and including the cut line.
#	Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar:    Shell Archiver
#	Run the following text with /bin/sh to create:
#	testraw.c
#	raw.c
#	sendpacket.c
# This archive created: Tue Jun 16 01:47:01 1987
cat << \SHAR_EOF > testraw.c
/*
 *	testraw.c
 *
 *   This program shows how to use the functions raw() and cooked() to
 * control the way characters are read from a Level 2 file pointer.
 * These are only useful if the file pointer points at an instance of
 * the console.device, which means one of 'stdin', 'stdout', or 'stderr'
 * or a console opened with fp = fopen("CON:x/y/wid/len/Title","w+");
 * like this example does. 
 *
 * Written : 16-Jun-87 By Chuck McManis, do with it what you will.
 *
 */

#include <stdio.h>
#ifndef u_char
#define	u_char	unsigned char
#endif

void main()

{
  FILE	*win;
  char	c;
  long	i;

  printf("This program shows how to use raw mode file pointers.\n");
  printf("First we open the window...\n");
  win = fopen("CON:10/10/400/100/Test Console","w+");
/* If opening a file, use setnbf() to set it to unbuffered mode, this is
 * very importatant. Not required for stdin since Lattice defaults it to
 * unbuffered. 
 */
  setnbf(win);
  fprintf(win,"Using the default mode, type some characters ... \n");  
  i = 0;
  while ((c = fgetc(win)) != 'Q') {
    i = (i + 1) % 25;
    if (i == 0) printf("\n");
    printf(" %02x",(u_char) c);
  }
  printf("\n************\n");
  fprintf(win,"Now switching to 'raw' mode ...\n");
  if (raw(win) != 0) perror("raw");
  i = 0;
  while ((c = fgetc(win)) != 'Q') {
    i = (i + 1) % 25;
    if (i == 0) printf("\n");
    printf(" %02x",(u_char) c);
  }
  printf("\n************\n");
  fprintf(win,"Now back to 'cooked' mode ... \n");
  if (cooked(win) != 0) perror("cooked");
  i = 0;
  while ((c = fgetc(win)) != 'Q') {
    i = (i + 1) % 25;
    if (i == 0) printf("\n");
    printf(" %02x",(u_char) c);
  }
}
SHAR_EOF
cat << \SHAR_EOF > raw.c
/* 
 *	raw.c
 *
 *    This is a routine for setting a given stream to raw or cooked mode.
 * This is useful when you are using Lattice C to produce programs that
 * want to read single characters with the "getch()" or "fgetc" call.
 *
 * Written : 18-Jun-87 By Chuck McManis. 
 * 	     If you use it I would appreciate credit for it somewhere.
 */
#include <exec/types.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <stdio.h>
#include <ios1.h>
#include <error.h>

/* New Packet in 1.2 */
#define ACTION_SCREEN_MODE	994L

extern	int	errno;		/* The error variable */

/*
 * Function raw() - Convert the specified file pointer to 'raw' mode. This
 * only works on TTY's and essentially keeps DOS from translating keys for
 * you, also (BIG WIN) it means getch() will return immediately rather than
 * wait for a return. You lose editing features though.
 */
long
raw(fp)

FILE *fp;

{
  struct MsgPort 	*mp; /* The File Handle message port */
  struct FileHandle 	*afh;
  struct UFB 		*ufb;
  long			Arg[1],res;

  ufb = (struct UFB *) chkufb(fileno(fp));  /* Step one, get the file handle */
  afh = (struct FileHandle *)(ufb->ufbfh); 

  if (!IsInteractive(afh)) {	/* Step two, check to see if it's a console */
    errno = ENOTTY;
    return(-1);
  }
                              /* Step three, get it's message port. */
  mp  = ((struct FileHandle *)(BADDR(afh)))->fh_Type;
  Arg[0] = -1L;
  res = SendPacket(mp,ACTION_SCREEN_MODE,Arg,1); /* Put it in RAW: mode */
  if (res == 0) {
    errno = ENXIO;
    return(-1);
  }
  return(0);
}

/*
 * Function - cooked() this function returns the designate file pointer to
 * it's normal, wait for a <CR> mode. This is exactly like raw() except that
 * it sends a 0 to the console to make it back into a CON: from a RAW:
 */

long
cooked(fp)

FILE *fp;

{
  struct MsgPort 	*mp; /* The File Handle message port */
  struct FileHandle 	*afh;
  struct UFB 		*ufb;
  long			Arg[1],res;

  ufb = (struct UFB *) chkufb(fileno(fp));
  afh = (struct FileHandle *)(ufb->ufbfh);
  if ( ! IsInteractive(afh)) {
    errno = ENOTTY;
    return(-1);
  }
  mp  = ((struct FileHandle *)(BADDR(afh)))->fh_Type;
  Arg[0] = 0;
  res = SendPacket(mp,ACTION_SCREEN_MODE,Arg,1);
  if (res == 0) {
    errno = ENXIO;
    return(-1);
  }
  return(0);
}
SHAR_EOF
cat << \SHAR_EOF > sendpacket.c
/*
 *	Sendpacket.c 
 *
 *  An invaluable addition to your Amiga.lib file. This code sends a packet
 * the given message port. This makes working around DOS lots easier.
 * 
 * Note, I didn't write this, those wonderful folks at CBM did. I do suggest
 * however that you may wish to add it to Amiga.Lib, to do so, compile it
 * and say 'oml lib:amiga.lib -r sendpacket.o' 
 */

#include <exec/types.h>
#include <exec/ports.h>
#include <exec/memory.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>

/*
 * Function - SendPacket written by Phil Lindsay, Carolyn Scheppner, and
 * Andy Finkel. This function will send a packet of the given type to the
 * Message Port supplied.
 */

long
SendPacket(pid,action,args,nargs)

struct MsgPort *pid;  /* process indentifier ... (handlers message port ) */
long action,          /* packet type ... (what you want handler to do )   */
     args[],          /* a pointer to a argument list */
     nargs;           /* number of arguments in list  */
{
  struct MsgPort        *replyport;
  struct StandardPacket *packet;
 
  long  count, *pargs, res1;

  replyport = (struct MsgPort *) CreatePort(NULL,0);
  if(!replyport) return(0);

  /* Allocate space for a packet, make it public and clear it */
  packet = (struct StandardPacket *) 
    AllocMem((long)sizeof(struct StandardPacket),MEMF_PUBLIC|MEMF_CLEAR);
  if(!packet) {
    DeletePort(replyport);
    return(0);
  }

  packet->sp_Msg.mn_Node.ln_Name = (char *)&(packet->sp_Pkt);
  packet->sp_Pkt.dp_Link         = &(packet->sp_Msg);
  packet->sp_Pkt.dp_Port         = replyport;
  packet->sp_Pkt.dp_Type         = action;

  /* copy the args into the packet */
  pargs = &(packet->sp_Pkt.dp_Arg1);       /* address of first argument */
  for(count=0;count < nargs;count++) 
    pargs[count]=args[count];
 
  PutMsg(pid,packet); /* send packet */

  WaitPort(replyport);
  GetMsg(replyport); 

  res1 = packet->sp_Pkt.dp_Res1;

  FreeMem(packet,(long)sizeof(struct StandardPacket));
  DeletePort(replyport); 

  return(res1);
}
SHAR_EOF
#	End of shell archive
exit 0
--Chuck McManis
uucp: {anywhere}!sun!cmcmanis   BIX: cmcmanis  ARPAnet: cmcmanis@sun.com
These opinions are my own and no one elses, but you knew that didn't you.