sldisply.c

来自「这是一个开放源代码的与WINNT/WIN2K/WIN2003兼容的操作系统」· C语言 代码 · 共 2,241 行 · 第 1/4 页

C
2,241
字号
/* Copyright (c) 1992, 1995 John E. Davis
 * All rights reserved.
 *
 * You may distribute under the terms of either the GNU General Public
 * License or the Perl Artistic License.
 */

/*
 * SLTT_TRANSP_ACS_PATCH (#define/#undef):
 *
 *  The problem: some terminals (e.g. QNX/qansi*) map the whole upper half of
 *  the ASCII table to the lower half, when alt-char-set is activated with
 *  the smacs/as string-sequence. This means, that if 0 <= ch < 128 written
 *  to the terminal, it will be translated to (ch+128) automatically by the
 *  terminal: so not only the line-drawing characters can be written, when
 *  the alt-char-set is activated. It implicitly means, that space, NL, CR,
 *  etc. characters (exactly: anything besides the "standard" line drawing
 *  characters) can not be written directly to the terminal, when the
 *  alt-char-set is activated, because writing these characters doesn't cause
 *  an implicit/temporary switching-back to the standard char-set!
 *
 *  The original code in SLang assumes that space, NL, CR, etc. can be
 *  printed when alt-char-set is activated. If SLTT_TRANSP_ACS_PATCH is
 *  defined, the modified code will not use this assumption.
 *  [Remark: the patch-code is not the most exact solution, but works...]
 */
/*#define SLTT_TRANSP_ACS_PATCH 1*/

/*
 * QNX_QANSI_SLANG_COMPAT_ACS (#define/#undef):
 *
 *  A more OS/terminal-specific solution for the problem mentioned above
 *  (->SLTT_TRANSP_ACS_PATCH).
 *
 *  If QNX_QANSI_SLANG_COMPAT_ACS is defined, the default smacs/sa, rmacs/ae,
 *  acsc/ac [and sgr/sa, if it would be used!] command sequences will be
 *  replaced internally with the "old style" (pre-QNX 4.23) sequences in case
 *  of QNX/qansi terminals. Using these optional command sequences the terminal
 *  remains compatible with the original SLang code (without using the
 *  workaround-code enabled by defining SLTT_TRANSP_ACS_PATCH).
 */
/*#define QNX_QANSI_SLANG_COMPAT_ACS 1*/

/* auto-configuration */
#ifdef SLTT_TRANSP_ACS_PATCH
# if defined(__QNX__) && defined(QNX_QANSI_SLANG_COMPAT_ACS)
#  undef SLTT_TRANSP_ACS_PATCH
# endif
#else
# if defined(__QNX__) && !defined(QNX_QANSI_SLANG_COMPAT_ACS)
#  define QNX_QANSI_SLANG_COMPAT_ACS 1
# endif
#endif

#include "sl-feat.h"
#include "config.h"

#include <stdio.h>
#include <string.h>
#ifdef SCO_FLAVOR
#	include <sys/types.h>
#	include <sys/timeb.h> /* for struct timeb, used in time.h */
#endif
#include <time.h>
#include <ctype.h>

#ifndef VMS
# include <sys/time.h>
# ifdef __QNX__
#   include <sys/select.h>
# endif
# include <sys/types.h>
#endif

#ifdef __BEOS__
/* Prototype for select */
# include <net/socket.h>
#endif

#ifdef HAVE_TERMIOS_H
# include <termios.h>
#endif

#ifdef VMS
# include <unixlib.h>
# include <unixio.h>
# include <dvidef.h>
# include <descrip.h>
# include <lib$routines.h>
# include <starlet.h>
#else
# if !defined(sun)
#  include <sys/ioctl.h>
# endif
#endif


#ifdef SYSV
# include <sys/termio.h>
# include <sys/stream.h>
# include <sys/ptem.h>
# include <sys/tty.h>
#endif

#if defined (_AIX) && !defined (FD_SET)
# include <sys/select.h>	/* for FD_ISSET, FD_SET, FD_ZERO */
#endif

#include <errno.h>
#include "slang.h"
#include "_slang.h"

#ifdef HAVE_STDLIB_H
# include <stdlib.h>
#endif

#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif

#if defined(__DECC) && defined(VMS)
/* These get prototypes for write an sleep */
# include <unixio.h>
#endif
#include <signal.h>


/* Colors:  These definitions are used for the display.  However, the
 * application only uses object handles which get mapped to this
 * internal representation.  The mapping is performed by the Color_Map
 * structure below. */

#define CHAR_MASK	0x000000FF
#define FG_MASK		0x0000FF00
#define BG_MASK		0x00FF0000
#define ATTR_MASK	0x1F000000
#define BGALL_MASK	0x0FFF0000

/* The 0x10000000 bit represents the alternate character set.  BGALL_MASK does
 * not include this attribute.
 */



#define GET_FG(color) ((color & FG_MASK) >> 8)
#define GET_BG(color) ((color & BG_MASK) >> 16)
#define MAKE_COLOR(fg, bg) (((fg) | ((bg) << 8)) << 8)

int SLtt_Screen_Cols;
int SLtt_Screen_Rows;
int SLtt_Term_Cannot_Insert;
int SLtt_Term_Cannot_Scroll;
int SLtt_Use_Ansi_Colors;
int SLtt_Blink_Mode = 1;
int SLtt_Use_Blink_For_ACS = 0;
int SLtt_Newline_Ok = 0;
int SLtt_Has_Alt_Charset = 0;
int SLtt_Force_Keypad_Init = 0;

/* -1 means unknown */
int SLtt_Has_Status_Line = -1;	       /* hs */

static int Automatic_Margins;
/* static int No_Move_In_Standout; */
static int Worthless_Highlight;
#define HP_GLITCH_CODE
#ifdef HP_GLITCH_CODE
/* This glitch is exclusive to HP term.  Basically it means that to clear
 * attributes, one has to erase to the end of the line.
 */
static int Has_HP_Glitch;
#endif

static char *Reset_Color_String;

static int Linux_Console;

/* It is crucial that JMAX_COLORS must be less than 128 since the high bit
 * is used to indicate a character from the ACS (alt char set).  The exception
 * to this rule is if SLtt_Use_Blink_For_ACS is true.  This means that of
 * the highbit is set, we interpret that as a blink character.  This is
 * exploited by DOSemu.
 */
#define JMAX_COLORS 256
#define JNORMAL_COLOR 0

typedef struct
{
   SLtt_Char_Type fgbg;
   SLtt_Char_Type mono;
   char *custom_esc;
} Ansi_Color_Type;

#define RGB1(r, g, b)   ((r) | ((g) << 1) | ((b) << 2))
#define RGB(r, g, b, br, bg, bb)  ((RGB1(r, g, b) << 8) | (RGB1(br, bg, bb) << 16))

static Ansi_Color_Type Ansi_Color_Map[JMAX_COLORS] =
{
   {RGB(1, 1, 1, 0, 0, 0), 0x00000000, NULL},   /* white/black */
   {RGB(0, 1, 0, 0, 0, 0), SLTT_REV_MASK, NULL},   /* green/black */
   {RGB(1, 0, 1, 0, 0, 0), SLTT_REV_MASK, NULL},   /* magenta/black */
   {RGB(0, 1, 1, 0, 0, 0), SLTT_REV_MASK, NULL},   /* cyan/black */
   {RGB(1, 0, 0, 0, 0, 0), SLTT_REV_MASK, NULL},
   {RGB(0, 1, 0, 0, 0, 1), SLTT_REV_MASK, NULL},
   {RGB(1, 0, 0, 0, 0, 1), SLTT_REV_MASK, NULL},
   {RGB(1, 0, 0, 0, 1, 0), SLTT_REV_MASK, NULL},
   {RGB(0, 0, 1, 1, 0, 0), SLTT_REV_MASK, NULL},
   {RGB(0, 1, 0, 1, 0, 0), SLTT_REV_MASK, NULL},
   {RGB(0, 1, 1, 1, 1, 1), SLTT_REV_MASK, NULL},
   {RGB(1, 1, 0, 1, 1, 1), SLTT_REV_MASK, NULL},
   {RGB(1, 0, 1, 1, 1, 1), SLTT_REV_MASK, NULL},
   {RGB(0, 0, 0, 0, 1, 1), SLTT_REV_MASK, NULL},
   {RGB(0, 1, 0, 1, 1, 1), SLTT_REV_MASK, NULL},
   {RGB(0, 1, 0, 1, 1, 1), SLTT_REV_MASK, NULL},
   {RGB(0, 1, 0, 1, 1, 1), SLTT_REV_MASK, NULL},
   {RGB(0, 1, 0, 1, 1, 1), SLTT_REV_MASK, NULL}
};


/* This is the string to use to use when outputting color information.
 */
#ifdef M_UNIX
/* work around for sco console bug that can't handle combined sequences */
  static char *Color_Escape_Sequence = "\033[3%dm\033[4%dm";
#else
/* Believe it or not, this is what is in the linux terminfo database.  It
 * produces the same escape sequence but it is much more CPU intensive.
 * Why not just encode it as "\033[3%p1%dm\033[4%p2%dm" ???
 */
 /* static char *Color_Escape_Sequence = "\033[%p1%{30}%+%dm\033[%p2%{40}%+%dm"; */
static char *Color_Escape_Sequence = "\033[3%d;4%dm";
#endif

char *SLtt_Graphics_Char_Pairs;	       /* ac termcap string -- def is vt100 */


/* 1 if terminal lacks the ability to do into insert mode or into delete
   mode. Currently controlled by S-Lang but later perhaps termcap. */

static char *UnderLine_Vid_Str;
static char *Blink_Vid_Str;
static char *Bold_Vid_Str;
static char *Ins_Mode_Str; /* = "\033[4h"; */   /* ins mode (im) */
static char *Eins_Mode_Str; /* = "\033[4l"; */  /* end ins mode (ei) */
static char *Scroll_R_Str; /* = "\033[%d;%dr"; */ /* scroll region */
static char *Cls_Str; /* = "\033[2J\033[H"; */  /* cl termcap STR  for ansi terminals */
static char *Rev_Vid_Str; /* = "\033[7m"; */    /* mr,so termcap string */
static char *Norm_Vid_Str; /* = "\033[m"; */   /* me,se termcap string */
static char *Del_Eol_Str; /* = "\033[K"; */	       /* ce */
static char *Del_Char_Str; /* = "\033[P"; */   /* dc */
static char *Del_N_Lines_Str; /* = "\033[%dM"; */  /* DL */
static char *Add_N_Lines_Str; /* = "\033[%dL"; */  /* AL */
static char *Rev_Scroll_Str;
static char *Curs_Up_Str;
static char *Curs_F_Str;    /* RI termcap string */
static char *Cursor_Visible_Str;    /* ve termcap string */
static char *Cursor_Invisible_Str;    /* vi termcap string */

static char *Start_Alt_Chars_Str;  /* as */
static char *End_Alt_Chars_Str;   /* ae */
static char *Enable_Alt_Char_Set;  /* eA */

static char *Term_Init_Str;
static char *Keypad_Init_Str;
static char *Term_Reset_Str;
static char *Keypad_Reset_Str;

/* status line functions */
static char *Disable_Status_line_Str;  /* ds */
static char *Return_From_Status_Line_Str;   /* fs */
static char *Goto_Status_Line_Str;     /* ts */
static int Num_Status_Line_Columns;    /* ws */
static int Status_Line_Esc_Ok;	       /* es */

/* static int Len_Curs_F_Str = 5; */

/* cm string has %i%d since termcap numbers columns from 0 */
/* char *CURS_POS_STR = "\033[%d;%df";  ansi-- hor and vert pos */
static char *Curs_Pos_Str; /* = "\033[%i%d;%dH";*/   /* cm termcap string */


/* scrolling region */
static int Scroll_r1 = 0, Scroll_r2 = 23;
static int Cursor_r, Cursor_c;	       /* 0 based */

/* current attributes --- initialized to impossible value */
static SLtt_Char_Type Current_Fgbg = 0xFFFFFFFFU;

static int Cursor_Set;		       /* 1 if cursor position known, 0
					* if not.  -1 if only row is known
					*/


#define MAX_OUTPUT_BUFFER_SIZE 4096

static unsigned char Output_Buffer[MAX_OUTPUT_BUFFER_SIZE];
static unsigned char *Output_Bufferp = Output_Buffer;

unsigned long SLtt_Num_Chars_Output;

#ifdef SLTT_TRANSP_ACS_PATCH
static int SLtt_ACS_Active = 0;
#endif

static int sl_usleep (unsigned long usecs)
{
#ifndef VMS
   struct timeval tv;
   tv.tv_sec = usecs / 1000000;
   tv.tv_usec = usecs % 1000000;
   return select(0, NULL, NULL, NULL, &tv);
#else
   return 0;
#endif
}

int SLtt_flush_output (void)
{
   int nwrite = 0;
   unsigned int total;
   int n = (int) (Output_Bufferp - Output_Buffer);

   SLtt_Num_Chars_Output += n;

   total = 0;
   while (n > 0)
     {
	nwrite = write (fileno(stdout), (char *) Output_Buffer + total, n);
	if (nwrite == -1)
	  {
	     nwrite = 0;
#ifdef EAGAIN
	     if (errno == EAGAIN)
	       {
		  sl_usleep (100000);   /* 1/10 sec */
		  continue;
	       }
#endif
#ifdef EWOULDBLOCK
	     if (errno == EWOULDBLOCK)
	       {
		  sl_usleep (100000);
		  continue;
	       }
#endif
#ifdef EINTR
	     if (errno == EINTR) continue;
#endif
	     break;
	  }
	n -= nwrite;
	total += nwrite;
     }
   Output_Bufferp = Output_Buffer;
   return n;
}


int SLtt_Baud_Rate;
static void tt_write(char *str, unsigned int n)
{
   static unsigned long last_time;
   static int total;
   unsigned long now;
   unsigned int ndiff;

   if ((str == NULL) || (n == 0)) return;
   total += n;

   while (1)
     {
	ndiff = MAX_OUTPUT_BUFFER_SIZE - (int) (Output_Bufferp - Output_Buffer);
	if (ndiff < n)
	  {
	     SLMEMCPY ((char *) Output_Bufferp, (char *) str, ndiff);
	     Output_Bufferp += ndiff;
	     SLtt_flush_output ();
	     n -= ndiff;
	     str += ndiff;
	  }
	else
	  {
	     SLMEMCPY ((char *) Output_Bufferp, str, n);
	     Output_Bufferp += n;
	     break;
	  }
     }

   if (((SLtt_Baud_Rate > 150) && (SLtt_Baud_Rate <= 9600))
       && (10 * total > SLtt_Baud_Rate))
     {
	total = 0;
	if ((now = (unsigned long) time(NULL)) - last_time <= 1)
	  {
	     SLtt_flush_output ();
	     sleep((unsigned) 1);
	  }
	last_time = now;
     }
}


void SLtt_write_string (char *str)
{
   if (str != NULL) tt_write(str, strlen(str));
}


void SLtt_putchar (char ch)
{
#ifdef SLTT_TRANSP_ACS_PATCH
   int restore_acs = 0;
#endif

   SLtt_normal_video ();
   if (Cursor_Set == 1)
     {
	if (ch >= ' ') Cursor_c++;
#ifndef SLTT_TRANSP_ACS_PATCH
	else if (ch == '\b') Cursor_c--;
#else
	if (ch <= ' ' && SLtt_ACS_Active)
	  {
	     SLtt_set_alt_char_set (0);
	     restore_acs = 1;
	  }
	if (ch == '\b') Cursor_c--;
#endif
	else if (ch == '\r') Cursor_c = 0;
	else Cursor_Set = 0;

	if ((Cursor_c + 1 == SLtt_Screen_Cols)
	    && Automatic_Margins) Cursor_Set = 0;
     }

   if (Output_Bufferp < Output_Buffer + MAX_OUTPUT_BUFFER_SIZE)
     {
	*Output_Bufferp++ = (unsigned char) ch;
     }
   else tt_write (&ch, 1);

#ifdef SLTT_TRANSP_ACS_PATCH
   if (restore_acs)
     {
        SLtt_set_alt_char_set (1);
     }
#endif
}

/* this is supposed to be fast--- also handles
   termcap: %d, &i, %., %+, %r strings as well as terminfo stuff */
static unsigned int tt_sprintf(char *buf, char *fmt, int x, int y)
{
   register unsigned char *f = (unsigned char *) fmt, *b, ch;
   int offset = 0, tinfo = 0;
   int stack[10];
   int i = 0, z;
   stack[0] = y; stack[1] = x; i = 2;

   b = (unsigned char *) buf;
   if (fmt != NULL) while ((ch = *f++) != 0)
     {
	if (ch != '%') *b++ = ch;
	else
	  {
	     ch = *f++;
	     if (tinfo)
	       {
		  if ((ch <= '3') && (ch >= '0'))
		    {
		       /* map it to termcap.  Since this is terminfo,
			* it must be one of:
			*   %2d, %3d, %02d, %03d
			*
			* I am assuming that a terminal that understands
			* %2d form will also understand the %02d form.  These
			* only differ by a space padding the field.
			*/

		       /* skip the 'd'-- hope it is there */
		       if (ch == '0')
			 {
			    ch = *f;
			    f += 2;
			 }
		       else f++;
		    }
	       }

	     switch (ch)
	       {
		case 'p':
		  tinfo = 1;
		  ch = *f++;
		  if (ch == '1') stack[i++] = x; else stack[i++] = y;
		  break;

		case '\'':   /* 'x' */
		  stack[i++] = *f++;
		  f++;
		  break;

		case '{':	       /* literal constant, e.g. {30} */
		  z = 0;
		  while (((ch = *f) <= '9') && (ch >= '0'))
		    {
		       z = z * 10 + (ch - '0');
		       f++;
		    }
		  stack[i++] = z;
		  if (ch == '}') f++;
		  break;

		case 'd':
		case '2':
		case '3':
		  z = stack[--i];
		  z += offset;
		  if (z >= 100)
		    {
		       *b++ = z / 100 + '0';
		       z = z % 100;
		       goto ten;
		    }
		  else if (ch == 3)
		    {
		       *b++ = '0';
		       ch = '2';
		    }

		  if (z >= 10)
		    {
		       ten:
		       *b++ = z / 10 + '0';
		       z = z % 10;
		    }
		  else if (ch == 2) *b++ = '0';

		  *b++ = z + '0';
		  break;

		case 'i':
		  offset = 1;
		  break;

		case '+':
		  if (tinfo)
		    {
		       z = stack[--i];
		       stack[i-1] += z;
		    }
		  else
		    {
		       ch = *f++;
		       if ((unsigned char) ch == 128) ch = 0;
		       ch = ch + (unsigned char) stack[--i];
		       if (ch == '\n') ch++;
		       *b++ = ch;
		    }
		  break;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?