/* PC terminal support file.

   This file originally contained code to do direct screen writes, but it has
   since grown into an all-encompassing behemoth, dealing with all things
   "PC terminals".

   The direct screen support for PC's was originally done by:
   Author: Chris Boucher (ccb@soton.ac.uk).
	   Copyright (C) 1993 Chris Boucher.
	   Modified by Darryl Okahata (darrylo@sr.hp.com) to work with
	   GNU Emacs V19, June 1993.

This file is part of GNU Emacs.

GNU Emacs is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 1, or (at your option)
any later version.

GNU Emacs is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with GNU Emacs; see the file COPYING.  If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */

 
#include "config.h"

#if defined(MSDOS) && !defined(DVX)

#include <stdio.h>
#include <string.h>
#ifndef __WATCOMC__
# include <sys/time.h>
#endif
#include <malloc.h>
#include <dos.h>

#include "lisp.h"
#include "dispextern.h"
#include "termhooks.h"
#include "termopts.h"
#include "termchar.h"
#include "frame.h"
#include "systime.h"

#include "dosfns.h"
#include "pccompat.h"

#ifdef MULTI_FRAME
This code will probably not work with multiple frames!
#endif

static unsigned long	fake_event_time;

#ifdef PC_MOUSE
/* `t' if a mouse button is depressed. */

Lisp_Object Vmouse_depressed;

/* Any buttons grabbed. */
static unsigned int x_mouse_grabbed;

/* The window system handling code should set this if the mouse has
   moved since the last call to the mouse_position_hook.  Calling that
   hook should clear this.  Code assumes that if this is set, it can
   call mouse_position_hook to get the promised position, so don't set
   it unless you're prepared to substantiate the claim!	 */
extern int mouse_moved;


#define CONVERT_COLUMN_COORDINATE(x)	((x) >> 3);
#define CONVERT_ROW_COORDINATE(y)	((y) >> 3);

unsigned char		inhibit_pc_mouse = 1;
static unsigned char	mouse_present;
static unsigned char	mouse_visible;
static int		mouse_x, mouse_y, mouse_buttons, new_mouse_buttons;
static unsigned char	mouse_buttons_changed;
static int		mouse_screen_mask, mouse_cursor_mask;


static void get_raw_mouse_coordinates(x, y, buttons)
     int	*x, *y, *buttons;
{
  REGS_TYPE	regs;

  *x = *y = *buttons = 0;
  if (mouse_present && mouse_visible) {
    memset(&regs, 0, sizeof(regs));
    REGISTER_AX(regs) = 3;
    INT(0x33, &regs, &regs);
    *buttons = REGISTER_BX(regs) & 0x07;  /* we try to handle 3-button mice! */
    *x = CONVERT_COLUMN_COORDINATE(REGISTER_CX(regs));
    *y = CONVERT_ROW_COORDINATE(REGISTER_DX(regs));
  }
}


void display_pc_mouse()
{
  REGS_TYPE	regs;

  if (mouse_present && !mouse_visible) {
    memset(&regs, 0, sizeof(regs));
    REGISTER_AX(regs) = 1;
    INT(0x33, &regs, &regs);
    mouse_visible = 1;
  }
}


hide_pc_mouse()
{
  REGS_TYPE	regs;
  int old_mouse_visible = mouse_visible;

  if (mouse_present && mouse_visible) {
    memset(&regs, 0, sizeof(regs));
    REGISTER_AX(regs) = 2;
    INT(0x33, &regs, &regs);
    mouse_visible = 0;
  }
  return (old_mouse_visible);
}


static reset_mouse(hardware)
int	hardware;
{
  REGS_TYPE	regs;
  int		reset_status;

  /*
   * First reset the mouse
   */
  memset(&regs, 0, sizeof(regs));
  if (hardware) {
    REGISTER_AX(regs) = 0x0000;
  } else {
    /* Software reset */
    REGISTER_AX(regs) = 0x0021;
  }
  INT(0x33, &regs, &regs);
  reset_status = REGISTER_AX(regs);
  if (reset_status)
    {
      /*
       * Set mouse mask types
       */
      memset(&regs, 0, sizeof(regs));
      REGISTER_AX(regs) = 0x000a;
      REGISTER_BX(regs) = 0;		/* software text cursor */
      REGISTER_CX(regs) = mouse_screen_mask;
      REGISTER_DX(regs) = mouse_cursor_mask;
      INT(0x33, &regs, &regs);
      /*
       * Set the horizontal cursor range
       */
      memset(&regs, 0, sizeof(regs));
      REGISTER_AX(regs) = 0x0007;
      REGISTER_CX(regs) = 0;
      REGISTER_DX(regs) = (ScreenCols() - 1) * 8;
      INT(0x33, &regs, &regs);
      /*
       * Set the vertical cursor range
       */
      memset(&regs, 0, sizeof(regs));
      REGISTER_AX(regs) = 0x0008;
      REGISTER_CX(regs) = 0;
      REGISTER_DX(regs) = (ScreenRows() - 1) * 8;
      INT(0x33, &regs, &regs);
    }
  return (reset_status);
}


void hard_reset_pc_mouse()
{
  unsigned char		old_mouse_visible;

  if (mouse_present) {
    reset_mouse(1);
    old_mouse_visible = mouse_visible;
    mouse_visible = 1;	/* fake out get_raw_mouse_coordinates() */
    get_raw_mouse_coordinates(&mouse_x, &mouse_y, &mouse_buttons);
    mouse_buttons_changed = 0;
    mouse_visible = old_mouse_visible;
    if (!mouse_visible) {
      /* restore old mouse visibility state */
      hide_pc_mouse();
    }
  }
}


void init_pc_mouse()
{
  static char	hardware_initialized;
  int		present;

  mouse_present = 0;
  if (!inhibit_pc_mouse) {
    present = reset_mouse(!hardware_initialized);
    hardware_initialized = 1;
    if (present) {
      mouse_present = 1;
      mouse_visible = 1;	/* fake out get_raw_mouse_coordinates() */
      get_raw_mouse_coordinates(&mouse_x, &mouse_y, &mouse_buttons);
      mouse_buttons_changed = 0;
    }
  }
  mouse_visible = 0;
}


void uninit_pc_mouse()
{
  if (mouse_present) {
    reset_mouse(0);
    hide_pc_mouse();
    mouse_visible = 0;
  }
}


static pc_mouse_input()
{
  int	x, y;

  if (!mouse_present || !mouse_visible || inhibit_pc_mouse) {
    return (0);
  }
  get_raw_mouse_coordinates(&x, &y, &new_mouse_buttons);
  if (x < 0)
    x = 0;
  if (x > FRAME_WIDTH(selected_frame) - 1)
    x = FRAME_WIDTH(selected_frame) - 1;
  if (y < 0)
    y = 0;
  if (y > FRAME_HEIGHT(selected_frame) - 1)
    y = FRAME_HEIGHT(selected_frame) - 1;
  if (x != mouse_x || y != mouse_y) {
    mouse_moved = 1;
  }
  if (new_mouse_buttons != mouse_buttons) {
    mouse_buttons_changed = 1;
  }
  mouse_x = x;
  mouse_y = y;
  return (mouse_moved || mouse_buttons_changed);
}


static check_mouse_button_event(button, event)
int			button;
struct input_event	*event;
{
  unsigned char		changed, button_press;
  int			mask, dos_modifiers, modifiers;

  switch (button) {
  case 1:	/* left button */
    mask = 1;
    break;
  case 2:	/* middle button??? */
    mask = 4;
    break;
  case 3:	/* right button */
    mask = 2;
    break;
  default:
    abort();	/* huh? */
    break;
  }
  changed = (new_mouse_buttons ^ mouse_buttons) & mask;
  button_press = new_mouse_buttons & mask;
  if (!changed)
    return (0);

  modifiers = 0;
  dos_modifiers = dos_get_modifiers();
  if (dos_modifiers & (M_Shift_L | M_Shift_R))
    modifiers |= shift_modifier;
  if (dos_modifiers & M_Control)
    modifiers |= ctrl_modifier;
  if (dos_modifiers & M_Alt)
    modifiers |= meta_modifier;		/* The "Alt" key is a meta key, and not
					   an Alt key */
  modifiers |= button_press ? down_modifier : up_modifier;

  event->kind = mouse_click;
  XSET (event->code, Lisp_Int, button - 1);
  event->timestamp = ++fake_event_time;
  event->modifiers = modifiers;
  XFASTINT (event->x) = mouse_x;
  XFASTINT (event->y) = mouse_y;
  event->frame_or_window = Qt;

  if (button_press) {
    if (! x_mouse_grabbed)
      Vmouse_depressed = Qt;
    x_mouse_grabbed |= (1 << (button - 1));
  } else {
    x_mouse_grabbed &= ~(1 << (button - 1));
    if (!x_mouse_grabbed)
      Vmouse_depressed = Qnil;
  }
  mouse_buttons = new_mouse_buttons;

  return (1);
}


static void PC_mouse_position(f, bar_window, part, x, y, time)
     FRAME_PTR *f;
     Lisp_Object *bar_window;
     enum scroll_bar_part *part;
     Lisp_Object *x, *y;
     unsigned long *time;
{
  *f = 0;
  *bar_window = Qnil;
  *part = 0;
  *x = *y = 0;
  *time = 0;
  mouse_moved = 0;
  if (!mouse_present || !mouse_visible || inhibit_pc_mouse) {
    return;
  }
  *f = selected_frame;
  XSETINT(*x, mouse_x);
  XSETINT(*y, mouse_y);
  *time = ++fake_event_time;
}

static void set_mouse_cursor_mask(new_screen_mask, new_cursor_mask)
int	new_screen_mask, new_cursor_mask;
{
  REGS_TYPE	regs;

  mouse_screen_mask = new_screen_mask & 0xffff;
  mouse_cursor_mask = new_cursor_mask & 0xffff;
  if (mouse_present && mouse_visible && !inhibit_pc_mouse)
    {
      memset(&regs, 0, sizeof(regs));
      REGISTER_AX(regs) = 0x000a;
      REGISTER_BX(regs) = 0;		/* software text cursor */
      REGISTER_CX(regs) = mouse_screen_mask;
      REGISTER_DX(regs) = mouse_cursor_mask;
      INT(0x33, &regs, &regs);
    }
}


DEFUN ("emacs-pc-enable-mouse", Femacs_pc_enable_mouse, Semacs_pc_enable_mouse,
       0, 0, "",
       "Enable the PC mouse, if present.")
     ()
{
  inhibit_pc_mouse = 0;
  init_pc_mouse();
  if (mouse_present)
    return (Qt);
  return (Qnil);
}


DEFUN ("emacs-pc-disable-mouse", Femacs_pc_disable_mouse,
       Semacs_pc_disable_mouse, 0, 0, "",
       "Disable the PC mouse, if present.")
     ()
{
  uninit_pc_mouse();
  inhibit_pc_mouse = 1;
  if (mouse_present)
    return (Qt);
  return (Qnil);
}

DEFUN ("emacs-pc-set-mouse-cursor", Femacs_pc_set_mouse_cursor,
       Semacs_pc_set_mouse_cursor, 2, 2, "nScreen mask: \nnCursor mask: ",
       "Set the mouse cursor attributes.\n\
The first argument is the screen mask, and the second is the cursor mask.\n\
Both of these are integers.")
     (screen_mask, cursor_mask)
{
  CHECK_NUMBER(screen_mask, 0);
  CHECK_NUMBER(cursor_mask, 1);
  set_mouse_cursor_mask (screen_mask, cursor_mask);  
  return (Qnil);
}

#endif	/* PC_MOUSE */


/*
 * Main MSDOS input routines
 */

static int PC_input_hook (sd, buf, numchars, waitp, expected)
     register int sd;			/* NOT USED */
     register struct input_event *buf;
     register int numchars;
     int waitp;				/* NOT USED */
     int expected;			/* NOT USED */
{
  int	ch, nread;

#if defined(__GO32__) && defined(CTRL_BREAK_INTERRUPTS)
  check_go32_break(1);
#endif

  nread = 0;

#ifdef PC_MOUSE
  if (pc_mouse_input()) {
    if (mouse_buttons_changed) {
      if (check_mouse_button_event(1, &buf[nread]))
	nread++;
      if (check_mouse_button_event(2, &buf[nread]))
	nread++;
      if (check_mouse_button_event(3, &buf[nread]))
	nread++;
      mouse_buttons_changed = 0;
    }
  }
#endif

  while (1)
    {
      if (!dos_keysns())
	break;
      ch = dos_keyread();
      buf[nread].kind = ascii_keystroke;
      buf[nread].modifiers = 0;
      if (meta_key == 1 && (ch & 0x80))
	buf[nread].modifiers = meta_modifier;
      if (meta_key != 2)
	ch &= ~0x80;

      XSET (buf[nread].code, Lisp_Int, ch);
#ifdef MULTI_FRAME
      XSET (buf[nread].frame_or_window, Lisp_Frame, selected_frame);
#else
      buf[nread].frame_or_window = Qnil;
#endif

      if (++nread >= numchars)	/* Don't read more than we can
				   handle */
	break;
    }
  return (nread);
}


input_wait_timeout (timeval)
     int timeval;		/* Time to wait, in seconds */
{
  time_t tnow, tthen;
#ifdef PC_MOUSE
  extern int do_mouse_tracking;
#endif

  if (detect_input_pending ())
    return;

#ifdef __WATCOMC__
  {
    clock_t	tnow, tthen;

    tnow = clock();
    tthen = tnow + timeval * CLOCKS_PER_SEC;
    while (!dos_keysns() && NILP (Vquit_flag)
#ifdef PC_MOUSE
	   && !mouse_buttons_changed && (!do_mouse_tracking || !mouse_moved)
#endif
	   )
      {
#ifdef PC_MOUSE
	pc_mouse_input();
#endif
	if (timeval != 0)
	  {
	    tnow = clock();
	    if (tnow >= tthen)
	      break;
	  }
      }
  }
#else	/* not __WATCOMC__  */
  {
    struct timeval	tstart, tnow, tdiff;

    gettimeofday(&tstart, (struct timezone *) 0);
    while (!dos_keysns() && NILP (Vquit_flag)
#ifdef PC_MOUSE
	   && !mouse_buttons_changed && (!do_mouse_tracking || !mouse_moved)
#endif
	   )
      {
#ifdef PC_MOUSE
	pc_mouse_input();
#endif
#if defined(__GO32__) && defined(CTRL_BREAK_INTERRUPTS)
	check_go32_break(1);
#endif
	if (timeval != 0)
	  {
	    gettimeofday(&tnow, (struct timezone *) 0);
	    timeval_subtract(&tdiff, tnow, tstart);
	    if (tdiff.tv_sec >= timeval)
	      break;
	  }
      }
  }
#endif	/* not __WATCOMC__  */
}


wait_for_dos_input(timeout_p)
EMACS_TIME	*timeout_p;
{
  int		timeout;

  timeout = 0;
  if (timeout_p) {
    timeout = EMACS_SECS(*timeout_p);
  }
  input_wait_timeout(timeout);
}


/*
 * Only tested with DJGPP.
 *
 * The only DJGPP specific parts are:
 *
 *   ScreenSetCursor() - move the cursor to row/col.
 *   ScreenMode()      - get the video mode.
 *   ScreenCols()      - number of columns.
 *   ScreenRows()      - number of rows.
 *   ScreenPrimary     - the address of the start of screen memory.
 *   dosmemput()       - write directly to the first Mb of memory.
 *   dosmemget()       - read directly from the first Mb of memory.
 */
#ifdef PC_DIRECT_SCREEN

#include "disptab.h"
#include "pc-term.h"

#define MScreenWidth	1000	/* A large, fixed number.
				   This should be changed to not require
				   a fixed size.  */

#ifndef __WATCOMC__
# include <pc.h>		/* DJGPP direct screen access routines. */
#endif


static Lisp_Object Vemacs_pc_follow_mode_changes;

/* Frame being updated by update_frame.	 This is declared in term.c.
   This is set by update_begin and looked at by all the
   PC functions.  It is zero while not inside an update.
   In that case, the PC functions assume that `selected_frame'
   is the frame to apply to.  */
extern struct frame *updating_frame;


static int cur_x, cur_y;	/* Keep track of cursor. */
int pc_cur_attrib;		/* Current screen attribute. */

static int original_mode;	/* Screen mode before we messed it all up. */
static int previous_mode;	/* Screen mode last in. */

int pc_normal_attrib;		/* Attribute for normal text. */
int pc_highli_attrib;		/* Attribute for status line. */

static int highlight_screen;

/* A buffer to hold the changes to the current line. */
static unsigned short line_buf[MScreenWidth];

/* Macro to convert a character/attribute pair into a single word. */
#define CA(c, a)		((c) | ((a) << 8))

/* Macros to put/get a line of char/attrib pairs. */
#define PUTLINE(buf, w, ptr)	dosmemput((buf), (2 * (w)), (int)(ptr))
#define GETLINE(ptr, w, buf)	dosmemget((int)(ptr), (2 * (w)), (buf))


#ifdef PC_FACES

#define ERROR_ATTRIBUTE		140	/* blinking bright red on black */

static get_attribute(face_id, default_face)
int		face_id;
struct face	*default_face;
{
  int		display_attrib;
  struct face	*face;

  if (default_face)
    {
      display_attrib = default_face->attribute;
      if (face_id != 0)
	{
	  if (face_id < 0 || face_id >= FRAME_N_FACES(NULL))
	    {
	      display_attrib = ERROR_ATTRIBUTE;
	    }
	  else if (face_id == 1)
	    {
	      face = FRAME_MODE_LINE_FACE(NULL);
	      display_attrib = face->attribute;
	    }
	  else
	    {
	      extern struct face *intern_face();

	      face = intern_face (NULL, FRAME_FACES (NULL) [face_id]);
	      display_attrib = face->attribute;
	    }
	}
      else
	{
	  if (highlight_screen)
	    {
	      face = FRAME_MODE_LINE_FACE (NULL);
	      display_attrib = face->attribute;
	    }
	}
    }
  else
    display_attrib = pc_cur_attrib;
  if (display_attrib == 0 ||
      ((display_attrib & 0x0f) ==
       ((display_attrib & 0xf0) >> 4)))
    {
      display_attrib = ERROR_ATTRIBUTE;
    }
  return (display_attrib);
}


static int get_default_attribute()
{
  int attribute;
  struct face *default_face;

  if (FRAME_N_FACES(NULL) > 0)
    default_face = FRAME_DEFAULT_FACE(NULL);
  else
    default_face = NULL;
  attribute = get_attribute(0, default_face);
  return (attribute);
}

#endif	/* PC_FACES */


/*
 * Erase current line from column START to right margin.
 * Leave cursor at START.
 */
pc_clear_end_of_line(register int start)
{
  register unsigned short ca;
  register int x, w;

  if (cur_y < 0 || cur_y >= FRAME_HEIGHT(selected_frame))
    return;

  if (start >= FRAME_WIDTH(selected_frame))
    return;
  if (start < 0)
    start = 0;

#ifdef PC_FACES
  ca = CA(' ', get_default_attribute());
#else	/* not PC_FACES */
  ca = CA(' ', pc_cur_attrib);
#endif	/* not PC_FACES */
  w = FRAME_WIDTH(selected_frame) - start;
  for (x = 0; x < w; x++)
    line_buf[x] = ca;

  PUTLINE(line_buf, w,
	  ScreenPrimary + cur_x + start + (cur_y *
					   FRAME_WIDTH(selected_frame)));

  PC_move_cursor(cur_y, start);
}


/*
 * Insert string from start to end at current cursor position.
 * Leave cursor at end.
 */
static writeglyphs(register GLYPH *string, register int len)
{
  register GLYPH g;
  register int tlen = GLYPH_TABLE_LENGTH;
  register Lisp_Object *tbase = GLYPH_TABLE_BASE;
  register int x, display_attrib, cursor_offset;
#ifdef PC_FACES
  int face_id;
  struct face *default_face;
#endif
#if defined(PC_MOUSE)
  int mouse_displayed = hide_pc_mouse();
#endif

  if (!((cur_y < 0) || (cur_y >= FRAME_HEIGHT(selected_frame)))) {

#ifdef PC_FACES
    if (FRAME_N_FACES(NULL) > 0)
      default_face = FRAME_DEFAULT_FACE(NULL);
    else
      default_face = NULL;
#endif
    cursor_offset = 0;
    while (--len >= 0)
      {
	g = *string++;
	/* Check quickly for G beyond length of table.
	   That implies it isn't an alias and is simple.  */
	if (g >= tlen)
	  {
	  simple:
#ifdef PC_FACES
	    face_id = GLYPH_FACE(g);
	    display_attrib = get_attribute(face_id, default_face);
#else
	    display_attrib = pc_cur_attrib;
#endif
	    line_buf[cursor_offset++] = CA(GLYPH_CHAR(g), display_attrib);
	    if (termscript)
	      putc (g & 0xff, termscript);
	  }
	else
	  {
	    /* G has an entry in Vglyph_table,
	       so process any alias and then test for simpleness.  */
	    GLYPH_FOLLOW_ALIASES (tbase, tlen, g);
	    if (GLYPH_SIMPLE_P (tbase, tlen, g))
	      goto simple;
	    else
	      {
		int j;
		char *cptr;

		/* Here if G (or its definition as an alias) is not simple.  */
#ifdef PC_FACES
		if (default_face)
		  display_attrib = default_face->attribute;
		else
#endif
		  display_attrib = pc_cur_attrib;

		for (j = 0, cptr = GLYPH_STRING (tbase, g);
		     j < GLYPH_LENGTH (tbase, g); ++j)
		  line_buf[cursor_offset++] =
		    CA(GLYPH_CHAR(*cptr++), display_attrib);
		if (termscript)
		  fwrite (GLYPH_STRING (tbase, g), 1, GLYPH_LENGTH (tbase, g),
			  termscript);
	      }
	  }
      }
    PUTLINE(line_buf, cursor_offset,
	    ScreenPrimary + cur_x + (cur_y * FRAME_WIDTH(selected_frame)));

#if 0	/* old code */
    if (((end - string) + cur_x) >= FRAME_WIDTH(selected_frame))
      end = string + (FRAME_WIDTH(selected_frame) - (cur_x + 1));

    if (end >= string) {
      for (cp = string, x = 0; cp <= end; cp++, x++)
	line_buf[x] = CA(GLYPH_CHAR(*cp), display_attrib);

      PUTLINE(line_buf, x,
	      ScreenPrimary + cur_x + (cur_y * FRAME_WIDTH(selected_frame)));
    }
#endif

    PC_move_cursor(cur_y, cur_x + cursor_offset);
  }
#if defined(PC_MOUSE)
  if (mouse_displayed) {
    display_pc_mouse();
  }
#endif
}


/*
 * Move the cursor and keep track of where it is.
 *
 * This is the only place where ScreenSetCursor() should be called.
 */
int PC_move_cursor(register int row, register int col)
{
  ScreenSetCursor(row, col);
  cur_x = col;
  cur_y = row;
}


/*
 * Clear the whole screen.
 */
static int PC_clear_screen()
{
  register unsigned short ca;
  register int i;

#ifdef PC_FACES
  pc_cur_attrib = get_default_attribute();
#else	/* not PC_FACES */
  pc_cur_attrib = pc_normal_attrib;
#endif	/* not PC_FACES */
  ca = CA(' ', pc_cur_attrib);

  for (i = 0; i < FRAME_WIDTH(selected_frame); i++)
    line_buf[i] = ca;
  for (i = 0; i < FRAME_HEIGHT(selected_frame); i++)
    PUTLINE(line_buf, FRAME_WIDTH(selected_frame),
	    ScreenPrimary + (i * FRAME_WIDTH(selected_frame)));

  PC_move_cursor(0, 0);
}


/*
 * Erase current line from current column to column END.
 * Leave cursor at END.
 */
static int PC_clear_end_of_line(register int end)
{
  register unsigned short ca;
  register int x, numcols;

  if (cur_y < 0 || cur_y >= FRAME_HEIGHT(selected_frame))
    return;

  if (end <= cur_x)
    return;
  if (end >= FRAME_WIDTH(selected_frame))
    end = FRAME_WIDTH(selected_frame);

  numcols = end - cur_x;
  ca = CA(' ', pc_cur_attrib);

  for (x = 0; x < numcols; x++)
    line_buf[x] = ca;

  PUTLINE(line_buf, numcols,
	  ScreenPrimary + cur_x + (cur_y * FRAME_WIDTH(selected_frame)));

  PC_move_cursor(cur_y, end);
}


/*
 * Not needed...
 */
static int PC_ins_del_lines(int vpos, int n)
{
}


/*
 * External interface to control of standout mode.
 * Call this when about to modify line at position VPOS
 * and not change whether it is highlighted.
 */
static int PC_reassert_line_highlight(int highlight, int vpos)
{
  highlight_screen = highlight;
#ifdef PC_FACES		/* Ultra-kludge */
  pc_cur_attrib = highlight ? pc_highli_attrib : get_default_attribute();
#else	/* not PC_FACES */
  pc_cur_attrib = (highlight) ? pc_highli_attrib : pc_normal_attrib;
#endif	/* not PC_FACES */
}


/*
 * Call this when about to modify line at position VPOS
 * and change whether it is highlighted.
 */
static int PC_change_line_highlight(int new_highlight, int vpos,
				    int first_unused_hpos)
{
  highlight_screen = new_highlight;
#ifdef PC_FACES		/* Ultra-kludge */
  pc_cur_attrib = new_highlight ? pc_highli_attrib : get_default_attribute();
#else	/* not PC_FACES */
  pc_cur_attrib = (new_highlight) ? pc_highli_attrib : pc_normal_attrib;
#endif	/* not PC_FACES */
  PC_move_cursor(vpos, 0);
  pc_clear_end_of_line(0);
}


static int PC_insert_glyphs(register char *start, register int len)
{
  abort();
}


static int PC_output_glyphs(register GLYPH *start, register int len)
{
  writeglyphs(start, len);
}


static int PC_delete_glyphs(register int n)
{
  abort();
}


/*
 * Beep!
 */
static int PC_feep()
{
  register unsigned short *bp;
  unsigned short *flash_buf;
  int fw, fh, x, y, xs, ys;
#if defined(PC_MOUSE)
  int mouse_displayed;
#endif

  if (visible_bell) {
    mouse_displayed = hide_pc_mouse();
    fw = FRAME_WIDTH(selected_frame) / 2;
    fh = FRAME_HEIGHT(selected_frame) / 2;

    flash_buf = (unsigned short *)xmalloc((fw * fh) *
					  sizeof(unsigned short));

    xs = fw / 2;
    ys = fh / 2;

    /* Save screen. */
    for (y = 0; y < fh; y++)
      GETLINE(ScreenPrimary + xs + ((y + ys) * FRAME_WIDTH(selected_frame)),
	      fw, flash_buf + (y * fw));

    /* Fill flash block. */
    bp = flash_buf;
    for (y = 0; y < fh; y++) {
      for (x = 0; x < fw; x++, bp++) {
	line_buf[x] = CA((*bp & 0xff), pc_highli_attrib);
      }
      PUTLINE(line_buf, fw,
	      ScreenPrimary + xs + ((y + ys) * FRAME_WIDTH(selected_frame)));
    }

    /* Wait a 1/4 of a second. */
    u_sleep(250000);

    /* Restore screen. */
    for (y = 0; y < fh; y++)
      PUTLINE(flash_buf + (y * fw), fw,
	      ScreenPrimary + xs + ((y + ys) * FRAME_WIDTH(selected_frame)));

    free(flash_buf);
#if defined(PC_MOUSE)
    if (mouse_displayed) {
      display_pc_mouse();
    }
#endif
  }
  else {
    putchar('\a');
  }
}


/*
 * Set up screen mode.
 */
static int PC_set_terminal_modes()
{
  if (EQ (Vemacs_pc_follow_mode_changes, Qnil) && previous_mode >= 0) {
    ScreenSetMode(previous_mode);
    /* Make sure that we only do this after shelling out. */
    previous_mode = -1;
  }
  PC_clear_screen();
#ifdef PC_MOUSE
  init_pc_mouse();
#endif
  dos_ttraw();
}


/*
 * Reset screen mode when we're finished (or shell out).
 */
static int PC_reset_terminal_modes()
{
  if (EQ (Vemacs_pc_follow_mode_changes, Qnil)) {
    previous_mode = ScreenMode();
    ScreenSetMode(original_mode);
  }
#ifdef PC_MOUSE
  uninit_pc_mouse();
#endif
  dos_ttcooked();
}


static int PC_update_begin()
{
  highlight_screen = 0;
#ifdef PC_MOUSE
  hide_pc_mouse();
#endif
}


static int PC_update_end()
{
#ifdef PC_MOUSE
  display_pc_mouse();
#endif
}


/*
 * Not needed...
 */
static int PC_set_terminal_window(register int n)
{
}


/*
 * Initialize this terminal type....
 */
pc_term_init ()
{
  char *delim, *s, *sw, *val, buf[100];

#ifdef __GO32__
  /* Kludge to get round a bug in go32 1.10 under DESQview/X. */
  extern unsigned short _core_select;
  if (_core_select == 0xffff) {
    _core_select = 0x0038;
  }
#endif

  original_mode = ScreenMode();
  previous_mode = -1;
  pc_normal_attrib = 0x07;
  pc_highli_attrib = 0x70;

  clear_frame_hook = PC_clear_screen;
  clear_end_of_line_hook = PC_clear_end_of_line;
  ins_del_lines_hook = PC_ins_del_lines;
  change_line_highlight_hook = PC_change_line_highlight;
  insert_glyphs_hook = PC_insert_glyphs;
  write_glyphs_hook = PC_output_glyphs;
  delete_glyphs_hook = PC_delete_glyphs;
  ring_bell_hook = PC_feep;
  reset_terminal_modes_hook = PC_reset_terminal_modes;
  set_terminal_modes_hook = PC_set_terminal_modes;
  update_begin_hook = PC_update_begin;
  update_end_hook = PC_update_end;
  set_terminal_window_hook = PC_set_terminal_window;
  read_socket_hook = PC_input_hook;
  cursor_to_hook = PC_move_cursor;
  reassert_line_highlight_hook = PC_reassert_line_highlight;

#ifdef PC_MOUSE
  mouse_screen_mask = 0x77ff;
  mouse_cursor_mask = 0x7700;
  mouse_position_hook = PC_mouse_position;
#else
  mouse_position_hook = 0;
#endif
  frame_rehighlight_hook = 0;
  frame_raise_lower_hook = 0;
  set_vertical_scroll_bar_hook = 0;
  condemn_scroll_bars_hook = 0;
  redeem_scroll_bar_hook = 0;
  judge_scroll_bars_hook = 0;

  scroll_region_ok = 0;		/* we'll scroll partial frames */
  char_ins_del_ok = 0;		/* just as fast to write the line */
  line_ins_del_ok = 0;		/* we'll just blt 'em */
  fast_clear_end_of_line = 1;	/* X does this well */
  memory_below_frame = 0;	/* we don't remember what scrolls
				   off the bottom */
  baud_rate = 19200;

  get_frame_size (&FRAME_WIDTH (selected_frame),
		  &FRAME_HEIGHT (selected_frame));
  if (FRAME_WIDTH (selected_frame) <= 0)
    FRAME_WIDTH (selected_frame) = 80;
  if (FRAME_HEIGHT (selected_frame) <= 0)
    FRAME_HEIGHT (selected_frame) = 25;

  FRAME_CAN_HAVE_SCROLL_BARS (selected_frame) = 0;
  FRAME_HAS_VERTICAL_SCROLL_BARS (selected_frame) = 0;

  FRAME_OUTPUT_METHOD (selected_frame) = output_pc_screen;

#ifdef PC_FACES
  the_only_frame.display.pc =
    (struct pc_display *) xmalloc(sizeof(struct pc_display));
  the_only_frame.display.pc->faces = NULL;
  the_only_frame.display.pc->n_faces = 0;
  /* hack, hack */
  staticpro(&the_only_frame.face_alist);
  the_only_frame.face_alist = Qnil;
  init_frame_faces(NULL);
#endif

  dos_ttraw();
}
#endif /* PC_DIRECT_SCREEN */

#endif /* defined(MSDOS) && !defined(DVX) */

syms_of_pc_term()
{
#ifdef PC_DIRECT_SCREEN
  DEFVAR_LISP ("emacs-pc-follow-mode-changes", &Vemacs_pc_follow_mode_changes,
	       "Non-nil if Emacs should reconfigure itself to follow display mode changes.\n\
If this is nil, Emacs will reset the display mode back to the original mode\n\
if the current mode is different from the original.");
  Vemacs_pc_follow_mode_changes = Qt;
#endif	/* PC_DIRECT_SCREEN */

#ifdef PC_MOUSE
  defsubr(&Semacs_pc_enable_mouse);
  defsubr(&Semacs_pc_disable_mouse);
  defsubr(&Semacs_pc_set_mouse_cursor);

  DEFVAR_LISP ("mouse-grabbed", &Vmouse_depressed,
	       "Non-nil if a mouse button is currently depressed.");
  Vmouse_depressed = Qnil;
#endif	/* PC_MOUSE */
}
