/*
**++
**  FACILITY:
**      NEWSMODE
**
**  ABSTRACT:
**      Map up and down arrow keys into NEWS, with an exit handler to restore
**      key settings
**
**  AUTHOR:
**      Geoff Huston
**
**  COPYRIGHT:
**      Copyright © 1988,1989,1990
**
**  VERSION:
**	V6.1b8	   Jul-1993	mark.martinec@ijs.si
**	  - Major rehash of code in this module
**	    (solving problems with SET LINE and SET NOBROADCAST);
**	    new functions: leave_screen & join_screen
**--
**/

#ifdef vaxc
#module NEWSMODE "V6.2"
#endif

#define _NEWSMODE_C
#define module_name "NEWSMODE"

#include "newsinclude.h"
#include "newsextern.h"

#if NAKED_INCLUDES
#include iodef
#include ttdef
#include tt2def
#else
#include <iodef.h>
#include <ttdef.h>
#include <tt2def.h>
#endif

static unsigned short  chan, iosb[4];
static unsigned int  devclass = 0;
static int
       chan_open = 0,
       tmp_char_defined = 0,
       tmp_char_buf[3],
       broadcast_trapping_set = 0,       /* broadcast trapping actually set ? */
       exit_reason = 0;
static struct { int reserved, (*handler) __ARGS((int *)), argc, *arg1; }
       exit_handler_block = {0,exit_mode,1,&exit_reason};


/*  open_tt_channel
 *
 *  Assign a channel to the controlling terminal if it has not been
 *  assigned before.  Assure the device type is really a terminal.
 *  On the first assignment read the device characteristics.
 *  Returns true if channel was successfully opened.
 */

int open_tt_channel(void)
{
    int status;

#ifdef __DECC
#pragma member_alignment save
#pragma nomember_alignment   /* no member alignment - this is a VMS structure */
#endif
    static struct
           { unsigned short buflen;
             unsigned short code;
             unsigned int * bufadr;
             unsigned int * rlenadr;
             unsigned int end_of_list; } itmlst
               = { sizeof devclass, DVI$_DEVCLASS, &devclass, 0, 0 };

    struct { unsigned char class, devtype;
             unsigned short page_width;
             unsigned char baschar[3];
             unsigned char page_length;
             unsigned int extchar; } char_buf;    /* terminal characteristics */
#ifdef __DECC
#pragma member_alignment restore
#endif

    if (!chan_open) {
      _c$cks(sys$assign(c$dsc("SYS$INPUT"),&chan,0,0)); chan_open = 1;
      if (!devclass) {
        _c$cks(sys$getdviw(0,chan,0,&itmlst,iosb,0,0,0));
        _c$cks(iosb[0]);
        }
      if (devclass != DC$_TERM)
        { _c$cks(sys$dassgn(chan)); chan_open = 0; }
      else if (!devcol || !devrow || !tmp_char_defined) {
        _c$cks(sys$qiow(0,chan,IO$_SENSEMODE,iosb,0,0,
                        &char_buf,sizeof char_buf,0,0,0,0));
        _c$cks(iosb[0]);
        if (!devcol) {
          devcol = (char_buf.page_width ? char_buf.page_width : 80);
          if (devcol) displ_subjlen = max(10, devcol-30);
          }
        if (!devrow) devrow = (char_buf.page_length? char_buf.page_length : 24);
        if (!tmp_char_defined) {
          tmp_char_buf[0] = * ((unsigned int *) &char_buf.class);
          tmp_char_buf[1] = * ((unsigned int *) &char_buf.baschar);
          tmp_char_buf[2] = * ((unsigned int *) &char_buf.extchar);
          tmp_char_defined = 1;
          }
        }
      }
    return chan_open;
}


/*
 *  exit_mode
 *
 *  Exit handler, to reset arrow key definition
 */

static int exit_mode(reason)
    int *reason;
{
    int status;
    unsigned short iosb[4];
    /* local copy of iosb to prevent conflict with iosb of the canceled QIO */

    if (!chan_open && (!devclass || devclass==DC$_TERM)) open_tt_channel();
    if (chan_open) {
      _c$cks(sys$cancel(chan));
      if (tmp_char_defined) {
        _c$cks(sys$qiow(0,chan,IO$_SETMODE,iosb,0,0,
                        tmp_char_buf,sizeof tmp_char_buf,0,0,0,0));
        _c$cks(iosb[0]);
        }
      }
    if (chan_open) { _c$cks(sys$dassgn(chan)); chan_open = 0; }
    return *reason;
}

unsigned int page_size()
{
    int status;

    if (devrow && devcol) return 0;
    if (!chan_open && (!devclass || devclass==DC$_TERM)) open_tt_channel();
    if (!devcol) { devcol = 80; displ_subjlen = devcol-30; }
    if (!devrow) { devrow = 24; }
    if (chan_open) { _c$cks(sys$dassgn(chan)); chan_open = 0; }
    return 1;
}

/*
 *  chg_mode
 *
 *  Turn off line edit mode to allow arrow keys to be sent to application
 */

void chg_mode()
{
    int status;

    _c$cks(sys$dclexh(&exit_handler_block));
    if (!chan_open && (!devclass || devclass==DC$_TERM)) open_tt_channel();
    if (chan_open) {
      _c$cks(sys$qiow(0,chan,IO$_SENSEMODE,iosb,0,0,
  	     tmp_char_buf,sizeof tmp_char_buf,0,0,0,0));
      _c$cks(iosb[0]);
      }
}

/*
 *  reset_mode
 *
 *  Revert mode to original, and cancel exit handler
 */

void reset_mode()
{
  int status;

  _c$cks(sys$canexh(&exit_handler_block));
  if (smg_active) {       /* keys are not necessarily defined - ignore status */
    smg$delete_key_def(&keytab,c$dsc("UP"),0);
    smg$delete_key_def(&keytab,c$dsc("DOWN"),0);
    smg$delete_key_def(&keytab,c$dsc("UP"),c$dsc("GOLD"));
    smg$delete_key_def(&keytab,c$dsc("DOWN"),c$dsc("GOLD"));
   }
  exit_reason = 1;
  exit_mode(&exit_reason);
}


void no_broad_trap()
{
  int status;

  if (smg_active && broadcast_trapping_set) {
    _c$cks(smg$disable_broadcast_trapping(&pid));
    broadcast_trapping_set = 0;
  }
  broadcast_trapping_requested = 0;
}

void broad_trap()
{
  int status;

  if (smg_active && !broadcast_trapping_set) {
    _c$cks(smg$set_broadcast_trapping(&pid,b_trap,0));
    broadcast_trapping_set = 1;
  }
  broadcast_trapping_requested = 1;
}

int set_broad()
{
  char sw[20];
  unsigned short sw_len;
  $DESCRIPTOR(sw_dsc,sw);

  if (cli$present(c$dsc("BOOL")) & 1) {
    if (cli$get_value(c$dsc("BOOL"),&sw_dsc,&sw_len) & 1) {
      sw[sw_len] = '\0';
      if (!strncmp(sw,"OF",2)) return(set_nobroad(),0);
      }
    }
  broad_trap();
  return(0);
}

int set_nobroad()
{
  no_broad_trap();
  return(0);
}


/*  set_line and set_noline are called from CLI
 */

int set_noline()
{
  line_editing = 0;
  return 0;
}

int set_line()
{
  char sw[20];
  unsigned short sw_len;
  $DESCRIPTOR(sw_dsc,sw);

  line_editing = 1;
  if (cli$present(c$dsc("BOOL")) & 1) {
    if (cli$get_value(c$dsc("BOOL"),&sw_dsc,&sw_len) & 1) {
      sw[sw_len] = '\0';
      if (!strncmp(sw,"OF",2)) line_editing = 0;
      }
    }
  return 0;
/* 
  This is weird.  As this routine is called from CLI$Dispatch
  it should return the VMS condition code (e.g. SS$_NORMAL),
  since CLI$Dispatch returns either its own condition code
  or the return value from the user action routine.
  ... but - if nonzero is returned, the main NEWS loop exits :-(
*/
}

int set_dotnewsrc()
{
  char sw[20];
  unsigned short sw_len;
  $DESCRIPTOR(sw_dsc,sw);

  usedotnewsrc = 1;
  if (cli$present(c$dsc("BOOL")) & 1) {
    if (cli$get_value(c$dsc("BOOL"),&sw_dsc,&sw_len) & 1) {
      sw[sw_len] = '\0';
      if (!strncmp(sw,"OF",2)) usedotnewsrc = 0;
      }
    }
  return 0;
}

int set_nodotnewsrc()
{
  usedotnewsrc = 0;
  return 0;
}


/*
 *  leave_screen
 *
 *  Save screen state and prepare terminal for a non-smg operation
 *  (e.g. spawning an editor).  If erase is true the screen is erased.
 *
 *  Should be called regardless of whether the SMG is active or not.
 *
 *  Function returns the SMG id of a saved physical display
 *  (or 0 if SMG is not active) - this id should be supplied
 *  to join_screen() after the non-smg task has completed.
 */

int leave_screen(erase)
  int erase;
{
  int status;
  int saved_display_id = 0;

  if (smg_active) {
    display_brdcst(2);
    no_broad_trap();
    _c$cks(smg$end_pasteboard_update(&pid));
    _c$cks(smg$save_physical_screen(&pid,&saved_display_id));
    if (erase) _c$cks(smg$erase_pasteboard(&pid));
    chg_mode();
    }
  return saved_display_id;
}

/*
 *  join_screen
 *
 *  Restores the screen environment which might have been destroyed
 *  by a non-smg task.  The saved_display_id must be the value
 *  returned by leave_screen().
 */

void join_screen(saved_display_id)
  int saved_display_id;
{
  int status;

  if (smg_active) {
    reset_mode();
    _c$cks(smg$restore_physical_screen(&pid,&saved_display_id));
    _c$cks(smg$begin_pasteboard_update(&pid));
    broad_trap();
    }
}
