]> code.delx.au - gnu-emacs/blobdiff - src/term.c
Merge from emacs--devo--0
[gnu-emacs] / src / term.c
index 48942a46671970eb4a6d09c4897172e0d8e8cf1d..331d9f20e4c342b6e54241de9fe0f54f31c9d70c 100644 (file)
@@ -1,6 +1,6 @@
 /* Terminal control module for terminals described by TERMCAP
    Copyright (C) 1985, 1986, 1987, 1993, 1994, 1995, 1998, 2000, 2001,
-                 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
+                 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -25,6 +25,9 @@ Boston, MA 02110-1301, USA.  */
 #include <stdio.h>
 #include <ctype.h>
 #include <string.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
 
 #include "termchar.h"
 #include "termopts.h"
@@ -33,6 +36,7 @@ Boston, MA 02110-1301, USA.  */
 #include "character.h"
 #include "charset.h"
 #include "coding.h"
+#include "composite.h"
 #include "keyboard.h"
 #include "frame.h"
 #include "disptab.h"
@@ -40,6 +44,8 @@ Boston, MA 02110-1301, USA.  */
 #include "dispextern.h"
 #include "window.h"
 #include "keymap.h"
+#include "blockinput.h"
+#include "intervals.h"
 
 /* For now, don't try to include termcap.h.  On some systems,
    configure finds a non-standard termcap.h that the main build
@@ -145,25 +151,6 @@ int (*read_socket_hook) P_ ((int, int, struct input_event *));
 
 void (*frame_up_to_date_hook) P_ ((struct frame *));
 
-/* Return the current position of the mouse.
-
-   Set *f to the frame the mouse is in, or zero if the mouse is in no
-   Emacs frame.  If it is set to zero, all the other arguments are
-   garbage.
-
-   If the motion started in a scroll bar, set *bar_window to the
-   scroll bar's window, *part to the part the mouse is currently over,
-   *x to the position of the mouse along the scroll bar, and *y to the
-   overall length of the scroll bar.
-
-   Otherwise, set *bar_window to Qnil, and *x and *y to the column and
-   row of the character cell the mouse is over.
-
-   Set *time to the time the mouse was at the returned position.
-
-   This should clear mouse_moved until the next motion
-   event arrives.  */
-
 void (*mouse_position_hook) P_ ((FRAME_PTR *f, int insist,
                                 Lisp_Object *bar_window,
                                 enum scroll_bar_part *part,
@@ -192,6 +179,11 @@ void (*frame_rehighlight_hook) P_ ((FRAME_PTR f));
 
 void (*frame_raise_lower_hook) P_ ((FRAME_PTR f, int raise));
 
+/* If the value of the frame parameter changed, whis hook is called.
+   For example, if going from fullscreen to not fullscreen this hook
+   may do something OS dependent, like extended window manager hints on X11.  */
+void (*fullscreen_hook) P_ ((struct frame *f));
+
 /* Set the vertical scroll bar for WINDOW to have its upper left corner
    at (TOP, LEFT), and be LENGTH rows high.  Set its handle to
    indicate that we are displaying PORTION characters out of a total
@@ -409,6 +401,9 @@ static int tty_cursor_hidden;
 char *tparam ();
 
 extern char *tgetstr ();
+
+static void term_clear_mouse_face ();
+static void term_mouse_highlight (struct frame *f, int x, int y);
 \f
 
 #ifdef WINDOWSNT
@@ -422,6 +417,27 @@ extern char *tgetstr ();
 #define FRAME_TERMCAP_P(_f_) 0
 #endif /* WINDOWSNT */
 
+#ifdef HAVE_GPM
+#include <sys/fcntl.h>
+
+/* Nonzero means mouse is enabled on Linux console.  */
+int term_gpm = 0;
+
+/* These variables describe the range of text currently shown in its
+   mouse-face, together with the window they apply to.  As long as
+   the mouse stays within this range, we need not redraw anything on
+   its account.  Rows and columns are glyph matrix positions in
+   MOUSE_FACE_WINDOW.  */
+static int mouse_face_beg_row, mouse_face_beg_col;
+static int mouse_face_end_row, mouse_face_end_col;
+static int mouse_face_past_end;
+static Lisp_Object Qmouse_face_window;
+static int mouse_face_face_id;
+
+static int pos_x, pos_y;
+static int last_mouse_x, last_mouse_y;
+#endif /* HAVE_GPM */
+
 void
 ring_bell ()
 {
@@ -835,7 +851,8 @@ encode_terminal_code (src, src_len, coding)
 
   /* Allocate sufficient size of buffer to store all characters in
      multibyte-form.  But, it may be enlarged on demand if
-     Vglyph_table contains a string.  */
+     Vglyph_table contains a string or a composite glyph is
+     encountered.  */
   required = MAX_MULTIBYTE_LENGTH * src_len;
   if (encode_terminal_src_size < required)
     {
@@ -852,8 +869,40 @@ encode_terminal_code (src, src_len, coding)
   nchars = 0;
   while (src < src_end)
     {
+      if (src->type == COMPOSITE_GLYPH)
+       {
+         struct composition *cmp = composition_table[src->u.cmp_id];
+         int i;
+
+         nbytes = buf - encode_terminal_src;
+         required = MAX_MULTIBYTE_LENGTH * cmp->glyph_len;
+
+         if (encode_terminal_src_size < nbytes + required)
+           {
+             encode_terminal_src_size = nbytes + required;
+             encode_terminal_src = xrealloc (encode_terminal_src,
+                                             encode_terminal_src_size);
+             buf = encode_terminal_src + nbytes;
+           }
+
+         for (i = 0; i < cmp->glyph_len; i++)
+           {
+             int c = COMPOSITION_GLYPH (cmp, i);
+             
+             if (! char_charset (c, charset_list, NULL))
+               break;
+             buf += CHAR_STRING (c, buf);
+             nchars++;
+           }
+         if (i == 0)
+           {
+             /* The first character of the composition is not encodable.  */
+             *buf++ = '?';
+             nchars++;
+           }
+       }
       /* We must skip glyphs to be padded for a wide character.  */
-      if (! CHAR_GLYPH_PADDING_P (*src))
+      else if (! CHAR_GLYPH_PADDING_P (*src))
        {
          int c;
          Lisp_Object string;
@@ -883,6 +932,14 @@ encode_terminal_code (src, src_len, coding)
 
          if (NILP (string))
            {
+             nbytes = buf - encode_terminal_src;
+             if (encode_terminal_src_size < nbytes + MAX_MULTIBYTE_LENGTH)
+               {
+                 encode_terminal_src_size = nbytes + MAX_MULTIBYTE_LENGTH;
+                 encode_terminal_src = xrealloc (encode_terminal_src,
+                                                 encode_terminal_src_size);
+                 buf = encode_terminal_src + nbytes;
+               }
              if (char_charset (c, charset_list, NULL))
                {
                  /* Store the multibyte form of C at BUF.  */
@@ -1010,11 +1067,13 @@ write_glyphs (string, len)
       conversion_buffer = encode_terminal_code (string, n, coding);
       if (coding->produced > 0)
        {
+         BLOCK_INPUT;
          fwrite (conversion_buffer, 1, coding->produced, stdout);
          if (ferror (stdout))
            clearerr (stdout);
          if (termscript)
            fwrite (conversion_buffer, 1, coding->produced, termscript);
+         UNBLOCK_INPUT;
        }
       len -= n;
       string += n;
@@ -1027,6 +1086,65 @@ write_glyphs (string, len)
   cmcheckmagic ();
 }
 
+void
+write_glyphs_with_face (string, len, face_id)
+     register struct glyph *string;
+     register int len, face_id;
+{
+  struct frame *sf = XFRAME (selected_frame);
+  struct frame *f = updating_frame ? updating_frame : sf;
+  unsigned char *conversion_buffer;
+  struct coding_system *coding;
+
+  turn_off_insert ();
+  tty_hide_cursor ();
+
+  /* Don't dare write in last column of bottom line, if Auto-Wrap,
+     since that would scroll the whole frame on some terminals.  */
+
+  if (AutoWrap
+      && curY + 1 == FRAME_LINES (sf)
+      && (curX + len) == FRAME_COLS (sf))
+    len --;
+  if (len <= 0)
+    return;
+
+  cmplus (len);
+
+  /* If terminal_coding does any conversion, use it, otherwise use
+     safe_terminal_coding.  We can't use CODING_REQUIRE_ENCODING here
+     because it always return 1 if the member src_multibyte is 1.  */
+  coding = (terminal_coding.common_flags & CODING_REQUIRE_ENCODING_MASK
+           ? &terminal_coding : &safe_terminal_coding);
+  /* The mode bit CODING_MODE_LAST_BLOCK should be set to 1 only at
+     the tail.  */
+  coding->mode &= ~CODING_MODE_LAST_BLOCK;
+
+
+  /* Turn appearance modes of the face.  */
+  highlight_if_desired ();
+  turn_on_face (f, face_id);
+
+  coding->mode |= CODING_MODE_LAST_BLOCK;
+  conversion_buffer = encode_terminal_code (string, len, coding);
+  if (coding->produced > 0)
+    {
+      BLOCK_INPUT;
+      fwrite (conversion_buffer, 1, coding->produced, stdout);
+      if (ferror (stdout))
+       clearerr (stdout);
+      if (termscript)
+       fwrite (conversion_buffer, 1, coding->produced, termscript);
+      UNBLOCK_INPUT;
+    }
+
+  /* Turn appearance modes off.  */
+  turn_off_face (f, face_id);
+  turn_off_highlight ();
+
+  cmcheckmagic ();
+}
+
 /* If start is zero, insert blanks instead of a string at start */
 
 void
@@ -1109,11 +1227,13 @@ insert_glyphs (start, len)
 
       if (coding->produced > 0)
        {
+         BLOCK_INPUT;
          fwrite (conversion_buffer, 1, coding->produced, stdout);
          if (ferror (stdout))
            clearerr (stdout);
          if (termscript)
            fwrite (conversion_buffer, 1, coding->produced, termscript);
+         UNBLOCK_INPUT;
        }
 
       OUTPUT1_IF (TS_pad_inserted_char);
@@ -1658,11 +1778,14 @@ term_get_fkeys_1 ()
 #ifdef static
 #define append_glyph append_glyph_term
 #define produce_stretch_glyph produce_stretch_glyph_term
+#define append_composite_glyph append_composite_glyph_term
+#define produce_composite_glyph produce_composite_glyph_term
 #endif
 
 static void append_glyph P_ ((struct it *));
 static void produce_stretch_glyph P_ ((struct it *));
-
+static void append_composite_glyph P_ ((struct it *));
+static void produce_composite_glyph P_ ((struct it *));
 
 /* Append glyphs to IT's glyph_row.  Called from produce_glyphs for
    terminal frames if IT->glyph_row != NULL.  IT->char_to_display is
@@ -1723,6 +1846,8 @@ produce_glyphs (it)
      struct it *it;
 {
   /* If a hook is installed, let it do the work.  */
+
+  /* Nothing but characters are supported on terminal frames.  */
   xassert (it->what == IT_CHARACTER
           || it->what == IT_COMPOSITION
           || it->what == IT_STRETCH);
@@ -1733,11 +1858,11 @@ produce_glyphs (it)
       goto done;
     }
 
-  /* Nothing but characters are supported on terminal frames.  For a
-     composition sequence, it->c is the first character of the
-     sequence.  */
-  xassert (it->what == IT_CHARACTER
-          || it->what == IT_COMPOSITION);
+  if (it->what == IT_COMPOSITION)
+    {
+      produce_composite_glyph (it);
+      goto done;
+    }
 
   /* Maybe translate single-byte characters to multibyte.  */
   it->char_to_display = it->c;
@@ -1896,6 +2021,57 @@ produce_stretch_glyph (it)
 }
 
 
+/* Append glyphs to IT's glyph_row for the composition IT->cmp_id.
+   Called from produce_composite_glyph for terminal frames if
+   IT->glyph_row != NULL.  IT->face_id contains the character's
+   face.  */
+
+static void
+append_composite_glyph (it)
+     struct it *it;
+{
+  struct glyph *glyph;
+
+  xassert (it->glyph_row);
+  glyph = it->glyph_row->glyphs[it->area] + it->glyph_row->used[it->area];
+  if (glyph < it->glyph_row->glyphs[1 + it->area])
+    {
+      glyph->type = COMPOSITE_GLYPH;
+      glyph->pixel_width = it->pixel_width;
+      glyph->u.cmp_id = it->cmp_id;
+      glyph->face_id = it->face_id;
+      glyph->padding_p = 0;
+      glyph->charpos = CHARPOS (it->position);
+      glyph->object = it->object;
+
+      ++it->glyph_row->used[it->area];
+      ++glyph;
+    }
+}
+
+
+/* Produce a composite glyph for iterator IT.  IT->cmp_id is the ID of
+   the composition.  We simply produces components of the composition
+   assuming that that the terminal has a capability to layout/render
+   it correctly.  */
+
+static void
+produce_composite_glyph (it)
+     struct it *it;
+{
+  struct composition *cmp = composition_table[it->cmp_id];
+  int c;
+
+  xassert (cmp->glyph_len > 0);
+  c = COMPOSITION_GLYPH (cmp, 0);
+  it->pixel_width = CHAR_WIDTH (it->c);
+  it->nglyphs = 1;
+
+  if (it->glyph_row)
+    append_composite_glyph (it);
+}
+
+
 /* Get information about special display element WHAT in an
    environment described by IT.  WHAT is one of IT_TRUNCATION or
    IT_CONTINUATION.  Maybe produce glyphs for WHAT if IT has a
@@ -2311,6 +2487,658 @@ set_tty_color_mode (f, val)
 
 #endif /* !WINDOWSNT */
 
+\f
+/***********************************************************************
+                              Mouse
+ ***********************************************************************/
+
+#ifdef HAVE_GPM
+void
+term_mouse_moveto (int x, int y)
+{
+  /* TODO: how to set mouse position?
+  const char *name;
+  int fd;
+  name = (const char *) ttyname (0);
+  fd = open (name, O_WRONLY);
+     SOME_FUNCTION (x, y, fd);
+  close (fd);
+  last_mouse_x = x;
+  last_mouse_y = y;  */
+}
+
+static void
+term_show_mouse_face (enum draw_glyphs_face draw)
+{
+  struct window *w = XWINDOW (Qmouse_face_window);
+  int save_x, save_y;
+  int i;
+
+  if (/* If window is in the process of being destroyed, don't bother
+        to do anything.  */
+      w->current_matrix != NULL
+      /* Recognize when we are called to operate on rows that don't exist
+        anymore.  This can happen when a window is split.  */
+      && mouse_face_end_row < w->current_matrix->nrows)
+    {
+      /* write_glyphs writes at cursor position, so we need to
+        temporarily move cursor coordinates to the beginning of
+        the highlight region.  */
+      
+      /* Save current cursor co-ordinates */
+      save_y = curY;
+      save_x = curX;
+
+      /* Note that mouse_face_beg_row etc. are window relative.  */
+      for (i = mouse_face_beg_row; i <= mouse_face_end_row; i++)
+       {
+         int start_hpos, end_hpos, nglyphs;
+         struct glyph_row *row = MATRIX_ROW (w->current_matrix, i);
+
+         /* Don't do anything if row doesn't have valid contents.  */
+         if (!row->enabled_p)
+           continue;
+
+         /* For all but the first row, the highlight starts at column 0.  */
+         if (i == mouse_face_beg_row)
+           start_hpos = mouse_face_beg_col;
+         else
+           start_hpos = 0;
+
+         if (i == mouse_face_end_row)
+           end_hpos = mouse_face_end_col;
+         else
+           {
+             end_hpos = row->used[TEXT_AREA];
+             if (draw == DRAW_NORMAL_TEXT)
+               row->fill_line_p = 1; /* Clear to end of line */
+           }
+
+         if (end_hpos <= start_hpos)
+           continue;
+         /* Record that some glyphs of this row are displayed in
+            mouse-face.  */
+         row->mouse_face_p = draw > 0;
+
+         nglyphs = end_hpos - start_hpos;
+
+         if (end_hpos >= row->used[TEXT_AREA])
+           nglyphs = row->used[TEXT_AREA] - start_hpos;
+
+         pos_y = row->y + WINDOW_TOP_EDGE_Y (w);
+         pos_x = row->used[LEFT_MARGIN_AREA] + start_hpos
+           + WINDOW_LEFT_EDGE_X (w);
+         
+         cursor_to (pos_y, pos_x);
+
+         if (draw == DRAW_MOUSE_FACE)
+           {
+             write_glyphs_with_face (row->glyphs[TEXT_AREA] + start_hpos,
+                                     nglyphs, mouse_face_face_id);
+           }
+         else /* draw == DRAW_NORMAL_TEXT */
+           write_glyphs (row->glyphs[TEXT_AREA] + start_hpos, nglyphs);
+       }
+      cursor_to (save_y, save_x);
+    }
+}
+
+static void
+term_clear_mouse_face ()
+{
+  if (!NILP (Qmouse_face_window))
+    term_show_mouse_face (DRAW_NORMAL_TEXT);
+
+  mouse_face_beg_row = mouse_face_beg_col = -1;
+  mouse_face_end_row = mouse_face_end_col = -1;
+  Qmouse_face_window = Qnil;
+}
+
+/* Find the glyph matrix position of buffer position POS in window W.
+   *HPOS and *VPOS are set to the positions found.  W's current glyphs
+   must be up to date.  If POS is above window start return (0, 0).
+   If POS is after end of W, return end of last line in W.
+   - taken from msdos.c */
+static int
+fast_find_position (struct window *w, int pos, int *hpos, int *vpos)
+{
+  int i, lastcol, line_start_position, maybe_next_line_p = 0;
+  int yb = window_text_bottom_y (w);
+  struct glyph_row *row = MATRIX_ROW (w->current_matrix, 0), *best_row = row;
+
+  while (row->y < yb)
+    {
+      if (row->used[TEXT_AREA])
+       line_start_position = row->glyphs[TEXT_AREA]->charpos;
+      else
+       line_start_position = 0;
+
+      if (line_start_position > pos)
+       break;
+      /* If the position sought is the end of the buffer,
+        don't include the blank lines at the bottom of the window.  */
+      else if (line_start_position == pos
+              && pos == BUF_ZV (XBUFFER (w->buffer)))
+       {
+         maybe_next_line_p = 1;
+         break;
+       }
+      else if (line_start_position > 0)
+       best_row = row;
+
+      /* Don't overstep the last matrix row, lest we get into the
+        never-never land... */
+      if (row->y + 1 >= yb)
+       break;
+
+      ++row;
+    }
+
+  /* Find the right column within BEST_ROW.  */
+  lastcol = 0;
+  row = best_row;
+  for (i = 0; i < row->used[TEXT_AREA]; i++)
+    {
+      struct glyph *glyph = row->glyphs[TEXT_AREA] + i;
+      int charpos;
+
+      charpos = glyph->charpos;
+      if (charpos == pos)
+       {
+         *hpos = i;
+         *vpos = row->y;
+         return 1;
+       }
+      else if (charpos > pos)
+       break;
+      else if (charpos > 0)
+       lastcol = i;
+    }
+
+  /* If we're looking for the end of the buffer,
+     and we didn't find it in the line we scanned,
+     use the start of the following line.  */
+  if (maybe_next_line_p)
+    {
+      ++row;
+      lastcol = 0;
+    }
+
+  *vpos = row->y;
+  *hpos = lastcol + 1;
+  return 0;
+}
+
+static void
+term_mouse_highlight (struct frame *f, int x, int y)
+{
+  enum window_part part;
+  Lisp_Object window;
+  struct window *w;
+  struct buffer *b;
+
+  if (NILP (Vmouse_highlight)
+      || !f->glyphs_initialized_p)
+    return;
+
+  /* Which window is that in?  */
+  window = window_from_coordinates (f, x, y, &part, &x, &y, 0);
+
+  /* Not on a window -> return.  */
+  if (!WINDOWP (window))
+    return;
+
+  if (!EQ (window, Qmouse_face_window))
+    term_clear_mouse_face ();
+
+  w = XWINDOW (window);
+
+  /* Are we in a window whose display is up to date?
+     And verify the buffer's text has not changed.  */
+  b = XBUFFER (w->buffer);
+  if (part == ON_TEXT
+      && EQ (w->window_end_valid, w->buffer)
+      && XFASTINT (w->last_modified) == BUF_MODIFF (b)
+      && XFASTINT (w->last_overlay_modified) == BUF_OVERLAY_MODIFF (b))
+    {
+      int pos, i, nrows = w->current_matrix->nrows;
+      struct glyph_row *row;
+      struct glyph *glyph;
+
+      /* Find the glyph under X/Y.  */
+      glyph = NULL;
+      if (y >= 0 && y < nrows)
+       {
+         row = MATRIX_ROW (w->current_matrix, y);
+         /* Give up if some row before the one we are looking for is
+            not enabled.  */
+         for (i = 0; i <= y; i++)
+           if (!MATRIX_ROW (w->current_matrix, i)->enabled_p)
+             break;
+         if (i > y  /* all rows upto and including the one at Y are enabled */
+             && row->displays_text_p
+             && x <  window_box_width (w, TEXT_AREA))
+           {
+             glyph = row->glyphs[TEXT_AREA];
+             if (x >= row->used[TEXT_AREA])
+               glyph = NULL;
+             else
+               {
+                 glyph += x;
+                 if (!BUFFERP (glyph->object))
+                   glyph = NULL;
+               }
+           }
+       }
+
+      /* Clear mouse face if X/Y not over text.  */
+      if (glyph == NULL)
+       {
+         term_clear_mouse_face ();
+         return;
+       }
+
+      if (!BUFFERP (glyph->object))
+       abort ();
+      pos = glyph->charpos;
+
+      /* Check for mouse-face.  */
+      {
+       extern Lisp_Object Qmouse_face;
+       Lisp_Object mouse_face, overlay, position, *overlay_vec;
+       int noverlays, obegv, ozv;
+       struct buffer *obuf;
+
+       /* If we get an out-of-range value, return now; avoid an error.  */
+       if (pos > BUF_Z (b))
+         return;
+
+       /* Make the window's buffer temporarily current for
+          overlays_at and compute_char_face.  */
+       obuf = current_buffer;
+       current_buffer = b;
+       obegv = BEGV;
+       ozv = ZV;
+       BEGV = BEG;
+       ZV = Z;
+
+       /* Is this char mouse-active?  */
+       XSETINT (position, pos);
+
+       /* Put all the overlays we want in a vector in overlay_vec.  */
+       GET_OVERLAYS_AT (pos, overlay_vec, noverlays, NULL, 0);
+       /* Sort overlays into increasing priority order.  */
+       noverlays = sort_overlays (overlay_vec, noverlays, w);
+
+       /* Check mouse-face highlighting.  */
+       if (!(EQ (window, Qmouse_face_window)
+             && y >= mouse_face_beg_row
+             && y <= mouse_face_end_row
+             && (y > mouse_face_beg_row
+                 || x >= mouse_face_beg_col)
+             && (y < mouse_face_end_row
+                 || x < mouse_face_end_col
+                 || mouse_face_past_end)))
+         {
+           /* Clear the display of the old active region, if any.  */
+           term_clear_mouse_face ();
+
+           /* Find the highest priority overlay that has a mouse-face
+              property.  */
+           overlay = Qnil;
+           for (i = noverlays - 1; i >= 0; --i)
+             {
+               mouse_face = Foverlay_get (overlay_vec[i], Qmouse_face);
+               if (!NILP (mouse_face))
+                 {
+                   overlay = overlay_vec[i];
+                   break;
+                 }
+             }
+
+           /* If no overlay applies, get a text property.  */
+           if (NILP (overlay))
+             mouse_face = Fget_text_property (position, Qmouse_face,
+                                              w->buffer);
+
+           /* Handle the overlay case.  */
+           if (!NILP (overlay))
+             {
+               /* Find the range of text around this char that
+                  should be active.  */
+               Lisp_Object before, after;
+               int ignore;
+
+
+               before = Foverlay_start (overlay);
+               after = Foverlay_end (overlay);
+               /* Record this as the current active region.  */
+               fast_find_position (w, XFASTINT (before),
+                                   &mouse_face_beg_col,
+                                   &mouse_face_beg_row);
+
+               mouse_face_past_end
+                 = !fast_find_position (w, XFASTINT (after),
+                                        &mouse_face_end_col,
+                                        &mouse_face_end_row);
+               Qmouse_face_window = window;
+
+               mouse_face_face_id
+                 = face_at_buffer_position (w, pos, 0, 0,
+                                            &ignore, pos + 1, 1);
+
+               /* Display it as active.  */
+               term_show_mouse_face (DRAW_MOUSE_FACE);
+             }
+           /* Handle the text property case.  */
+           else if (!NILP (mouse_face))
+             {
+               /* Find the range of text around this char that
+                  should be active.  */
+               Lisp_Object before, after, beginning, end;
+               int ignore;
+
+               beginning = Fmarker_position (w->start);
+               XSETINT (end, (BUF_Z (b) - XFASTINT (w->window_end_pos)));
+               before
+                 = Fprevious_single_property_change (make_number (pos + 1),
+                                                     Qmouse_face,
+                                                     w->buffer, beginning);
+               after
+                 = Fnext_single_property_change (position, Qmouse_face,
+                                                 w->buffer, end);
+
+               /* Record this as the current active region.  */
+               fast_find_position (w, XFASTINT (before),
+                                   &mouse_face_beg_col,
+                                   &mouse_face_beg_row);
+               mouse_face_past_end
+                 = !fast_find_position (w, XFASTINT (after),
+                                        &mouse_face_end_col,
+                                        &mouse_face_end_row);
+               Qmouse_face_window = window;
+
+               mouse_face_face_id
+                 = face_at_buffer_position (w, pos, 0, 0,
+                                            &ignore, pos + 1, 1);
+
+               /* Display it as active.  */
+               term_show_mouse_face (DRAW_MOUSE_FACE);
+             }
+         }
+
+       /* Look for a `help-echo' property.  */
+       {
+         Lisp_Object help;
+         extern Lisp_Object Qhelp_echo;
+
+         /* Check overlays first.  */
+         help = Qnil;
+         for (i = noverlays - 1; i >= 0 && NILP (help); --i)
+           {
+             overlay = overlay_vec[i];
+             help = Foverlay_get (overlay, Qhelp_echo);
+           }
+
+         if (!NILP (help))
+           {
+             help_echo_string = help;
+             help_echo_window = window;
+             help_echo_object = overlay;
+             help_echo_pos = pos;
+           }
+         /* Try text properties.  */
+         else if (NILP (help)
+                  && ((STRINGP (glyph->object)
+                       && glyph->charpos >= 0
+                       && glyph->charpos < SCHARS (glyph->object))
+                      || (BUFFERP (glyph->object)
+                          && glyph->charpos >= BEGV
+                          && glyph->charpos < ZV)))
+           {
+             help = Fget_text_property (make_number (glyph->charpos),
+                                        Qhelp_echo, glyph->object);
+             if (!NILP (help))
+               {
+                 help_echo_string = help;
+                 help_echo_window = window;
+                 help_echo_object = glyph->object;
+                 help_echo_pos = glyph->charpos;
+               }
+           }
+       }
+
+       BEGV = obegv;
+       ZV = ozv;
+       current_buffer = obuf;
+      }
+    }
+}
+
+static int
+term_mouse_movement (FRAME_PTR frame, Gpm_Event *event)
+{
+  /* Has the mouse moved off the glyph it was on at the last sighting?  */
+  if (event->x != last_mouse_x || event->y != last_mouse_y)
+    {
+      frame->mouse_moved = 1;
+      term_mouse_highlight (frame, event->x, event->y);
+      /* Remember which glyph we're now on.  */
+      last_mouse_x = event->x;
+      last_mouse_y = event->y;
+      return 1;
+    }
+  return 0;
+}
+
+/* Return the current position of the mouse.
+
+   Set *f to the frame the mouse is in, or zero if the mouse is in no
+   Emacs frame.  If it is set to zero, all the other arguments are
+   garbage.
+
+   Set *bar_window to Qnil, and *x and *y to the column and
+   row of the character cell the mouse is over.
+
+   Set *time to the time the mouse was at the returned position.
+
+   This clears mouse_moved until the next motion
+   event arrives.  */
+static void
+term_mouse_position (FRAME_PTR *fp, int insist, Lisp_Object *bar_window,
+                    enum scroll_bar_part *part, Lisp_Object *x,
+                    Lisp_Object *y, unsigned long *time)
+{
+  struct timeval now;
+
+  *fp = SELECTED_FRAME ();
+  (*fp)->mouse_moved = 0;
+
+  *bar_window = Qnil;
+  *part = 0;
+
+  XSETINT (*x, last_mouse_x);
+  XSETINT (*y, last_mouse_y);
+  gettimeofday(&now, 0);
+  *time = (now.tv_sec * 1000) + (now.tv_usec / 1000);
+}
+
+/* Prepare a mouse-event in *RESULT for placement in the input queue.
+
+   If the event is a button press, then note that we have grabbed
+   the mouse.  */
+
+static Lisp_Object
+term_mouse_click (struct input_event *result, Gpm_Event *event,
+                 struct frame *f)
+{
+  struct timeval now;
+  int i, j;
+
+  result->kind = GPM_CLICK_EVENT;
+  for (i = 0, j = GPM_B_LEFT; i < 3; i++, j >>= 1 )
+    {
+      if (event->buttons & j) {
+       result->code = i; /* button number */
+       break;
+      }
+    }
+  gettimeofday(&now, 0);
+  result->timestamp = (now.tv_sec * 1000) + (now.tv_usec / 1000);
+
+  if (event->type & GPM_UP)
+    result->modifiers = up_modifier;
+  else if (event->type & GPM_DOWN)
+    result->modifiers = down_modifier;
+  else
+    result->modifiers = 0;
+  
+  if (event->type & GPM_SINGLE)
+    result->modifiers |= click_modifier;
+  
+  if (event->type & GPM_DOUBLE)
+    result->modifiers |= double_modifier;
+
+  if (event->type & GPM_TRIPLE)
+    result->modifiers |= triple_modifier;
+
+  if (event->type & GPM_DRAG)
+    result->modifiers |= drag_modifier;
+
+  if (!(event->type & (GPM_MOVE | GPM_DRAG))) {
+
+    /* 1 << KG_SHIFT */
+    if (event->modifiers & (1 << 0))
+      result->modifiers |= shift_modifier;
+
+    /* 1 << KG_CTRL */
+    if (event->modifiers & (1 << 2))
+      result->modifiers |= ctrl_modifier;
+
+    /* 1 << KG_ALT || KG_ALTGR */
+    if (event->modifiers & (1 << 3)
+       || event->modifiers & (1 << 1))
+      result->modifiers |= meta_modifier;
+  }
+
+  XSETINT (result->x, event->x);
+  XSETINT (result->y, event->y);
+  XSETFRAME (result->frame_or_window, f);
+  result->arg = Qnil;
+  return Qnil;
+}
+
+int 
+handle_one_term_event (Gpm_Event *event, struct input_event* hold_quit)
+{
+  struct frame *f = SELECTED_FRAME ();
+  int fd;
+  struct input_event ie;
+  int do_help = 0;
+  int count = 0;
+
+  EVENT_INIT (ie);
+  ie.kind = NO_EVENT;
+  ie.arg = Qnil;
+
+  if (event->type & (GPM_MOVE | GPM_DRAG)) {
+    unsigned char buf[6 * sizeof (short)];
+    unsigned short *arg = (unsigned short *) buf + 1;
+    const char *name;
+
+    previous_help_echo_string = help_echo_string;
+    help_echo_string = Qnil;
+
+    /* Display mouse pointer */
+    buf[sizeof(short) - 1] = 2;  /* set selection */
+
+    arg[0] = arg[2] = (unsigned short) event->x + gpm_zerobased;
+    arg[1] = arg[3] = (unsigned short) event->y + gpm_zerobased;
+    arg[4] = (unsigned short) 3;
+    
+    name = ttyname (0);
+    fd = open (name, O_WRONLY);
+    ioctl (fd, TIOCLINUX, buf + sizeof (short) - 1);
+    close (fd);
+
+    if (!term_mouse_movement (f, event))
+      help_echo_string = previous_help_echo_string;
+
+    /* If the contents of the global variable help_echo_string
+       has changed, generate a HELP_EVENT.  */
+    if (!NILP (help_echo_string)
+       || !NILP (previous_help_echo_string))
+      do_help = 1;
+
+    goto done;
+  }
+  else {
+    f->mouse_moved = 0;
+    term_mouse_click (&ie, event, f);
+  }
+
+ done:
+  if (ie.kind != NO_EVENT)
+    {
+      kbd_buffer_store_event_hold (&ie, hold_quit);
+      count++;
+    }
+
+  if (do_help
+      && !(hold_quit && hold_quit->kind != NO_EVENT))
+    {
+      Lisp_Object frame;
+
+      if (f)
+       XSETFRAME (frame, f);
+      else
+       frame = Qnil;
+
+      gen_help_event (help_echo_string, frame, help_echo_window,
+                     help_echo_object, help_echo_pos);
+      count++;
+    }
+
+  return count;
+}
+
+DEFUN ("term-open-connection", Fterm_open_connection, Sterm_open_connection,
+       0, 0, 0,
+       doc: /* Open a connection to Gpm.  */)
+     ()
+{
+  Gpm_Connect connection;
+
+  connection.eventMask = ~0;
+  connection.defaultMask = ~GPM_HARD;
+  connection.maxMod = ~0;
+  connection.minMod = 0;
+  gpm_zerobased = 1;
+
+  if (Gpm_Open (&connection, 0) < 0)
+    return Qnil;
+  else
+    {
+      term_gpm = 1;
+      reset_sys_modes ();
+      init_sys_modes ();
+      add_gpm_wait_descriptor (gpm_fd);
+      return Qt;
+    }
+}
+
+DEFUN ("term-close-connection", Fterm_close_connection, Sterm_close_connection,
+       0, 0, 0,
+       doc: /* Close a connection to Gpm.  */)
+     ()
+{
+   delete_gpm_wait_descriptor (gpm_fd);
+   while (Gpm_Close()); /* close all the stack */
+   term_gpm = 0;
+   return Qnil;
+}
+#endif /* HAVE_GPM */
+
 \f
 /***********************************************************************
                            Initialization
@@ -2331,6 +3159,11 @@ term_init (terminal_type)
   encode_terminal_src_size = 0;
   encode_terminal_dst_size = 0;
 
+#ifdef HAVE_GPM
+  mouse_position_hook = term_mouse_position;
+  Qmouse_face_window = Qnil;
+#endif
+
 #ifdef WINDOWSNT
   initialize_w32_display ();
 
@@ -2778,6 +3611,14 @@ bigger, or it may make it blink, or it may do nothing at all.  */);
   defsubr (&Stty_display_color_p);
   defsubr (&Stty_display_color_cells);
   defsubr (&Stty_no_underline);
+#ifdef HAVE_GPM
+  defsubr (&Sterm_open_connection);
+  defsubr (&Sterm_close_connection);
+
+  staticpro (&Qmouse_face_window);
+#endif /* HAVE_GPM */
+
+  fullscreen_hook = NULL;
 }
 
 /* arch-tag: 498e7449-6f2e-45e2-91dd-b7d4ca488193