changeset 1720:4f5e3ac5d822

* frame.h (struct frame): New fields `can_have_scrollbars' and `has_vertical_scrollbars'. (FRAME_CAN_HAVE_SCROLLBARS, FRAME_HAS_VERTICAL_SCROLLBARS): New accessors, for both the MULTI_FRAME and non-MULTI_FRAME. (VERTICAL_SCROLLBAR_WIDTH, WINDOW_VERTICAL_SCROLLBAR, WINDOW_VERTICAL_SCROLLBAR_COLUMN, WINDOW_VERTICAL_SCROLLBAR_HEIGHT): New macros. * window.h (struct window): New field `vertical_scrollbar'. * xterm.h (struct x_display): vertical_scrollbars, judge_timestamp, vertical_scrollbar_extra: New fields. (struct scrollbar): New struct. (VERTICAL_SCROLLBAR_PIXEL_WIDTH, VERTICAL_SCROLLBAR_PIXEL_HEIGHT, VERTICAL_SCROLLBAR_LEFT_BORDER, VERTICAL_SCROLLBAR_RIGHT_BORDER, VERTICAL_SCROLLBAR_TOP_BORDER, VERTICAL_SCROLLBAR_BOTTOM_BORDER, CHAR_TO_PIXEL_WIDTH, CHAR_TO_PIXEL_HEIGHT, PIXEL_TO_CHAR_WIDTH, PIXEL_TO_CHAR_HEIGHT): New accessors and macros. * frame.c (make_frame): Initialize the `can_have_scrollbars' and `has_vertical_scrollbars' fields of the frame. * term.c (term_init): Note that TERMCAP terminals don't support scrollbars. (mouse_position_hook): Document new args. (set_vertical_scrollbar_hook, condemn_scrollbars_hook, redeem_scrollbar_hook, judge_scrollbars_hook): New hooks. * termhooks.h: Declare and document them. (enum scrollbar_part): New type. (struct input_event): Describe the new form of the scrollbar_click event type. Change `part' from a Lisp_Object to an enum scrollbar_part. Add a new field `scrollbar'. * keyboard.c (kbd_buffer_get_event): Pass appropriate new parameters to *mouse_position_hook, and make_lispy_movement. * xfns.c (x_set_vertical_scrollbar): New function. (x_figure_window_size): Use new macros to calculate frame size. (Fx_create_frame): Note that X Windows frames do support scroll bars. Default to "yes". * xterm.c: #include <X11/cursorfont.h> and "window.h". (x_vertical_scrollbar_cursor): New variable. (x_term_init): Initialize it. (last_mouse_bar, last_mouse_bar_frame, last_mouse_part, last_mouse_scroll_range_start, last_mouse_scroll_range_end): New variables. (XTmouse_position): Use them to return scrollbar movement events. Take new arguments, for that purpose. (x_window_to_scrollbar, x_scrollbar_create, x_scrollbar_set_handle, x_scrollbar_remove, x_scrollbar_move, XTset_scrollbar, XTcondemn_scrollbars, XTredeem_scrollbar, XTjudge_scrollbars, x_scrollbar_expose, x_scrollbar_background_expose, x_scrollbar_handle_click, x_scrollbar_handle_motion): New functions to implement scrollbars. (x_term_init): Set the termhooks.h hooks to point to them. (x_set_window_size): Use new macros to calculate frame size. Set vertical_scrollbar_extra field. (x_make_frame_visible): Use the frame accessor FRAME_HAS_VERTICAL_SCROLLBARS to decide if we need to map the frame's subwindows as well. (XTread_socket): Use new size-calculation macros from xterm.h when processing ConfigureNotify events. (x_wm_set_size_hint): Use PIXEL_TO_CHAR_WIDTH and PIXEL_TO_CHAR_HEIGHT macros. * ymakefile (xdisp.o): This now depends on termhooks.h. (xterm.o): This now depends on window.h. * xterm.h (struct x_display): Delete v_scrollbar, v_thumbup, v_thumbdown, v_slider, h_scrollbar, h_thumbup, h_thumbdown, h_slider, v_scrollbar_width, h_scrollbar_height fields. * keyboard.c (Qvscrollbar_part, Qvslider_part, Qvthumbup_part, Qvthumbdown_part, Qhscrollbar_part, Qhslider_part, Qhthumbup_part, Qhthumbdown_part, Qscrollbar_click): Deleted; part of an obsolete interface. (head_table): Removed from here as well. (syms_of_keyboard): And here. * keyboard.h: And here. (POSN_SCROLLBAR_BUTTON): Removed. * xscrollbar.h: File removed - no longer necessary. * xfns.c: Don't #include it any more. (Qhorizontal_scroll_bar, Qvertical_scroll_bar): Deleted. (syms_of_xfns): Don't initialize or staticpro them. (gray_bits): Salvaged from xscrollbar.h. (x_window_to_scrollbar): Deleted. (x_set_horizontal_scrollbar): Deleted. (enum x_frame_parm, x_frame_parms): Remove references to x_set_horizontal_scrollbar. (x_set_foreground_color, x_set_background_color, x_set_border_pixel): Remove special code to support scrollbars. (Fx_create_frame): Remove old scrollbar setup code. (install_vertical_scrollbar, install_horizontal_scrollbar, adjust_scrollbars, x_resize_scrollbars): Deleted. * xterm.c (construct_mouse_click): This doesn't need to take care of scrollbar clicks anymore. (XTread_socket): Remove old code to support scrollbars. Call new functions instead for events which occur in scrollbar windows. (XTupdate_end): Remove call to adjust_scrollbars; the main redisplay code takes care of that now. (enum window_type): Deleted. * ymakefile: Note that xfns.o no longer depends on xscrollbar.h. * xterm.c (x_set_mouse_position): Clip mouse position to be within frame. * xterm.c: Adjust the first line of each page to have a reasonable description. This makes pages-directory more useful. * xterm.c (x_do_pending_expose): Declare this routine only if HAVE_X11 is not #defined; X11 doesn't need it. (XTread_socket): Protect call to x_do_pending_expose with `#ifdef HAVE_X11'. * xterm.c (notice_mouse_movement): Deleted; obsolete and unused. Properly handle focus shift events, so the cursor is filled and hollow at the appropriate times, even in titleless windows. * xterm.c (x_focus_event_frame): New variable. (XTread_socket): When we receive a FocusIn event that's not NotifyPointer, record the frame in x_focus_event_frame. When we receive a FocusOut event that's not NotifyPointer, clear it. When we get a LeaveNotify event, don't take it seriously if we still have focus. * xterm.c (XTread_socket): Remove special code in EnterNotify case to handle scrollbars and fake mouse motion events. Change the meaning of focus redirection to make switching windows work properly. Fredirect_frame_focus has the details. * frame.h (focus_frame): Doc fix. [not MULTI_FRAME] (FRAME_FOCUS_FRAME): Make this Qnil, which indicates no focus redirection, instead of zero, which is selected_frame. * frame.c (make_frame): Initialize f->focus_frame to Qnil, rather than making it point to frame itself. (Fselect_frame): If changing the selected frame from FOO to BAR, make all redirections to FOO shift to BAR as well. Doc fix. (Fredirect_frame_focus): Doc fix. Accept nil as a valid redirection, not just as a default for FRAME. (Fframe_focus): Doc fix. * keyboard.c (kbd_buffer_store_event, kbd_buffer_get_event): Deal with focus redirections being nil. * xterm.c (XTframe_rehighlight): Doc fix. Deal with focus redirections being nil. * xterm.c (x_error_quitter): Just abort, so we can look at the core to see what happened. It's a pain to remember that you can't assign to FRAME->visible. Let's change all references to the `visible' member of struct frame to use the accessor macros, and then write a setter for the `visible' field that does the right thing. * frame.h (FRAME_VISIBLE_P): Make this not an l-value. (FRAME_SET_VISIBLE): New macro. * frame.c (make_terminal_frame, Fdelete_frame): Use FRAME_SET_VISIBLE. (Fframe_visible_p, Fvisible_frame_list): Use FRAME_VISIBLE_P and FRAME_ICONIFIED_P. * dispnew.c (Fredraw_display): Use the FRAME_VISIBLE_P and FRAME_GARBAGED_P accessors. * xdisp.c (redisplay): Use the FRAME_VISIBLE_P accessor. * xfns.c (x_set_foreground_color, x_set_background_color, x_set_cursor_color, x_set_border_pixel, x_set_icon_type): Use the FRAME_VISIBLE_P accessor. (Fx_create_frame): Use FRAME_SET_VISIBILITY. * xterm.c (clear_cursor, x_display_bar_cursor, x_display_box_cursor): Use FRAME_SET_VISIBILITY.
author Jim Blandy <jimb@redhat.com>
date Thu, 24 Dec 1992 06:21:14 +0000
parents 48f539ac6921
children 6ba3bca4c3de
files src/xterm.c
diffstat 1 files changed, 701 insertions(+), 306 deletions(-) [+]
line wrap: on
line diff
--- a/src/xterm.c	Thu Dec 24 06:19:31 1992 +0000
+++ b/src/xterm.c	Thu Dec 24 06:21:14 1992 +0000
@@ -38,6 +38,7 @@
 /* This may include sys/types.h, and that somehow loses
    if this is not done before the other system files.  */
 #include "xterm.h"
+#include <X11/cursorfont.h>
 
 #ifndef USG
 /* Load sys/types.h if not already loaded.
@@ -89,6 +90,7 @@
 #include "frame.h"
 #include "disptab.h"
 #include "buffer.h"
+#include "window.h"
 
 #ifdef HAVE_X11
 #define XMapWindow XMapRaised		/* Raise them when mapping. */
@@ -105,8 +107,9 @@
 
 /* Nonzero means we must reprint all windows
    because 1) we received an ExposeWindow event
-   or 2) we received too many ExposeRegion events to record.  */
-
+   or 2) we received too many ExposeRegion events to record.
+
+   This is never needed under X11.  */
 static int expose_all_windows;
 
 /* Nonzero means we must reprint all icon windows.  */
@@ -159,6 +162,9 @@
 
 Display *x_current_display;
 
+/* The cursor to use for vertical scrollbars on x_current_display.  */
+static Cursor x_vertical_scrollbar_cursor;
+
 /* Frame being updated by update_frame.  */
 /* This is set by XTupdate_begin and looked at by all the
    XT functions.  It is zero while not inside an update.
@@ -168,9 +174,18 @@
 static struct frame *updating_frame;
 
 /* The frame (if any) which has the X window that has keyboard focus.
-   Zero if none.  This is examined by Ffocus_frame in frame.c.  */
+   Zero if none.  This is examined by Ffocus_frame in frame.c.  Note
+   that a mere EnterNotify event can set this; if you need to know the
+   last frame specified in a FocusIn or FocusOut event, use
+   x_focus_event_frame.  */
 struct frame *x_focus_frame;
 
+/* The last frame mentioned in a FocusIn or FocusOut event.  This is
+   separate from x_focus_frame, because whether or not LeaveNotify
+   events cause us to lose focus depends on whether or not we have
+   received a FocusIn event for it.  */
+struct frame *x_focus_event_frame;
+
 /* The frame which currently has the visual highlight, and should get
    keyboard input (other sorts of input have the frame encoded in the
    event).  It points to the X focus frame's selected window's
@@ -260,7 +275,9 @@
 static int XTclear_end_of_line ();
 
 
-/* These hooks are called by update_frame at the beginning and end
+/* Starting and ending updates. 
+
+   These hooks are called by update_frame at the beginning and end
    of a frame update.  We record in `updating_frame' the identity
    of the frame being updated, so that the XT... functions do not
    need to take a frame as argument.  Most of the XT... functions
@@ -289,7 +306,9 @@
   UNBLOCK_INPUT;
 }
 
+#ifndef HAVE_X11
 static void x_do_pending_expose ();
+#endif
 
 static
 XTupdate_end (f)
@@ -304,9 +323,8 @@
   BLOCK_INPUT;
 #ifndef HAVE_X11
   dumpqueue ();
+  x_do_pending_expose ();
 #endif /* HAVE_X11 */
-  adjust_scrollbars (f);
-  x_do_pending_expose ();
 
   x_display_cursor (f, 1);
 
@@ -356,8 +374,8 @@
 /*  XTclear_frame ();  */
 }
 
-/* Set the nominal cursor position of the frame:
-   where display update commands will take effect.
+/* Set the nominal cursor position of the frame.
+   This is where display update commands will take effect.
    This does not affect the place where the cursor-box is displayed.  */
 
 static int
@@ -524,8 +542,8 @@
 }
 #endif /* ! 0 */
 
-/* Output some text at the nominal frame cursor position,
-   advancing the cursor over the text.
+/* Output some text at the nominal frame cursor position.
+   Advance the cursor over the text.
    Output LEN glyphs at START.
 
    `highlight', set up by XTreassert_line_highlight or XTchange_line_highlight,
@@ -577,7 +595,8 @@
   UNBLOCK_INPUT;
 }
 
-/* Erase the current text line from the nominal cursor position (inclusive)
+/* Clear to the end of the line.
+   Erase the current text line from the nominal cursor position (inclusive)
    to column FIRST_UNUSED (exclusive).  The idea is that everything
    from FIRST_UNUSED onward is already erased.  */
   
@@ -758,8 +777,9 @@
     }
 }
 
-/* Insert and delete character are not supposed to be used
-   because we are supposed to turn off the feature of using them.  */
+/* Insert and delete character.
+   These are not supposed to be used because we are supposed to turn
+   off the feature of using them.  */
 
 static 
 XTinsert_glyphs (start, len)
@@ -794,8 +814,8 @@
     flexlines = n;
 }
 
-/* Perform an insert-lines operation, inserting N lines
-   at a vertical position curs_y.  */
+/* Perform an insert-lines operation.
+   Insert N lines at a vertical position curs_y.  */
 
 static void
 stufflines (n)
@@ -955,6 +975,7 @@
   UNBLOCK_INPUT;
 }
 
+/* Support routines for exposure events.  */
 static void clear_cursor ();
 
 /* Output into a rectangle of an X-window (for frame F)
@@ -1072,10 +1093,11 @@
 }
 #endif /* HAVE_X11 */
 
-/* Process all expose events that are pending.
+/* Process all expose events that are pending, for X10.
    Redraws the cursor if necessary on any frame that
    is not in the process of being updated with update_frame.  */
 
+#ifndef HAVE_X11
 static void
 x_do_pending_expose ()
 {
@@ -1139,6 +1161,7 @@
     dumpqueue ();
 #endif /* ! defined (HAVE_X11) */
 }
+#endif
 
 #ifdef HAVE_X11
 static void
@@ -1245,9 +1268,9 @@
 }
 
 
-/* The focus has changed, or we have make a frame's selected window
-   point to a window on a different frame (this happens with global
-   minibuffer frames).  Shift the highlight as appropriate.  */
+/* The focus has changed, or we have redirected a frame's focus to
+   another frame (this happens when a frame uses a surrogate
+   minibuffer frame).  Shift the highlight as appropriate.  */
 static void
 XTframe_rehighlight ()
 {
@@ -1255,10 +1278,15 @@
 
   if (x_focus_frame)
     {
-      x_highlight_frame = XFRAME (FRAME_FOCUS_FRAME (x_focus_frame));
-      if (x_highlight_frame->display.nothing == 0)
-	XSET (FRAME_FOCUS_FRAME (x_focus_frame), Lisp_Frame,
-	      (x_highlight_frame = x_focus_frame));
+      x_highlight_frame =
+	((XTYPE (FRAME_FOCUS_FRAME (x_focus_frame)) == Lisp_Frame)
+	 ? XFRAME (FRAME_FOCUS_FRAME (x_focus_frame))
+	 : x_focus_frame);
+      if (! FRAME_LIVE_P (x_highlight_frame))
+	{
+	  FRAME_FOCUS_FRAME (x_focus_frame) = Qnil;
+	  x_highlight_frame = x_focus_frame;
+	}
     }
   else
     x_highlight_frame = 0;
@@ -1272,114 +1300,6 @@
     }
 }
 
-enum window_type
-{
-  no_window,
-  scrollbar_window,
-  text_window,
-};
-
-/* Position of the mouse in characters */
-unsigned int x_mouse_x, x_mouse_y;
-
-/* Offset in buffer of character under the pointer, or 0. */
-extern int mouse_buffer_offset;
-
-extern int buffer_posn_from_coords ();
-
-/* Symbols from xfns.c to denote the different parts of a window.  */
-extern Lisp_Object Qmodeline_part, Qtext_part;
-
-#if 0
-/* Set *RESULT to an emacs input_event corresponding to MOTION_EVENT.
-   F is the frame in which the event occurred.
-
-   WINDOW_TYPE says whether the event happened in a scrollbar window
-   or a text window, affecting the format of the event created.
-
-   PART specifies which part of the scrollbar the event happened in,
-   if WINDOW_TYPE == scrollbar_window.
-
-   If the mouse is over the same character as the last time we checked,
-   don't return an event; set result->kind to no_event.  */
-
-static void
-notice_mouse_movement (result, motion_event, f, window_type, part)
-     struct input_event *result;
-     XMotionEvent motion_event;
-     struct frame *f;
-     int window_type;
-     Lisp_Object part;
-{
-  int x, y, root_x, root_y, pix_x, pix_y;
-  unsigned int keys_and_buttons;
-  Window w, root_window;
-
-  /* Unless we decide otherwise below, return a non-event.  */
-  result->kind = no_event;
-  
-  if (XQueryPointer (x_current_display,
-		     FRAME_X_WINDOW (f),
-		     &root_window, &w,
-		     &root_x, &root_y, &pix_x, &pix_y,
-		     &keys_and_buttons)
-      == False)
-    return;
-
-#if 0
-  if (w == None)   /* Mouse no longer in window. */
-    return Qnil;
-#endif /* ! 0 */
-
-  pixel_to_glyph_translation (f, pix_x, pix_y, &x, &y);
-  if (x == x_mouse_x && y == x_mouse_y)
-    return;
-
-  x_mouse_x = x;
-  x_mouse_y = y;
-
-  /* What sort of window are we in now?  */
-  if (window_type == text_window)            /* Text part */
-    {
-      int modeline_p;
-
-      Vmouse_window = window_from_coordinates (f, x, y, &modeline_p);
-
-      if (XTYPE (Vmouse_window) == Lisp_Window)
-	mouse_buffer_offset
-	  = buffer_posn_from_coords (XWINDOW (Vmouse_window), x, y);
-      else
-	mouse_buffer_offset = 0;
-
-      if (EQ (Vmouse_window, Qnil))
-	Vmouse_frame_part = Qnil;
-      else if (modeline_p)
-	Vmouse_frame_part = Qmodeline_part;
-      else
-	Vmouse_frame_part = Qtext_part;
-      
-      result->kind = window_sys_event;
-      result->code = Qmouse_moved;
-
-      return;
-    }
-  else if (window_type == scrollbar_window)  /* Scrollbar */
-    {
-      Vmouse_window = f->selected_window;
-      mouse_buffer_offset = 0;
-      Vmouse_frame_part = part;
-
-      result->kind = window_sys_event;
-      result->code = Qmouse_moved;
-
-      return;
-    }
-
-  return;
-}
-#endif /* ! 0 */
-
-
 /* Mouse clicks and mouse movement.  Rah.  */
 #ifdef HAVE_X11
 
@@ -1531,30 +1451,21 @@
 	  | ((state & x_meta_mod_mask)		       ? meta_modifier  : 0));
 }
 
-extern struct frame *x_window_to_scrollbar ();
-extern Lisp_Object Vmouse_event;
-
 /* 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.
-
-   If PART and PREFIX are 0, then the event occurred in the text part;
-   otherwise it happened in a scrollbar. */
+   the mouse.  */
 
 static Lisp_Object
-construct_mouse_click (result, event, f, part, prefix)
+construct_mouse_click (result, event, f)
      struct input_event *result;
      XButtonEvent *event;
      struct frame *f;
-     int prefix;
-     Lisp_Object part;
 {
-  /* Initialize those fields text and scrollbar clicks hold in common.
-     Make the event type no_event; we'll change that when we decide
+  /* Make the event type no_event; we'll change that when we decide
      otherwise.  */
-  result->kind = no_event;
-  XSET (result->code, Lisp_Int, event->button);
+  result->kind = mouse_click;
+  XSET (result->code, Lisp_Int, event->button - Button1);
   result->timestamp = event->time;
   result->modifiers = (x_convert_modifiers (event->state)
 		       | (event->type == ButtonRelease
@@ -1575,33 +1486,14 @@
 	Vmouse_depressed = Qnil;
     }
 
-  if (! NILP (part))		/* Scrollbar event */
-    {
-      int pos, len;
-
-      pos = event->y - (f->display.x->v_scrollbar_width - 2);
-      x_mouse_x = pos;
-      len = ((FONT_HEIGHT (f->display.x->font) * f->height)
-	     + f->display.x->internal_border_width
-	     - (2 * (f->display.x->v_scrollbar_width - 2)));
-      x_mouse_y = len;
-
-      result->kind = scrollbar_click;
-      result->part = part;
-      XSET (result->x, Lisp_Int, (f->display.x->top_pos - event->y));
-      XSET (result->y, Lisp_Int, f->display.x->pixel_height);
-      result->frame = f;
-    }
-  else				/* Text Window Event */
-    {
-      int row, column;
-
-      pixel_to_glyph_coords (f, event->x, event->y, &column, &row, NULL);
-      result->kind = mouse_click;
-      XFASTINT (result->x) = column;
-      XFASTINT (result->y) = row;
-      result->frame = f;
-    }
+  {
+    int row, column;
+
+    pixel_to_glyph_coords (f, event->x, event->y, &column, &row, NULL);
+    XFASTINT (result->x) = column;
+    XFASTINT (result->y) = row;
+    result->frame = f;
+  }
 }
 
 
@@ -1628,6 +1520,15 @@
 static FRAME_PTR last_mouse_frame;
 static XRectangle last_mouse_glyph;
 
+/* If the last-checked mouse motion was in a scrollbar, this is that
+   scrollbar, the part being dragged, and the limits it is moving in.
+   Otherwise, this is zero.  */
+static struct scrollbar *last_mouse_bar;
+static FRAME_PTR last_mouse_bar_frame;
+static enum scrollbar_part last_mouse_part;
+static int last_mouse_scroll_range_start;
+static int last_mouse_scroll_range_end;
+
 /* This is a hack.  We would really prefer that XTmouse_position would
    return the time associated with the position it returns, but there
    doesn't seem to be any way to wrest the timestamp from the server
@@ -1679,8 +1580,10 @@
    */
 
 static void
-XTmouse_position (f, x, y, time)
+XTmouse_position (f, bar, part, x, y, time)
      FRAME_PTR *f;
+     struct scrollbar **bar;
+     enum scrollbar_part *part;
      Lisp_Object *x, *y;
      unsigned long *time;
 {
@@ -1724,17 +1627,35 @@
          try instead.  */
       guess = root;
 
-  *f = last_mouse_frame = x_window_to_frame (guess);
-  if (! *f)
-    *x = *y = Qnil;
+  if (last_mouse_bar)
+    {
+      *f = last_mouse_bar_frame;
+      *bar = last_mouse_bar;
+      *part = last_mouse_part;
+
+      if (iy < last_mouse_scroll_range_start)
+	iy = last_mouse_scroll_range_start;
+      if (iy > last_mouse_scroll_range_end)
+	iy = last_mouse_scroll_range_end;
+      XSETINT (*x, iy - last_mouse_scroll_range_start);
+      XSETINT (*y, (last_mouse_scroll_range_end
+		    - last_mouse_scroll_range_start));
+    }
   else
     {
-      pixel_to_glyph_coords (*f, ix, iy, &ix, &iy, &last_mouse_glyph);
-      XSET (*x, Lisp_Int, ix);
-      XSET (*y, Lisp_Int, iy);
+      *f = last_mouse_frame = x_window_to_frame (guess);
+      if (! *f)
+	*x = *y = Qnil;
+      else
+	{
+	  pixel_to_glyph_coords (*f, ix, iy, &ix, &iy, &last_mouse_glyph);
+	  XSET (*x, Lisp_Int, ix);
+	  XSET (*y, Lisp_Int, iy);
+	}
     }
 
   mouse_moved = 0;
+  last_mouse_bar = 0;
 
   /* I don't know how to find the time for the last movement; it seems
      like XQueryPointer ought to return it, but it doesn't.  So, we'll
@@ -1750,6 +1671,472 @@
 #define XEvent XKeyPressedEvent
 #endif /* ! defined (HAVE_X11) */
 
+/* Scrollbar support.  */
+
+/* Map an X window that implements a scroll bar to the struct
+   scrollbar representing it.  */
+static struct scrollbar *
+x_window_to_scrollbar (window_id)
+     Window window_id;
+{
+  Lisp_Object tail, frame;
+  struct frame *f;
+
+  for (tail = Vframe_list; CONSP (tail); tail = XCONS (tail)->cdr)
+    {
+      struct scrollbar *bar;
+      Lisp_Object frame = XCONS (tail)->car;
+
+      /* All elements of Vframe_list should be frames.  */
+      if (XTYPE (frame) != Lisp_Frame)
+	abort ();
+
+      /* Scan this frame's scrollbar list for a scrollbar with the
+         right window ID.  */
+      for (bar = XFRAME (frame)->display.x->vertical_scrollbars;
+	   bar;
+	   bar = bar->next)
+	if (bar->window == window_id)
+	  return bar;
+    }
+
+  return 0;
+}
+
+
+/* Open a new X window to serve as a scrollbar.  */
+static struct scrollbar *
+x_scrollbar_create (frame, top, left, width, height)
+     FRAME_PTR frame;
+     int top, left, width, height;
+{
+  struct x_display *d = frame->display.x;
+
+  /* We can't signal a malloc error from within redisplay, so call
+     malloc instead of xmalloc.  */
+  struct scrollbar *bar =
+    (struct scrollbar *) malloc (sizeof (struct scrollbar));
+
+  if (! bar)
+    return 0;
+
+  BLOCK_INPUT;
+
+  {
+    XSetWindowAttributes a;
+    unsigned long mask;
+
+    a.background_pixel = d->background_pixel;
+    a.border_pixel = d->foreground_pixel;
+    a.event_mask = (KeyPressMask
+		    | ButtonPressMask | ButtonReleaseMask
+		    | ButtonMotionMask);
+    a.cursor = x_vertical_scrollbar_cursor;
+
+    mask = (CWBackPixel | CWBorderPixel | CWEventMask | CWCursor);
+
+    bar->window =
+      XCreateWindow (x_current_display, FRAME_X_WINDOW (frame),
+
+		     /* Position and size of scrollbar.  */
+		     top, left, width, height,
+
+		     /* Border width, depth, class, and visual.  */
+		     1, CopyFromParent, CopyFromParent, CopyFromParent,
+
+		     /* Attributes.  */
+		     mask, &a);
+  }
+
+  bar->frame = frame;
+  bar->top = top;
+  bar->left = left;
+  bar->width = width;
+  bar->height = height;
+  bar->start = bar->end = 0;
+  bar->judge_timestamp = d->judge_timestamp;
+  bar->dragging = -1;
+
+  /* Add bar to its frame's list of scroll bars.  */
+  bar->next = d->vertical_scrollbars;
+  d->vertical_scrollbars = bar;
+
+  XMapWindow (x_current_display, bar->window);
+
+  UNBLOCK_INPUT;
+}
+
+/* Draw BAR's handle in the proper position.  */
+static void
+x_scrollbar_set_handle (bar, start, end)
+     struct scrollbar *bar;
+     int start, end;
+{
+  BLOCK_INPUT;
+
+  {
+    int inside_width = (bar->width
+			- VERTICAL_SCROLLBAR_LEFT_BORDER
+			- VERTICAL_SCROLLBAR_RIGHT_BORDER);
+    int inside_height = (bar->height
+			 - VERTICAL_SCROLLBAR_TOP_BORDER
+			 - VERTICAL_SCROLLBAR_BOTTOM_BORDER);
+
+    /* Make sure the values are reasonable, and try to preserve
+       the distance between start and end.  */
+    if (end < start)
+      end = start;
+    if (start < VERTICAL_SCROLLBAR_TOP_BORDER)
+      {
+	end = VERTICAL_SCROLLBAR_TOP_BORDER + (end - start);
+	start = VERTICAL_SCROLLBAR_TOP_BORDER;
+      }
+    if (end > bar->height - VERTICAL_SCROLLBAR_BOTTOM_BORDER)
+      {
+	start = ((bar->height - VERTICAL_SCROLLBAR_BOTTOM_BORDER)
+		 - (end - start));
+	end = bar->height - VERTICAL_SCROLLBAR_BOTTOM_BORDER;
+      }
+    if (start < VERTICAL_SCROLLBAR_TOP_BORDER)
+      start = VERTICAL_SCROLLBAR_TOP_BORDER;
+
+    /* Draw the empty space above the handle.  */
+    XClearArea (x_current_display, bar->window,
+
+		/* x, y, width, height, and exposures.  */
+		VERTICAL_SCROLLBAR_LEFT_BORDER,
+		VERTICAL_SCROLLBAR_TOP_BORDER,
+		inside_width + 1, start + 1,
+		False);
+
+    /* Draw the handle itself.  */
+    XFillRectangle (x_current_display, bar->window,
+		    bar->frame->display.x->normal_gc,
+
+		    /* x, y, width, height */
+		    VERTICAL_SCROLLBAR_LEFT_BORDER, start,
+		    inside_width, (end - start) + 1);
+
+
+    /* Draw the empty space below the handle.  */
+    XClearArea (x_current_display, bar->window,
+
+		/* x, y, width, height, and exposures.  */
+		VERTICAL_SCROLLBAR_LEFT_BORDER,
+		VERTICAL_SCROLLBAR_TOP_BORDER + end,
+		inside_width + 1, (inside_height - end) + 1,
+		False);
+
+    bar->start = start;
+    bar->end = end;
+  }
+
+  UNBLOCK_INPUT;
+}
+
+/* Remove the scrollbar BAR.  */
+static void
+x_scrollbar_remove (bar)
+     struct scrollbar *bar;
+{
+  BLOCK_INPUT;
+
+  /* Remove bar from the frame's list.  */
+  {
+    struct scrollbar **ptr;
+
+    for (ptr = &bar->frame->display.x->vertical_scrollbars;
+	 *ptr;
+	 ptr = &(*ptr)->next)
+      if (*ptr == bar)
+	{
+	  *ptr = bar->next;
+	  break;
+	}
+  }
+
+  /* Destroy the window.  */
+  XDestroyWindow (x_current_display, bar->window);
+
+  /* Free the storage.  */
+  free (bar);
+
+  UNBLOCK_INPUT;
+}
+
+static void
+x_scrollbar_move (bar, top, left, width, height)
+     struct scrollbar *bar;
+     int top, left, width, height;
+{
+  BLOCK_INPUT;
+
+  {
+    XWindowChanges wc;
+    unsigned int mask = 0;
+
+    wc.x = left;
+    wc.y = top;
+    wc.width = width;
+    wc.height = height;
+
+    if (left != bar->left)     mask |= CWX;
+    if (top != bar->top)       mask |= CWY;
+    if (width != bar->width)   mask |= CWWidth;
+    if (height != bar->height) mask |= CWHeight;
+
+    XConfigureWindow (x_current_display, bar->window, mask, &wc);
+  }
+
+  UNBLOCK_INPUT;
+}
+
+/* Set BAR to be the vertical scroll bar for WINDOW.  Set its handle
+   to indicate that we are displaying PORTION characters out of a
+   total of WHOLE characters, starting at POSITION.  Return BAR.  If
+   BAR is zero, create a new scrollbar and return a pointer to it.  */
+static struct scrollbar *
+XTset_scrollbar (bar, window, portion, whole, position)
+     struct scrollbar *bar;
+     struct window *window;
+     int portion, whole, position;
+{
+  FRAME_PTR f = XFRAME (WINDOW_FRAME (window));
+  struct x_display *d = f->display.x;
+  int top = XINT (window->top);
+  int left = WINDOW_VERTICAL_SCROLLBAR_COLUMN (window);
+  int height = WINDOW_VERTICAL_SCROLLBAR_HEIGHT (window);
+
+  /* Where should this scrollbar be, pixelwise?  */
+  int pixel_top  = (d->internal_border_width + top  * FONT_HEIGHT (d->font));
+  int pixel_left = (d->internal_border_width + left * FONT_WIDTH  (d->font));
+  int pixel_width = VERTICAL_SCROLLBAR_PIXEL_WIDTH (f);
+  int pixel_height = VERTICAL_SCROLLBAR_PIXEL_HEIGHT (f, height);
+
+  /* Does the scrollbar exist yet?  */
+  if (! bar)
+    bar = x_scrollbar_create (f,
+			      pixel_top, pixel_left,
+			      pixel_width, pixel_height);
+  else
+    /* It may just need to be moved and resized.  */
+    x_scrollbar_move (bar, pixel_top, pixel_left, pixel_width, pixel_height);
+
+  /* Set the scrollbar's current state, unless we're currently being
+     dragged.  */
+
+  if (bar && bar->dragging == -1)
+    {
+      int inside_height = (pixel_height
+			   - VERTICAL_SCROLLBAR_TOP_BORDER
+			   - VERTICAL_SCROLLBAR_BOTTOM_BORDER);
+      int start = (position * inside_height) / whole;
+      int end = ((position + portion) * inside_height) / whole;
+
+      x_scrollbar_set_handle (bar, start, end);
+    }
+
+  return bar;
+}
+
+/* The following three hooks are used when we're doing a thorough
+   redisplay of the frame.  We don't explicitly know which scrollbars
+   are going to be deleted, because keeping track of when windows go
+   away is a real pain - can you say set-window-configuration?
+   Instead, we just assert at the beginning of redisplay that *all*
+   scrollbars are to be removed, and then save scrollbars from the
+   firey pit when we actually redisplay their window.  */
+
+/* Arrange for all scrollbars on FRAME to be removed at the next call
+   to `*judge_scrollbars_hook'.  A scrollbar may be spared if
+   `*redeem_scrollbar_hook' is applied to it before the judgement.  */
+static void 
+XTcondemn_scrollbars (frame)
+     FRAME_PTR frame;
+{
+  /* Any scrollbars which don't get caught up to this will be deleted.  */
+  frame->display.x->judge_timestamp++;
+}
+
+/* Unmark BAR for deletion in this judgement cycle.  */
+static void
+XTredeem_scrollbar (bar)
+     struct scrollbar *bar;
+{
+  bar->judge_timestamp = bar->frame->display.x->judge_timestamp;
+}
+
+/* Remove all scrollbars on FRAME that haven't been saved since the
+   last call to `*condemn_scrollbars_hook'.  */
+static void
+XTjudge_scrollbars(frame)
+     FRAME_PTR frame;
+{
+  int judge_timestamp = frame->display.x->judge_timestamp;
+  struct scrollbar *bar, *next;
+
+  for (bar = frame->display.x->vertical_scrollbars; bar; bar = next)
+    {
+      next = bar->next;
+      if (bar->judge_timestamp < judge_timestamp)
+	x_scrollbar_remove (bar);
+    }
+}
+
+
+/* Handle an Expose or GraphicsExpose event on a scrollbar.  */
+static void
+x_scrollbar_expose (bar, event)
+     struct scrollbar *bar;
+     XEvent *event;
+{
+  BLOCK_INPUT;
+
+  x_scrollbar_set_handle (bar, bar->start, bar->end);
+
+  /* Draw the extra-thick border on the right.  */
+  XFillRectangle (x_current_display, bar->window,
+		  bar->frame->display.x->normal_gc,
+
+		  /* x, y, width, height */
+		  bar->width - VERTICAL_SCROLLBAR_RIGHT_BORDER, 0,
+		  VERTICAL_SCROLLBAR_RIGHT_BORDER, bar->height + 1);
+
+  UNBLOCK_INPUT;
+}
+
+/* Handle an exposure event which might be over the extra scrollbar space.  */
+static void
+x_scrollbar_background_expose (frame, event)
+     FRAME_PTR frame;
+     XEvent *event;
+{
+  /* Where is the extra scrollbar space, anyway?  */
+  int width = VERTICAL_SCROLLBAR_PIXEL_WIDTH (frame);
+  int height = PIXEL_HEIGHT (frame);
+  int x = PIXEL_WIDTH (frame) - width;
+  int y = 0;
+
+  BLOCK_INPUT;
+
+  /* Clear it out.  */
+  XClearArea (x_current_display, FRAME_X_WINDOW (frame),
+
+	      /* x, y, width, height, expose */
+	      x, y, width+1, height+1, False);
+
+  /* Draw the border.  */
+  XDrawRectangle (x_current_display, FRAME_X_WINDOW (frame),
+		  frame->display.x->normal_gc,
+		  x, y, width, height);
+
+  /* Draw the extra-thick border on the right edge.  */
+  XFillRectangle (x_current_display, FRAME_X_WINDOW (frame),
+		 frame->display.x->normal_gc,
+		 x + width - VERTICAL_SCROLLBAR_RIGHT_BORDER, 0,
+		 VERTICAL_SCROLLBAR_RIGHT_BORDER, height + 1);
+
+  UNBLOCK_INPUT;
+}
+
+/* Handle a mouse click on the scrollbar BAR.  If *EMACS_EVENT's kind
+   is set to something other than no_event, it is enqueued.  */
+static void
+x_scrollbar_handle_click (bar, event, emacs_event)
+     struct scrollbar *bar;
+     XEvent *event;
+     struct input_event *emacs_event;
+{
+  emacs_event->kind = scrollbar_click;
+  XSETINT (emacs_event->code, event->xbutton.button - Button1);
+  emacs_event->modifiers =
+    (x_convert_modifiers (event->xbutton.state)
+     | (event->type == ButtonRelease
+	? up_modifier
+	: down_modifier));
+  emacs_event->part =
+    ((event->xbutton.x < bar->start) ? scrollbar_above_handle
+     : (event->xbutton.x < bar->end) ? scrollbar_handle
+     : scrollbar_below_handle);
+  emacs_event->scrollbar = bar;
+
+  if (event->xbutton.y < VERTICAL_SCROLLBAR_TOP_BORDER)
+    event->xbutton.y = VERTICAL_SCROLLBAR_TOP_BORDER;
+  if (event->xbutton.y > bar->height - VERTICAL_SCROLLBAR_BOTTOM_BORDER)
+    event->xbutton.y = bar->height - VERTICAL_SCROLLBAR_BOTTOM_BORDER;
+  XSETINT (emacs_event->x,
+	   event->xbutton.y - VERTICAL_SCROLLBAR_TOP_BORDER);
+  XSETINT (emacs_event->y,
+	   (bar->height
+	    - VERTICAL_SCROLLBAR_TOP_BORDER
+	    - VERTICAL_SCROLLBAR_BOTTOM_BORDER));
+
+  emacs_event->frame = bar->frame;
+  emacs_event->timestamp = event->xbutton.time;
+
+  if (event->type == ButtonPress
+      && emacs_event->part == scrollbar_handle)
+    bar->dragging = event->xbutton.x - bar->start;
+  else
+    {
+      int new_start = event->xbutton.x - bar->dragging;
+      int new_end = new_start + (bar->end - bar->start);
+
+      x_scrollbar_set_handle (bar, new_start, new_end);
+      bar->dragging = -1;
+    }
+}
+
+
+/* Handle some mouse motion while someone is dragging the scrollbar.  */
+static void
+x_scrollbar_handle_motion (bar, event)
+     struct scrollbar *bar;
+     XEvent *event;
+{
+  last_mouse_movement_time = event->xmotion.time;
+
+  mouse_moved = 1;
+  last_mouse_bar = bar;
+  last_mouse_bar_frame = bar->frame;
+  last_mouse_part = (bar->dragging == -1 ? scrollbar_handle
+		     : (event->xbutton.x < bar->start) ? scrollbar_above_handle
+		     : (event->xbutton.x < bar->end) ? scrollbar_handle
+		     : scrollbar_below_handle);
+  last_mouse_scroll_range_start = bar->top + VERTICAL_SCROLLBAR_TOP_BORDER;
+  last_mouse_scroll_range_end = (bar->top
+				 + bar->height
+				 - VERTICAL_SCROLLBAR_BOTTOM_BORDER);
+
+  /* If we're dragging the bar, display it.  */
+  if (bar->dragging != -1)
+    {
+      /* Where should the handle be now?  */
+      int new_start = event->xmotion.x - bar->dragging;
+
+      if (new_start != bar->start)
+	{
+	  int new_end = new_start + (bar->end - bar->start);
+	
+	  x_scrollbar_set_handle (bar, new_start, new_end);
+	}
+    }
+
+  /* Call XQueryPointer so we'll get an event the next time the mouse
+     moves and we can see *still* on the same position.  */
+  {
+    int dummy;
+      
+    XQueryPointer (event->xmotion.display, event->xmotion.window,
+		   (Window *) &dummy, (Window *) &dummy,
+		   &dummy, &dummy, &dummy, &dummy,
+		   (unsigned int *) &dummy);
+  }
+}
+
+
+
+/* The main X event-reading loop - XTread_socket.  */
 
 /* Timestamp of enter window event.  This is only used by XTread_socket,
    but we have to put it out here, since static variables within functions
@@ -1931,19 +2318,35 @@
 		  SET_FRAME_GARBAGED (f);
 		}
 	      else
-		dumprectangle (x_window_to_frame (event.xexpose.window),
-			       event.xexpose.x, event.xexpose.y,
-			       event.xexpose.width, event.xexpose.height);
+		{
+		  dumprectangle (x_window_to_frame (event.xexpose.window),
+				 event.xexpose.x, event.xexpose.y,
+				 event.xexpose.width, event.xexpose.height);
+		  x_scrollbar_background_expose (f, &event);
+		}
+	    }
+	  else
+	    {
+	      struct scrollbar *bar
+		= x_window_to_scrollbar (event.xexpose.window);
+
+	      if (bar)
+		x_scrollbar_expose (bar, &event);
 	    }
 	  break;
 
 	case GraphicsExpose:	/* This occurs when an XCopyArea's
 				  source area was obscured or not
 				  available.*/
-	  dumprectangle (x_window_to_frame (event.xgraphicsexpose.drawable),
-			 event.xgraphicsexpose.x, event.xgraphicsexpose.y,
-			 event.xgraphicsexpose.width,
-			 event.xgraphicsexpose.height);
+	  f = x_window_to_frame (event.xgraphicsexpose.drawable);
+	  if (f)
+	    {
+	      dumprectangle (f,
+			     event.xgraphicsexpose.x, event.xgraphicsexpose.y,
+			     event.xgraphicsexpose.width,
+			     event.xgraphicsexpose.height);
+	      x_scrollbar_background_expose (f, &event);
+	    }
 	  break;
 
 	case NoExpose:		/* This occurs when an XCopyArea's
@@ -2000,20 +2403,15 @@
 
 #ifdef HAVE_X11
 	case UnmapNotify:
-	  {
-	    XWMHints *hints;
-
-	    f = x_window_to_frame (event.xunmap.window);
-	    if (f)		/* F may no longer exist if
+	  f = x_window_to_frame (event.xunmap.window);
+	  if (f)		/* F may no longer exist if
 				   the frame was deleted.  */
-	      {
-		/* While a frame is unmapped, display generation is
-		   disabled; you don't want to spend time updating a
-		   display that won't ever be seen.  */
-		f->async_visible = 0;
-		x_mouse_x = x_mouse_y = -1;
-	      }
-	  }
+	    {
+	      /* While a frame is unmapped, display generation is
+		 disabled; you don't want to spend time updating a
+		 display that won't ever be seen.  */
+	      f->async_visible = 0;
+	    }
 	  break;
 
 	case MapNotify:
@@ -2046,6 +2444,7 @@
 #ifdef HAVE_X11
 	case KeyPress:
 	  f = x_window_to_frame (event.xkey.window);
+
 	  if (f != 0)
 	    {
 	      KeySym keysym;
@@ -2165,18 +2564,18 @@
 #endif /* ! defined (HAVE_X11) */
 
 #ifdef HAVE_X11
+
+	  /* Here's a possible interpretation of the whole
+	     FocusIn-EnterNotify FocusOut-LeaveNotify mess.  If you get a
+	     FocusIn event, you have to get a FocusOut event before you
+	     relinquish the focus.  If you haven't received a FocusIn event,
+	     then a mere LeaveNotify is enough to free you.  */
+
 	case EnterNotify:
 	  f = x_window_to_frame (event.xcrossing.window);
 
-	  if (event.xcrossing.detail == NotifyInferior)	/* Left Scrollbar */
-	    ;
-	  else if (event.xcrossing.focus)		/* Entered Window */
+	  if (event.xcrossing.focus)		/* Entered Window */
 	    {
-	      /* If we decide we want to generate an event to be seen
-		 by the rest of Emacs, we put it here.  */
-	      struct input_event emacs_event;
-	      emacs_event.kind = no_event;
-
 	      /* Avoid nasty pop/raise loops. */
 	      if (f && (!(f->auto_raise)
 			|| !(f->auto_lower)
@@ -2185,58 +2584,45 @@
 		  x_new_focus_frame (f);
 		  enter_timestamp = event.xcrossing.time;
 		}
-#if 0
-	      else if ((f = x_window_to_scrollbar (event.xcrossing.window,
-						   &part, &prefix)))
-		/* Fake a motion event */
-		notice_mouse_movement (&emacs_event,
-				       event.xmotion, f, scrollbar_window,
-				       part);
-#endif /* ! 0 */
-
-#if 0
-	      if (! EQ (Vx_send_mouse_movement_events, Qnil)
-		  && numchars >= 1
-		  && emacs_event.kind != no_event)
-		{
-		  bcopy (&emacs_event, bufp, sizeof (struct input_event));
-		  bufp++;
-		  count++;
-		  numchars--;
-		}
-#endif /* ! 0 */
 	    }
 	  else if (f == x_focus_frame)
 	    x_new_focus_frame (0);
-#if 0
-	  else if (f = x_window_to_frame (event.xcrossing.window))
-	    x_mouse_frame = f;
-#endif /* ! 0 */
 
 	  break;
 
 	case FocusIn:
 	  f = x_window_to_frame (event.xfocus.window);
+	  if (event.xfocus.detail != NotifyPointer) 
+	    x_focus_event_frame = f;
 	  if (f)
 	    x_new_focus_frame (f);
 	  break;
 
+
 	case LeaveNotify:
-	  if (event.xcrossing.detail != NotifyInferior
-	      && event.xcrossing.subwindow == None
-	      && event.xcrossing.mode == NotifyNormal)
+	  f = x_window_to_frame (event.xcrossing.window);
+
+	  if (event.xcrossing.focus)
 	    {
-	      f = x_window_to_frame (event.xcrossing.window);
-
-	      if (event.xcrossing.focus)
+	      if (! x_focus_event_frame)
+		x_new_focus_frame (0);
+	      else
 		x_new_focus_frame (f);
-	      else if (f == x_focus_frame)
+	    }
+	  else 
+	    {
+	      if (f == x_focus_event_frame)
+		x_focus_event_frame = 0;
+	      if (f == x_focus_frame)
 		x_new_focus_frame (0);
 	    }
 	  break;
 
 	case FocusOut:
 	  f = x_window_to_frame (event.xfocus.window);
+	  if (event.xfocus.detail != NotifyPointer
+	      && f == x_focus_event_frame)
+	    x_focus_event_frame = 0;
 	  if (f && f == x_focus_frame)
 	    x_new_focus_frame (0);
 	  break;
@@ -2283,13 +2669,14 @@
 	    f = x_window_to_frame (event.xmotion.window);
 	    if (f)
 	      note_mouse_position (f, &event.xmotion);
-#if 0
-	    else if ((f = x_window_to_scrollbar (event.xmotion.window,
-						 &part, &prefix)))
+	    else
 	      {
-		What should go here?
+		struct scrollbar *bar =
+		  x_window_to_scrollbar (event.xmotion.window);
+
+		if (bar)
+		  x_scrollbar_handle_motion (bar, &event);
 	      }
-#endif /* ! 0 */
 	  }
 	  break;
 
@@ -2300,14 +2687,8 @@
 	    if (!f)
 	      break;
 
-	    columns = ((event.xconfigure.width -
-			(2 * f->display.x->internal_border_width)
-			- f->display.x->v_scrollbar_width)
-		       / FONT_WIDTH (f->display.x->font));
-	    rows = ((event.xconfigure.height -
-		     (2 * f->display.x->internal_border_width)
-		     - f->display.x->h_scrollbar_height)
-		    / FONT_HEIGHT (f->display.x->font));
+	    columns = PIXEL_TO_CHAR_WIDTH (f, event.xconfigure.width);
+	    rows = PIXEL_TO_CHAR_HEIGHT (f, event.xconfigure.height);
 
 	    /* Even if the number of character rows and columns has
 	       not changed, the font size may have changed, so we need
@@ -2318,7 +2699,6 @@
 		|| event.xconfigure.height != f->display.x->pixel_height)
 	      {
 		change_frame_size (f, rows, columns, 0, 1);
-		x_resize_scrollbars (f);
 		SET_FRAME_GARBAGED (f);
 	      }
 
@@ -2339,21 +2719,19 @@
 
 	    f = x_window_to_frame (event.xbutton.window);
 	    if (f)
-	      if (!x_focus_frame || (f == x_focus_frame))
-		construct_mouse_click (&emacs_event,
-				       &event, f, Qnil, 0);
-	      else
-		continue;
+	      {
+		if (!x_focus_frame || (f == x_focus_frame))
+		  construct_mouse_click (&emacs_event,
+					 &event, f, Qnil, 0);
+	      }
 	    else
-	      if ((f = x_window_to_scrollbar (event.xbutton.window,
-					      &part, &prefix)))
-		{
-		  if (!x_focus_frame || (selected_frame == x_focus_frame))
-		    construct_mouse_click (&emacs_event,
-					   &event, f, part, prefix);
-		  else
-		    continue;
-		}
+	      {
+		struct scrollbar *bar =
+		  x_window_to_scrollbar (event.xbutton.window);
+
+		if (bar)
+		  x_scrollbar_handle_click (bar, &event, &emacs_event);
+	      }
 
 	    if (numchars >= 1 && emacs_event.kind != no_event)
 	      {
@@ -2452,8 +2830,10 @@
 #endif /* ! defined (HAVE_SELECT) */
 #endif /* ! 0 */
 
+#ifndef HAVE_X11
   if (updating_frame == 0)
     x_do_pending_expose ();
+#endif
 
   UNBLOCK_INPUT;
   return count;
@@ -2525,6 +2905,9 @@
 #endif /* HAVE_X11 */
 
 
+/* Drawing the cursor.  */
+
+
 /* Draw a hollow box cursor.  Don't change the inside of the box.  */
 
 static void
@@ -2573,7 +2956,7 @@
 {
   int mask;
 
-  if (! f->visible
+  if (! FRAME_VISIBLE_P (f)
       || f->phys_cursor_x < 0)
     return;
 
@@ -2608,7 +2991,7 @@
   register int y1;
   register int y2;
 
-  if (! f->visible || (! on && f->phys_cursor_x < 0))
+  if (! FRAME_VISIBLE_P (f) || (! on && f->phys_cursor_x < 0))
     return;
 
 #ifdef HAVE_X11
@@ -2685,7 +3068,7 @@
       curs_y = FRAME_CURSOR_Y (f);
     }
 
-  if (! f->visible)
+  if (! FRAME_VISIBLE_P (f))
     return;
 
   /* If cursor is off and we want it off, return quickly.  */
@@ -2950,6 +3333,10 @@
 {
   char buf[256];
 
+  /* While we're testing Emacs 19, we'll just dump core whenever we
+     get an X error, so we can figure out why it happened.  */
+  abort ();
+
   /* Note that there is no real way portable across R3/R4 to get the 
      original error handler.  */
 
@@ -3041,6 +3428,8 @@
 #endif /* ! 0 */
 
 
+/* Changing the font of the frame.  */
+
 /* Set the font of the x-window specified by frame F
    to the font named NEWNAME.  This is safe to use
    even before F has an actual x-window.  */
@@ -3171,6 +3560,8 @@
 }
 #endif /* ! defined (HAVE_X11) */
 
+/* X Window sizes and positions.  */
+
 x_calc_absolute_position (f)
      struct frame *f;
 {
@@ -3227,10 +3618,12 @@
   BLOCK_INPUT;
 
   check_frame_size (f, &rows, &cols);
-  pixelwidth =  (cols * FONT_WIDTH (f->display.x->font) + 2 * ibw
-		 + f->display.x->v_scrollbar_width);
-  pixelheight = (rows * FONT_HEIGHT (f->display.x->font) + 2 * ibw
-		 + f->display.x->h_scrollbar_height);
+  f->display.x->vertical_scrollbar_extra =
+    (FRAME_HAS_VERTICAL_SCROLLBARS (f)
+     ? VERTICAL_SCROLLBAR_PIXEL_WIDTH (f)
+     : 0);
+  pixelwidth = CHAR_TO_PIXEL_WIDTH (f, cols);
+  pixelheight = CHAR_TO_PIXEL_HEIGHT (f, rows);
 
 #ifdef HAVE_X11
   x_wm_set_size_hint (f, 0);
@@ -3261,6 +3654,7 @@
 }
 #endif /* HAVE_X11 */
 
+/* Mouse warping, focus shifting, raising and lowering.  */
 
 x_set_mouse_position (f, x, y)
      struct frame *f;
@@ -3270,28 +3664,20 @@
 
   x_raise_frame (f);
 
-  if (x < 0)
-    pix_x = (FRAME_WIDTH (f)
-             * FONT_WIDTH (f->display.x->font)
-             + 2 * f->display.x->internal_border_width
-             + f->display.x->v_scrollbar_width) / 2;
-  else
-    pix_x = x * FONT_WIDTH (f->display.x->font) + 2; /* add 2 pixels to each
-       						 dimension to move the
-       						 mouse into the char
-       						 cell */
-
-  if (y < 0)
-    pix_y = (FRAME_HEIGHT (f)
-             * FONT_HEIGHT (f->display.x->font)
-             + 2 * f->display.x->internal_border_width
-             + f->display.x->h_scrollbar_height) / 2;
-  else
-    pix_y = y * FONT_HEIGHT (f->display.x->font) + 2;
+  pix_x = (f->display.x->internal_border_width
+	   + x * FONT_WIDTH (f->display.x->font)
+	   + FONT_WIDTH (f->display.x->font) / 2);
+  pix_y = (f->display.x->internal_border_width
+	   + y * FONT_HEIGHT (f->display.x->font)
+	   + FONT_HEIGHT (f->display.x->font) / 2);
+
+  if (pix_x < 0) pix_x = 0;
+  if (pix_x > PIXEL_WIDTH (f)) pix_x = PIXEL_WIDTH (f);
+
+  if (pix_y < 0) pix_y = 0;
+  if (pix_y > PIXEL_HEIGHT (f)) pix_y = PIXEL_HEIGHT (f);
 
   BLOCK_INPUT;
-  x_mouse_x = x;
-  x_mouse_y = y;
 
   XWarpMousePointer (FRAME_X_WINDOW (f), pix_x, pix_y);
   UNBLOCK_INPUT;
@@ -3368,7 +3754,7 @@
 	x_wm_set_window_state (f, NormalState);
 
       XMapWindow (XDISPLAY FRAME_X_WINDOW (f));
-      if (f->display.x->v_scrollbar != 0 || f->display.x->h_scrollbar != 0)
+      if (FRAME_HAS_VERTICAL_SCROLLBARS (f))
 	XMapSubwindows (x_current_display, FRAME_X_WINDOW (f));
 #else /* ! defined (HAVE_X11) */
       XMapWindow (XDISPLAY FRAME_X_WINDOW (f));
@@ -3535,6 +3921,8 @@
     x_highlight_frame = 0;
 }
 
+/* Manage event queues for X10.  */
+
 #ifndef HAVE_X11
 
 /* Manage event queues.
@@ -3607,6 +3995,8 @@
 }
 #endif /* HAVE_X11 */
 
+/* Setting window manager hints.  */
+
 #ifdef HAVE_X11
 
 x_wm_set_size_hint (f, prompting)
@@ -3626,19 +4016,14 @@
   size_hints.width = PIXEL_WIDTH (f);
   size_hints.width_inc = FONT_WIDTH (f->display.x->font);
   size_hints.height_inc = FONT_HEIGHT (f->display.x->font);
-  size_hints.max_width =
-    (x_screen_width - ((2 * f->display.x->internal_border_width)
-		       + f->display.x->v_scrollbar_width));
-  size_hints.max_height =
-    (x_screen_height - ((2 * f->display.x->internal_border_width)
-			+ f->display.x->h_scrollbar_height));
+  size_hints.max_width  = PIXEL_TO_CHAR_WIDTH  (f, x_screen_width);
+  size_hints.max_height = PIXEL_TO_CHAR_HEIGHT (f, x_screen_height);
+    
   {
     int base_width, base_height;
 
-    base_width = ((2 * f->display.x->internal_border_width)
-		  + f->display.x->v_scrollbar_width);
-    base_height = ((2 * f->display.x->internal_border_width)
-		   + f->display.x->h_scrollbar_height);
+    base_width = CHAR_TO_PIXEL_WIDTH (f, 0);
+    base_height = CHAR_TO_PIXEL_HEIGHT (f, 0);
 
     {
       int min_rows = 0, min_cols = 0;
@@ -3731,6 +4116,8 @@
 }
 
 
+/* Initialization.  */
+
 void
 x_term_init (display_name)
      char *display_name;
@@ -3784,7 +4171,11 @@
 
   /* Figure out which modifier bits mean what.  */
   x_find_modifier_meanings ();
-  
+
+  /* Get the scrollbar cursor.  */
+  x_vertical_scrollbar_cursor =
+    XCreateFontCursor (x_current_display, XC_sb_v_double_arrow);
+
   /* Watch for PropertyNotify events on the root window; we use them
      to figure out when to invalidate our cache of the cut buffers.  */
   x_watch_cut_buffer_cache ();
@@ -3839,8 +4230,12 @@
   read_socket_hook = XTread_socket;
   cursor_to_hook = XTcursor_to;
   reassert_line_highlight_hook = XTreassert_line_highlight;
+  mouse_position_hook = XTmouse_position;
   frame_rehighlight_hook = XTframe_rehighlight;
-  mouse_position_hook = XTmouse_position;
+  set_vertical_scrollbar_hook = XTset_scrollbar;
+  condemn_scrollbars_hook = XTcondemn_scrollbars;
+  redeem_scrollbar_hook = XTredeem_scrollbar;
+  judge_scrollbars_hook = XTjudge_scrollbars;
   
   scroll_region_ok = 1;		/* we'll scroll partial frames */
   char_ins_del_ok = 0;		/* just as fast to write the line */