[INCLUDED_FCNTL]: Don't include fcntl.h again.
authorRichard M. Stallman <rms@gnu.org>
Thu, 31 Mar 1994 23:17:23 +0000 (23:17 +0000)
committerRichard M. Stallman <rms@gnu.org>
Thu, 31 Mar 1994 23:17:23 +0000 (23:17 +0000)
(XTread_socket, MotionNotify and LeaveNotify cases):
If not in any frame, call clear_mouse_face.

(x_term_init): Set frame_up_to_date_hook.
(XTframe_up_to_date): New function.

(XTupdate_begin): Turn off mouse face display, and defer it.
(XTupdate_end): Undefer.
(mouse_face_defer): New variable.
(mouse_face_mouse_frame, mouse_face_mouse_x, mouse_face_mouse_y):
New variables.

(dumpglyphs): Handle HL = 3 by using mouse_face_face_id.
(note_mouse_movement): Check for mouse face.
(show_mouse_face, fast_find_position): New functions.
(clear_mouse_face): New functions.
(mouse_face_beg, mouse_face_end, mouse_face_face_id): New variables.
(mouse_face_window): New variable.
(syms_of_xterm): Init and staticpro it.

src/xterm.c

index 474082d..799a306 100644 (file)
@@ -1,5 +1,5 @@
 /* X Communication module for terminals which understand the X protocol.
-   Copyright (C) 1989, 1993 Free Software Foundation, Inc.
+   Copyright (C) 1989, 1993, 1994 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -71,7 +71,9 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #include "systty.h"
 #include "systime.h"
 
+#ifndef INCLUDED_FCNTL
 #include <fcntl.h>
+#endif
 #include <ctype.h>
 #include <errno.h>
 #include <setjmp.h>
@@ -275,6 +277,21 @@ unsigned int x_mouse_grabbed;
    it's somewhat accurate.  */
 static Time last_mouse_movement_time;
 
+/* 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.  */
+static int mouse_face_beg, mouse_face_end;
+static Lisp_Object mouse_face_window;
+static int mouse_face_face_id;
+
+/* FRAME and X, Y position of mouse when last checked for highlighting.  */
+static FRAME_PTR mouse_face_mouse_frame;
+static int mouse_face_mouse_x, mouse_face_mouse_y;
+
+/* Nonzero means defer mouse-motion highlighting.  */
+static int mouse_face_defer;
+
 #ifdef HAVE_X11
 /* `t' if a mouse button is depressed. */
 
@@ -293,6 +310,8 @@ extern Window requestor_window;
 /* Nonzero enables some debugging for the X interface code. */
 extern int _Xdebug;
 
+extern Qface, Qmouse_face;
+
 #else /* ! defined (HAVE_X11) */
 
 /* Bit patterns for the mouse cursor.  */
@@ -334,6 +353,10 @@ static void flashback ();
 static void redraw_previous_char ();
 static unsigned int x_x_to_emacs_modifiers ();
 
+static void note_mouse_highlight ();
+static void clear_mouse_face ();
+static void show_mouse_face ();
+
 #ifndef HAVE_X11
 static void dumpqueue ();
 #endif /* HAVE_X11 */
@@ -367,6 +390,13 @@ XTupdate_begin (f)
   highlight = 0;
 
   BLOCK_INPUT;
+
+  if (f == mouse_face_mouse_frame)
+    {
+      mouse_face_defer = 1;
+      if (!NILP (mouse_face_window))
+       clear_mouse_face ();
+    }
 #ifndef HAVE_X11
   dumpqueue ();
 #endif /* HAVE_X11 */
@@ -391,9 +421,32 @@ XTupdate_end (f)
 
   x_display_cursor (f, 1);
 
+  if (f == mouse_face_mouse_frame)
+    mouse_face_defer = 0;
+#if 0
+  /* This fails in the case of having updated only the echo area
+     if we have switched buffers.  In that case, FRAME_CURRENT_GLYPHS
+     has no relation to the current contents, and its charstarts
+     have no relation to the contents of the window-buffer.
+  I don't know a clean way to check
+     for that case.  window_end_valid isn't set up yet.  */
+  if (f == mouse_face_mouse_frame)
+    note_mouse_highlight (f, mouse_face_mouse_x, mouse_face_mouse_y);
+#endif
+
   XFlushQueue ();
   UNBLOCK_INPUT;
 }
+
+/* This is called when all windows on frame F are now up to date.  */
+
+static
+XTframe_up_to_date (f)
+     FRAME_PTR f;
+{
+  if (f == mouse_face_mouse_frame)
+    note_mouse_highlight (f, mouse_face_mouse_x, mouse_face_mouse_y);
+}
 \f
 /* External interface to control of standout mode.
    Call this when about to modify line at position VPOS
@@ -461,7 +514,8 @@ XTcursor_to (row, col)
 \f
 /* Display a sequence of N glyphs found at GP.
    WINDOW is the x-window to output to.  LEFT and TOP are starting coords.
-   HL is 1 if this text is highlighted, 2 if the cursor is on it.
+   HL is 1 if this text is highlighted, 2 if the cursor is on it,
+   3 if should appear in its mouse-face.
 
    FONT is the default font to use (for glyphs whose font-code is 0).
 
@@ -526,6 +580,10 @@ dumpglyphs (f, left, top, gp, n, hl)
        int defaulted = 1;
        int gc_temporary = 0;
 
+       /* HL = 3 means use a mouse face previously chosen.  */
+       if (hl == 3)
+         cf = mouse_face_face_id;
+
        /* First look at the face of the text itself.  */
        if (cf != 0)
          {
@@ -1202,7 +1260,7 @@ dumprectangle (f, left, top, cols, rows)
        || right > intborder + f->width * FONT_WIDTH (f->display.x->font))
       dumpborder (f, 0);
   }
-#endif /* HAVE_X11             Window manger does this for X11. */
+#endif /* not HAVE_X11         Window manger does this for X11. */
   
   /* Convert rectangle edges in pixels to edges in chars.
      Round down for left and top, up for right and bottom.  */
@@ -1810,6 +1868,7 @@ construct_menu_click (result, event, f)
    If the mouse is over a different glyph than it was last time, tell
    the mainstream emacs code by setting mouse_moved.  If not, ask for
    another motion event, so we can check again the next time it moves.  */
+
 static void
 note_mouse_movement (frame, event)
      FRAME_PTR frame;
@@ -1826,6 +1885,18 @@ note_mouse_movement (frame, event)
     {
       mouse_moved = 1;
       last_mouse_scroll_bar = Qnil;
+
+      note_mouse_highlight (frame, event->x, event->y);
+
+      /* Ask for another mouse motion event.  */
+      {
+       int dummy;
+
+       XQueryPointer (event->display, event->window,
+                      (Window *) &dummy, (Window *) &dummy,
+                      &dummy, &dummy, &dummy, &dummy,
+                      (unsigned int *) &dummy);
+      }
     }
   else
     {
@@ -1841,6 +1912,240 @@ note_mouse_movement (frame, event)
     }
 }
 
+/* Take proper action when the mouse has moved to position X, Y on frame F
+   as regards highlighting characters that have mouse-face properties.
+   Also dehighlighting chars where the mouse was before.  */
+
+static void
+note_mouse_highlight (f, x, y)
+     FRAME_PTR f;
+{
+  int row, column, portion;
+  XRectangle new_glyph;
+  Lisp_Object window;
+  struct window *w;
+
+  mouse_face_mouse_x = x;
+  mouse_face_mouse_y = y;
+  mouse_face_mouse_frame = f;
+
+  if (mouse_face_defer)
+    return;
+
+  /* Find out which glyph the mouse is on.  */
+  pixel_to_glyph_coords (f, x, y, &column, &row,
+                        &new_glyph, x_mouse_grabbed);
+
+  /* Which window is that in?  */
+  window = window_from_coordinates (f, column, row, &portion);
+  w = XWINDOW (window);
+
+  /* If we were displaying active text in another window, clear that.  */
+  if (! EQ (window, mouse_face_window))
+    clear_mouse_face ();
+
+  /* Are we in a window whose display is up to date?  */
+  if (WINDOWP (window) && portion == 0
+      && EQ (w->window_end_valid, Qt))
+    {
+      int *ptr = FRAME_CURRENT_GLYPHS (f)->charstarts[row];
+      int i, pos;
+
+      /* Find which buffer position the mouse corresponds to.  */
+      for (i = column; i >= 0; i--)
+       if (ptr[i] > 0)
+         break;
+      pos = ptr[i];
+      /* Is it outside the displayed active region (if any)?  */
+      if (pos > 0
+         && ! (EQ (window, mouse_face_window)
+               && pos >= mouse_face_beg && pos < mouse_face_end))
+       {
+         Lisp_Object mouse_face, overlay, position;
+         Lisp_Object *overlay_vec;
+         int len, noverlays, ignor1;
+
+         /* Yes.  Clear the display of the old active region, if any.  */
+         clear_mouse_face ();
+
+         /* Is this char mouse-active?  */
+         XSET (position, Lisp_Int, pos);
+
+         len = 10;
+         overlay_vec = (Lisp_Object *) xmalloc (len * sizeof (Lisp_Object));
+
+         /* Put all the overlays we want in a vector in overlay_vec.
+            Store the length in len.  */
+         noverlays = overlays_at (XINT (pos), 1, &overlay_vec, &len, &ignor1);
+         sort_overlays (overlay_vec, noverlays, w);
+
+         /* Find the highest priority overlay that has a mouse-face prop.  */
+         overlay = Qnil;
+         for (i = 0; i < noverlays; i++)
+           {
+             mouse_face = Foverlay_get (overlay_vec[i], Qmouse_face);
+             if (!NILP (mouse_face))
+               {
+                 overlay = overlay_vec[i];
+                 break;
+               }
+           }
+         free (overlay_vec);
+         /* 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.  */
+             mouse_face_beg = XFASTINT (before);
+             mouse_face_end = XFASTINT (after);
+             mouse_face_window = window;
+             mouse_face_face_id = compute_char_face (f, w, pos, 0, 0,
+                                                     &ignore, pos + 1, 1);
+
+             /* Display it as active.  */
+             show_mouse_face (1);
+           }
+         /* 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);
+             XSET (end, Lisp_Int,
+                   (BUF_ZV (XBUFFER (w->buffer))
+                    - 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.  */
+             mouse_face_beg = XFASTINT (before);
+             mouse_face_end = XFASTINT (after);
+             mouse_face_window = window;
+             mouse_face_face_id
+               = compute_char_face (f, w, pos, 0, 0,
+                                    &ignore, pos + 1, 1);
+
+             /* Display it as active.  */
+             show_mouse_face (1);
+           }
+       }
+      else if (pos <= 0)
+       clear_mouse_face ();
+    }
+}
+\f
+/* Find the row and column of position POS in window WINDOW.
+   Store them in *COLUMNP and *ROWP.
+   This assumes display in WINDOW is up to date.  */
+
+static int
+fast_find_position (window, pos, columnp, rowp)
+     Lisp_Object window;
+     int pos;
+     int *columnp, *rowp;
+{
+  struct window *w = XWINDOW (window);
+  FRAME_PTR f = XFRAME (WINDOW_FRAME (w));
+  int i;
+  int row;
+  int left = w->left;
+  int top = w->top;
+  int height = XFASTINT (w->height) - ! MINI_WINDOW_P (w);
+  int width = window_internal_width (w);
+  int *charstarts;
+
+  for (i = 0;
+       i < height;
+       i++)
+    {
+      int linestart = FRAME_CURRENT_GLYPHS (f)->charstarts[top + i][left];
+      if (linestart > pos)
+       break;
+      if (linestart > 0)
+       row = i;
+    }
+
+  charstarts = FRAME_CURRENT_GLYPHS (f)->charstarts[top + row];
+  for (i = 0; i < width; i++)
+    if (charstarts[left + i] == pos)
+      {
+       *rowp = row + top;
+       *columnp = i + left;
+       return 1;
+      }
+
+  return 0;
+}
+
+/* Display the active region described by mouse_face_*
+   in its mouse-face if HL > 0, in its normal face if HL = 0.  */
+
+static void
+show_mouse_face (hl)
+     int hl;
+{
+  int begcol, begrow, endcol, endrow;
+  struct window *w = XWINDOW (mouse_face_window);
+  int width = window_internal_width (w);
+  FRAME_PTR f = XFRAME (WINDOW_FRAME (w));
+  int i;
+
+  fast_find_position (mouse_face_window, mouse_face_beg,
+                     &begcol, &begrow);
+  fast_find_position (mouse_face_window, mouse_face_end,
+                     &endcol, &endrow);
+
+  x_display_cursor (f, 0);
+
+  for (i = begrow; i <= endrow; i++)
+    {
+      int column = (i == begrow ? begcol : w->left);
+      int endcolumn = (i == endrow ? endcol : w->left + width);
+      endcolumn = min (endcolumn, FRAME_CURRENT_GLYPHS (f)->used[i] - w->left),
+
+      dumpglyphs (f,
+                 CHAR_TO_PIXEL_COL (f, column),
+                 CHAR_TO_PIXEL_ROW (f, i),
+                 FRAME_CURRENT_GLYPHS (f)->glyphs[i] + column,
+                 endcolumn - column,
+                 /* Highlight with mouse face if hl > 0.  */
+                 hl > 0 ? 3 : 0);
+    }
+
+  x_display_cursor (f, 1);
+}
+
+/* Clear out the mouse-highlighted active region.
+   Redraw it unhighlighted first.  */
+
+static void
+clear_mouse_face ()
+{
+  if (! NILP (mouse_face_window))
+    show_mouse_face (0);
+
+  mouse_face_beg = -1;
+  mouse_face_end = -1;
+  mouse_face_window = Qnil;
+}
+\f
 static struct scroll_bar *x_window_to_scroll_bar ();
 static void x_scroll_bar_report_motion ();
 
@@ -3387,6 +3692,11 @@ XTread_socket (sd, bufp, numchars, waitp, expected)
        case LeaveNotify:
          f = x_any_window_to_frame (event.xcrossing.window);
 
+         if (f == mouse_face_mouse_frame)
+           /* If we move outside the frame,
+              then we're certainly no longer on any text in the frame.  */
+           clear_mouse_face ();
+
          if (event.xcrossing.focus)
            {
              if (! x_focus_event_frame)
@@ -3470,6 +3780,10 @@ XTread_socket (sd, bufp, numchars, waitp, expected)
 
                if (bar)
                  x_scroll_bar_note_movement (bar, &event);
+
+               /* If we move outside the frame,
+                  then we're certainly no longer on any text in the frame.  */
+               clear_mouse_face ();
              }
          }
 #ifdef USE_X_TOOLKIT
@@ -5393,6 +5707,7 @@ Check the DISPLAY environment variable or use \"-d\"\n",
   update_end_hook = XTupdate_end;
   set_terminal_window_hook = XTset_terminal_window;
   read_socket_hook = XTread_socket;
+  frame_up_to_date_hook = XTframe_up_to_date;
   cursor_to_hook = XTcursor_to;
   reassert_line_highlight_hook = XTreassert_line_highlight;
   mouse_position_hook = XTmouse_position;
@@ -5432,6 +5747,8 @@ syms_of_xterm ()
 {
   staticpro (&last_mouse_scroll_bar);
   last_mouse_scroll_bar = Qnil;
+  staticpro (&mouse_face_window);
+  mouse_face_window = Qnil;
 }
 #endif /* ! defined (HAVE_X11) */
 #endif /* ! defined (HAVE_X_WINDOWS) */