[comp.os.vms] Reading single keystrokes - how do I cope with PF keys?

cameron@runx.ips.oz (James Cameron) (05/04/88)

I have a program  which  allows  users  to  control  their  local
printer  queue.   It  uses the SMG$ Run-Time Library routines for
screen control, the $GETQUI  system  service  for  obtaining  the
queue  contents,  and  the $SNDJBC system service for controlling
the queue (and jobs).

Commands are typed as single keystrokes - S to stop the queue,  G
to go, etc.  There is a VAX MACRO routine that issues the $QIO to
read a single character from SYS$INPUT. The $QIO  call  specifies
an  AST  routine  that  will  handle  the  character (it does the
$SNDJBC according to what was typed).  The AST  routine  requeues
the $QIO so as to catch the next character.

This is the $QIO used to read the first and next character;

        $QIO_S  chan=channel, func=#io$_readvblk!io$m_noecho, -
                iosb=iosb, astadr=vq_command_ast, astprm=#buffer, -
                p1=buffer, p2=#1 ; one character length

There's the background - the problem is the PF4  key.  It  causes
the  AST  routine to be called once for each character of the se-
quence <ESC>OS, which causes the queue to be stopped, since  nei-
ther <ESC> nor O are valid commands.  The PF4 key is used in most
of our systems as a get-out key. Users  are  accidently  stopping
their queues.

I've tried adding the function modifier IO$M_ESCAPE to the  $QIO,
but  that simply causes a %SYSTEM-F, Partial Escape status in the
IOSB.  I've tried enlarging the buffer so  that  the  escape  se-
quence can fit into it, but that causes the I/O to wait until the
buffer is filled before declaring the I/O complete (have to  type
SSSS or S followed by RETURN).  I know I could probably kludge it
so that the AST routine would parse the escape sequence - but I'd
rather do it some other way.

I thought that I could use an unsolicited data mailbox.  When the
program receives the unsolicited data message, it would issue the
$QIO to the terminal with a timeout value of zero, thus  catching
either  a  single character that the user typed or the escape se-
quence. But I have a worry about that - if the terminal  line  is
low  speed,  or a multiplexor causes the subsequent characters of
an escape sequence to arrive a bit later, will this cause the I/O
to  only pick up the first few characters of the sequence? Does a
read with timeout of zero only read  the  type-ahead  buffer?  Or
does  it  wait to see an entire escape sequence if it has noticed
an escape character?

I would appreciate any comments or help. Please reply by MAIL.  I
may eventually summarise the response.

James Cameron, Kilpatrick Green Pty. Ltd., P.O. Box N366, Sydney 2000 Australia
Internet: cameron@runx.ips.oz.au  UUCP: uunet!runx.ips.oz.au!cameron

u3369429@ucsvc.unimelb.edu.au (Michael Bednarek) (05/09/88)

In article <1501@runx.ips.oz>, cameron@runx.ips.oz (James Cameron) writes:
> 
> I have a program  which  allows  users  to  control  their  local
> printer  queue.   It  uses the SMG$ Run-Time Library routines for
> screen control, the $GETQUI  system  service  for  obtaining  the
> queue  contents,  and  the $SNDJBC system service for controlling
> the queue (and jobs).
> 
> Commands are typed as single keystrokes - S to stop the queue,  G
> to go, etc.  There is a VAX MACRO routine that issues the $QIO to
> read a single character from SYS$INPUT. [...]

The best thing would probably be to use SMG$ to read the keyboard.

Using only QIOs, here are three small Fortran programs.

	o Sense_Type_Ahead	! Sense Type-Ahead-Buffer 
	o Inkey0	! Read one character without echo from SYS$COMMAND
			! with a time-out value of zero
	o TInkey0	! Demonstrate usage of `Sense_Type_Ahead' and `Inkey0'

--
Michael Bednarek, Institute of Applied Economic and Social Research (IAESR)
   //  Melbourne University,Parkville 3052, AUSTRALIA, Phone:+61 3 344 5744
 \X/   Domain:u3369429@{murdu.oz.au | ucsvc.dn.mu.oz.au} | mb@munnari.oz.au
       "bang":...UUNET!munnari!murdu!u3369429     PSI%23343000301::U3369429
"POST NO BILLS."
>>>
	Options /Extend_Source
	Integer Function Sense_Type_Ahead(First)
	Implicit NONE

C** Sense Type-Ahead-Buffer from SYS$COMMAND		       Michael Bednarek

	Integer Status,Func,Channel,SYS$Assign,SYS$QIOW,SYS$DASSGN
	Character First
	Include   '($IODEF)/List'
	Parameter (Func=IO$_SenseMode.or.IO$M_TypeAhdCnt)

	Structure /Sense_Mode_Buffer/
	 Integer*2 Number
	 Character First
	 Byte	   %FILL(5)
	End Structure
	Record /Sense_Mode_Buffer/ P1

C** assign a channel to the terminal
	Status=SYS$Assign('SYS$COMMAND',Channel,,)
	If (.not.Status) Call LIB$Signal(%VAL(Status))

C** Sense Type-Ahead-Buffer
	Status=SYS$QIOW(,%VAL(Channel),
	1	%VAL(Func),,,,
	1	P1,%VAL(8),,,,)
	If (.not.Status) Call LIB$Signal(%VAL(Status))

	Sense_Type_Ahead=P1.Number
	First=P1.First

c** deassign the i/o channel
	Status=SYS$DASSGN(%VAL(Channel))
	If (.not.Status) Call LIB$Signal(%VAL(Status))

	Return
	End
>>>
	Options /Extend_Source
	Integer Function INKEY0 (c)
C** Read one character without echo from SYS$COMMAND	       Michael Bednarek
C   with a time-out value of zero

C Usage: Status=INKEY0(c)
C Returns character in `c', and QIOW's Status. If no character was read,
C Status is set to zero and `c' remains unchanged.

C Note: If function or arrow keys are pressed, each character of the
C	transmitted code is returned in successive calls.
C	E.g. pressing UP-ARROW will send the three characters `<ESC>[A'.

	Implicit	NONE
	Integer		Func,Status,Channel,SYS$Assign,SYS$QIOW,SYS$DASSGN
	Character	c

	Include   '($SSDEF)/List'
	Include   '($IODEF)/List'
	Parameter (Func=IO$_READVBLK.or.IO$M_NOECHO
	1	    .or.IO$M_NOFILTR.or.IO$M_TIMED)

	Structure /IOSB/
	 Integer*2 Value
	 Integer*2 Count
	 Integer*4 Dev_Spec
	End Structure
	Record /IOSB/ IOSB

C** assign a channel to the terminal
	Status=SYS$Assign('SYS$COMMAND',Channel,,)
	If (.not.Status) Call LIB$Signal(%VAL(Status))

C** read 1 character
	Status=SYS$QIOW(,%VAL(Channel),%VAL(Func),IOSB,,,
	2	%REF(c),%VAL(1),%VAL(0),,,)
	If (.not.Status) Call LIB$Signal(%VAL(Status))
	If (IOSB.Value.eq.SS$_TIMEOUT) then
	 INKEY0=0
	else
	 INKEY0=Status
	End If

c** deassign the i/o channel
	Status=SYS$DASSGN(%VAL(Channel))
	If (.not.Status) Call LIB$Signal(%VAL(Status))

	Return
	End
>>>
	Options /Extend_Source
	Program TInkey0

! Demonstrate usage of `Sense_Type_Ahead' and 'Inkey0':

	Implicit	Integer (A-z)
	Character	c

	Do while (1)
	 l=Sense_Type_Ahead(c)
	 If (l.gt.0) then
	  Write	(*,*) 'Sensed',l,' characters. First: >',c,'<',ICHAR(c)
	  Do i=1,l
	   Status=Inkey0(c)
	   If (Status.ne.0) Write (*,*) 'INKEY0 returns >',c,'<',ICHAR(c)
	  End Do
	 End If
	End Do

	End

rrk@byuvax.bitnet (05/10/88)

An easy solution to your problem was included in a recent (somewhere between
4.0 and 4.4) revision of the operating system.  I don't remember the exact
names of the codes.  Please refer to your I/O reference manual.  This much
I do remember.  You have to revise the form of your terminal QIO to use
the terminal QIO extended itemlist.  You can do this without changing the
basic way your QIO works--it is just a different way of passing parameters
because they ran out of parameter/qualifier space in the $QIO with only
six parameters.  Anyway, there is a code you can include to reserve N
characters of the buffer for receiving the rest of an escape sequence.
If you specify a buffer size of 20 and an escape overflow of 19, this will
cause any keystroke from the terminal to terminate the I/O, but if the key
generated an escape sequence, the entire sequence (up to 20 characters
including the escape) will be placed in your buffer.  I hope this helps.

                                AMMON::RAY