+ BLOCK_INPUT;
+
+ if (! NILP (last_mouse_scroll_bar))
+ x_scroll_bar_report_motion (f, bar_window, part, x, y, time);
+ else
+ {
+ Window root;
+ int root_x, root_y;
+
+ Window dummy_window;
+ int dummy;
+
+ mouse_moved = 0;
+ last_mouse_scroll_bar = Qnil;
+
+ /* Figure out which root window we're on. */
+ XQueryPointer (x_current_display,
+ DefaultRootWindow (x_current_display),
+
+ /* The root window which contains the pointer. */
+ &root,
+
+ /* Trash which we can't trust if the pointer is on
+ a different screen. */
+ &dummy_window,
+
+ /* The position on that root window. */
+ &root_x, &root_y,
+
+ /* More trash we can't trust. */
+ &dummy, &dummy,
+
+ /* Modifier keys and pointer buttons, about which
+ we don't care. */
+ (unsigned int *) &dummy);
+
+ /* Now we have a position on the root; find the innermost window
+ containing the pointer. */
+ {
+ Window win, child;
+ int win_x, win_y;
+ int parent_x, parent_y;
+
+ win = root;
+ for (;;)
+ {
+ XTranslateCoordinates (x_current_display,
+
+ /* From-window, to-window. */
+ root, win,
+
+ /* From-position, to-position. */
+ root_x, root_y, &win_x, &win_y,
+
+ /* Child of win. */
+ &child);
+
+ if (child == None)
+ break;
+
+ win = child;
+ parent_x = win_x;
+ parent_y = win_y;
+ }
+
+ /* Now we know that:
+ win is the innermost window containing the pointer
+ (XTC says it has no child containing the pointer),
+ win_x and win_y are the pointer's position in it
+ (XTC did this the last time through), and
+ parent_x and parent_y are the pointer's position in win's parent.
+ (They are what win_x and win_y were when win was child.
+ If win is the root window, it has no parent, and
+ parent_{x,y} are invalid, but that's okay, because we'll
+ never use them in that case.) */
+
+ /* Is win one of our frames? */
+ *f = x_window_to_frame (win);
+
+ /* If not, is it one of our scroll bars? */
+ if (! *f)
+ {
+ struct scroll_bar *bar = x_window_to_scroll_bar (win);
+
+ if (bar)
+ {
+ *f = XFRAME (WINDOW_FRAME (XWINDOW (bar->window)));
+ win_x = parent_x;
+ win_y = parent_y;
+ }
+ }
+
+ if (*f)
+ {
+ pixel_to_glyph_coords (*f, win_x, win_y, &win_x, &win_y,
+ &last_mouse_glyph);
+
+ *bar_window = Qnil;
+ *part = 0;
+ XSET (*x, Lisp_Int, win_x);
+ XSET (*y, Lisp_Int, win_y);
+ *time = last_mouse_movement_time;
+ }
+ }
+ }
+
+ UNBLOCK_INPUT;
+}
+
+#else /* ! defined (HAVE_X11) */
+#define XEvent XKeyPressedEvent
+#endif /* ! defined (HAVE_X11) */
+\f
+/* Scroll bar support. */
+
+/* Given an X window ID, find the struct scroll_bar which manages it.
+ This can be called in GC, so we have to make sure to strip off mark
+ bits. */
+static struct scroll_bar *
+x_window_to_scroll_bar (window_id)
+ Window window_id;
+{
+ Lisp_Object tail, frame;
+
+ for (tail = Vframe_list;
+ XGCTYPE (tail) == Lisp_Cons;
+ tail = XCONS (tail)->cdr)
+ {
+ Lisp_Object frame = XCONS (tail)->car;
+ Lisp_Object bar, condemned;
+
+ /* All elements of Vframe_list should be frames. */
+ if (XGCTYPE (frame) != Lisp_Frame)
+ abort ();
+
+ /* Scan this frame's scroll bar list for a scroll bar with the
+ right window ID. */
+ condemned = FRAME_CONDEMNED_SCROLL_BARS (XFRAME (frame));
+ for (bar = FRAME_SCROLL_BARS (XFRAME (frame));
+ /* This trick allows us to search both the ordinary and
+ condemned scroll bar lists with one loop. */
+ ! GC_NILP (bar) || (bar = condemned,
+ condemned = Qnil,
+ ! GC_NILP (bar));
+ bar = XSCROLL_BAR(bar)->next)
+ if (SCROLL_BAR_X_WINDOW (XSCROLL_BAR (bar)) == window_id)
+ return XSCROLL_BAR (bar);
+ }
+
+ return 0;
+}
+
+/* Open a new X window to serve as a scroll bar, and return the
+ scroll bar vector for it. */
+static struct scroll_bar *
+x_scroll_bar_create (window, top, left, width, height)
+ struct window *window;
+ int top, left, width, height;
+{
+ FRAME_PTR frame = XFRAME (WINDOW_FRAME (window));
+ struct scroll_bar *bar =
+ XSCROLL_BAR (Fmake_vector (make_number (SCROLL_BAR_VEC_SIZE), Qnil));
+
+ BLOCK_INPUT;
+
+ {
+ XSetWindowAttributes a;
+ unsigned long mask;
+
+ a.background_pixel = frame->display.x->background_pixel;
+ a.event_mask = (ButtonPressMask | ButtonReleaseMask
+ | ButtonMotionMask | PointerMotionHintMask
+ | ExposureMask);
+ a.cursor = x_vertical_scroll_bar_cursor;
+
+ mask = (CWBackPixel | CWEventMask | CWCursor);
+
+ SET_SCROLL_BAR_X_WINDOW
+ (bar,
+ XCreateWindow (x_current_display, FRAME_X_WINDOW (frame),
+
+ /* Position and size of scroll bar. */
+ left, top, width, height,
+
+ /* Border width, depth, class, and visual. */
+ 0, CopyFromParent, CopyFromParent, CopyFromParent,
+
+ /* Attributes. */
+ mask, &a));
+ }
+
+ XSET (bar->window, Lisp_Window, window);
+ XSET (bar->top, Lisp_Int, top);
+ XSET (bar->left, Lisp_Int, left);
+ XSET (bar->width, Lisp_Int, width);
+ XSET (bar->height, Lisp_Int, height);
+ XSET (bar->start, Lisp_Int, 0);
+ XSET (bar->end, Lisp_Int, 0);
+ bar->dragging = Qnil;
+
+ /* Add bar to its frame's list of scroll bars. */
+ bar->next = FRAME_SCROLL_BARS (frame);
+ bar->prev = Qnil;
+ XSET (FRAME_SCROLL_BARS (frame), Lisp_Vector, bar);
+ if (! NILP (bar->next))
+ XSET (XSCROLL_BAR (bar->next)->prev, Lisp_Vector, bar);
+
+ XMapWindow (x_current_display, SCROLL_BAR_X_WINDOW (bar));
+
+ UNBLOCK_INPUT;
+
+ return bar;
+}
+
+/* Draw BAR's handle in the proper position.
+ If the handle is already drawn from START to END, don't bother
+ redrawing it, unless REBUILD is non-zero; in that case, always
+ redraw it. (REBUILD is handy for drawing the handle after expose
+ events.)
+
+ Normally, we want to constrain the start and end of the handle to
+ fit inside its rectangle, but if the user is dragging the scroll bar
+ handle, we want to let them drag it down all the way, so that the
+ bar's top is as far down as it goes; otherwise, there's no way to
+ move to the very end of the buffer. */
+static void
+x_scroll_bar_set_handle (bar, start, end, rebuild)
+ struct scroll_bar *bar;
+ int start, end;
+ int rebuild;
+{
+ int dragging = ! NILP (bar->dragging);
+ Window w = SCROLL_BAR_X_WINDOW (bar);
+ GC gc = XFRAME (WINDOW_FRAME (XWINDOW (bar->window)))->display.x->normal_gc;
+
+ /* If the display is already accurate, do nothing. */
+ if (! rebuild
+ && start == XINT (bar->start)
+ && end == XINT (bar->end))
+ return;
+
+ BLOCK_INPUT;
+
+ {
+ int inside_width = VERTICAL_SCROLL_BAR_INSIDE_WIDTH (XINT (bar->width));
+ int inside_height = VERTICAL_SCROLL_BAR_INSIDE_HEIGHT (XINT (bar->height));
+ int top_range = VERTICAL_SCROLL_BAR_TOP_RANGE (XINT (bar->height));
+
+ /* Make sure the values are reasonable, and try to preserve
+ the distance between start and end. */
+ {
+ int length = end - start;
+
+ if (start < 0)
+ start = 0;
+ else if (start > top_range)
+ start = top_range;
+ end = start + length;
+
+ if (end < start)
+ end = start;
+ else if (end > top_range && ! dragging)
+ end = top_range;
+ }
+
+ /* Store the adjusted setting in the scroll bar. */
+ XSET (bar->start, Lisp_Int, start);
+ XSET (bar->end, Lisp_Int, end);
+
+ /* Clip the end position, just for display. */
+ if (end > top_range)
+ end = top_range;
+
+ /* Draw bottom positions VERTICAL_SCROLL_BAR_MIN_HANDLE pixels
+ below top positions, to make sure the handle is always at least
+ that many pixels tall. */
+ end += VERTICAL_SCROLL_BAR_MIN_HANDLE;
+
+ /* Draw the empty space above the handle. Note that we can't clear
+ zero-height areas; that means "clear to end of window." */
+ if (0 < start)
+ XClearArea (x_current_display, w,
+
+ /* x, y, width, height, and exposures. */
+ VERTICAL_SCROLL_BAR_LEFT_BORDER,
+ VERTICAL_SCROLL_BAR_TOP_BORDER,
+ inside_width, start,
+ False);
+
+ /* Draw the handle itself. */
+ XFillRectangle (x_current_display, w, gc,
+
+ /* x, y, width, height */
+ VERTICAL_SCROLL_BAR_LEFT_BORDER,
+ VERTICAL_SCROLL_BAR_TOP_BORDER + start,
+ inside_width, end - start);
+
+
+ /* Draw the empty space below the handle. Note that we can't
+ clear zero-height areas; that means "clear to end of window." */
+ if (end < inside_height)
+ XClearArea (x_current_display, w,
+
+ /* x, y, width, height, and exposures. */
+ VERTICAL_SCROLL_BAR_LEFT_BORDER,
+ VERTICAL_SCROLL_BAR_TOP_BORDER + end,
+ inside_width, inside_height - end,
+ False);
+
+ }
+
+ UNBLOCK_INPUT;
+}
+
+/* Move a scroll bar around on the screen, to accomodate changing
+ window configurations. */
+static void
+x_scroll_bar_move (bar, top, left, width, height)
+ struct scroll_bar *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 != XINT (bar->left)) mask |= CWX;
+ if (top != XINT (bar->top)) mask |= CWY;
+ if (width != XINT (bar->width)) mask |= CWWidth;
+ if (height != XINT (bar->height)) mask |= CWHeight;
+
+ if (mask)
+ XConfigureWindow (x_current_display, SCROLL_BAR_X_WINDOW (bar),
+ mask, &wc);
+ }
+
+ XSET (bar->left, Lisp_Int, left);
+ XSET (bar->top, Lisp_Int, top);
+ XSET (bar->width, Lisp_Int, width);
+ XSET (bar->height, Lisp_Int, height);
+
+ UNBLOCK_INPUT;
+}
+
+/* Destroy the X window for BAR, and set its Emacs window's scroll bar
+ to nil. */
+static void
+x_scroll_bar_remove (bar)
+ struct scroll_bar *bar;
+{
+ FRAME_PTR f = XFRAME (WINDOW_FRAME (XWINDOW (bar->window)));
+
+ BLOCK_INPUT;
+
+ /* Destroy the window. */
+ XDestroyWindow (x_current_display, SCROLL_BAR_X_WINDOW (bar));
+
+ /* Disassociate this scroll bar from its window. */
+ XWINDOW (bar->window)->vertical_scroll_bar = Qnil;
+
+ UNBLOCK_INPUT;
+}
+
+/* Set the handle of the vertical scroll bar for WINDOW to indicate
+ that we are displaying PORTION characters out of a total of WHOLE
+ characters, starting at POSITION. If WINDOW has no scroll bar,
+ create one. */
+static void
+XTset_vertical_scroll_bar (window, portion, whole, position)
+ struct window *window;
+ int portion, whole, position;
+{
+ FRAME_PTR f = XFRAME (WINDOW_FRAME (window));
+ int top = XINT (window->top);
+ int left = WINDOW_VERTICAL_SCROLL_BAR_COLUMN (window);
+ int height = WINDOW_VERTICAL_SCROLL_BAR_HEIGHT (window);
+
+ /* Where should this scroll bar be, pixelwise? */
+ int pixel_top = CHAR_TO_PIXEL_ROW (f, top);
+ int pixel_left = CHAR_TO_PIXEL_COL (f, left);
+ int pixel_width = VERTICAL_SCROLL_BAR_PIXEL_WIDTH (f);
+ int pixel_height = VERTICAL_SCROLL_BAR_PIXEL_HEIGHT (f, height);
+
+ struct scroll_bar *bar;
+
+ /* Does the scroll bar exist yet? */
+ if (NILP (window->vertical_scroll_bar))
+ bar = x_scroll_bar_create (window,
+ pixel_top, pixel_left,
+ pixel_width, pixel_height);
+ else
+ {
+ /* It may just need to be moved and resized. */
+ bar = XSCROLL_BAR (window->vertical_scroll_bar);
+ x_scroll_bar_move (bar, pixel_top, pixel_left, pixel_width, pixel_height);
+ }
+
+ /* Set the scroll bar's current state, unless we're currently being
+ dragged. */
+ if (NILP (bar->dragging))
+ {
+ int top_range =
+ VERTICAL_SCROLL_BAR_TOP_RANGE (pixel_height);
+
+ if (whole == 0)
+ x_scroll_bar_set_handle (bar, 0, top_range, 0);
+ else
+ {
+ int start = (position * top_range) / whole;
+ int end = ((position + portion) * top_range) / whole;
+
+ x_scroll_bar_set_handle (bar, start, end, 0);
+ }
+ }
+
+ XSET (window->vertical_scroll_bar, Lisp_Vector, bar);
+}
+
+
+/* The following three hooks are used when we're doing a thorough
+ redisplay of the frame. We don't explicitly know which scroll bars
+ are going to be deleted, because keeping track of when windows go
+ away is a real pain - "Can you say set-window-configuration, boys
+ and girls?" Instead, we just assert at the beginning of redisplay
+ that *all* scroll bars are to be removed, and then save a scroll bar
+ from the fiery pit when we actually redisplay its window. */
+
+/* Arrange for all scroll bars on FRAME to be removed at the next call
+ to `*judge_scroll_bars_hook'. A scroll bar may be spared if
+ `*redeem_scroll_bar_hook' is applied to its window before the judgement. */
+static void
+XTcondemn_scroll_bars (frame)
+ FRAME_PTR frame;
+{
+ /* The condemned list should be empty at this point; if it's not,
+ then the rest of Emacs isn't using the condemn/redeem/judge
+ protocol correctly. */
+ if (! NILP (FRAME_CONDEMNED_SCROLL_BARS (frame)))
+ abort ();
+
+ /* Move them all to the "condemned" list. */
+ FRAME_CONDEMNED_SCROLL_BARS (frame) = FRAME_SCROLL_BARS (frame);
+ FRAME_SCROLL_BARS (frame) = Qnil;
+}
+
+/* Unmark WINDOW's scroll bar for deletion in this judgement cycle.
+ Note that WINDOW isn't necessarily condemned at all. */
+static void
+XTredeem_scroll_bar (window)
+ struct window *window;
+{
+ struct scroll_bar *bar;
+
+ /* We can't redeem this window's scroll bar if it doesn't have one. */
+ if (NILP (window->vertical_scroll_bar))
+ abort ();
+
+ bar = XSCROLL_BAR (window->vertical_scroll_bar);
+
+ /* Unlink it from the condemned list. */
+ {
+ FRAME_PTR f = XFRAME (WINDOW_FRAME (window));
+
+ if (NILP (bar->prev))
+ {
+ /* If the prev pointer is nil, it must be the first in one of
+ the lists. */
+ if (EQ (FRAME_SCROLL_BARS (f), window->vertical_scroll_bar))
+ /* It's not condemned. Everything's fine. */
+ return;
+ else if (EQ (FRAME_CONDEMNED_SCROLL_BARS (f),
+ window->vertical_scroll_bar))
+ FRAME_CONDEMNED_SCROLL_BARS (f) = bar->next;
+ else
+ /* If its prev pointer is nil, it must be at the front of
+ one or the other! */
+ abort ();
+ }
+ else
+ XSCROLL_BAR (bar->prev)->next = bar->next;
+
+ if (! NILP (bar->next))
+ XSCROLL_BAR (bar->next)->prev = bar->prev;
+
+ bar->next = FRAME_SCROLL_BARS (f);
+ bar->prev = Qnil;
+ XSET (FRAME_SCROLL_BARS (f), Lisp_Vector, bar);
+ if (! NILP (bar->next))
+ XSET (XSCROLL_BAR (bar->next)->prev, Lisp_Vector, bar);
+ }
+}
+
+/* Remove all scroll bars on FRAME that haven't been saved since the
+ last call to `*condemn_scroll_bars_hook'. */
+static void
+XTjudge_scroll_bars (f)
+ FRAME_PTR f;
+{
+ Lisp_Object bar, next;
+
+ bar = FRAME_CONDEMNED_SCROLL_BARS (f);
+
+ /* Clear out the condemned list now so we won't try to process any
+ more events on the hapless scroll bars. */
+ FRAME_CONDEMNED_SCROLL_BARS (f) = Qnil;
+
+ for (; ! NILP (bar); bar = next)
+ {
+ struct scroll_bar *b = XSCROLL_BAR (bar);
+
+ x_scroll_bar_remove (b);
+
+ next = b->next;
+ b->next = b->prev = Qnil;
+ }
+
+ /* Now there should be no references to the condemned scroll bars,
+ and they should get garbage-collected. */
+}
+
+
+/* Handle an Expose or GraphicsExpose event on a scroll bar.
+
+ This may be called from a signal handler, so we have to ignore GC
+ mark bits. */
+static void
+x_scroll_bar_expose (bar, event)
+ struct scroll_bar *bar;
+ XEvent *event;
+{
+ Window w = SCROLL_BAR_X_WINDOW (bar);
+ GC gc = XFRAME (WINDOW_FRAME (XWINDOW (bar->window)))->display.x->normal_gc;
+
+ BLOCK_INPUT;
+
+ x_scroll_bar_set_handle (bar, XINT (bar->start), XINT (bar->end), 1);
+
+ /* Draw a one-pixel border just inside the edges of the scroll bar. */
+ XDrawRectangle (x_current_display, w, gc,
+
+ /* x, y, width, height */
+ 0, 0, XINT (bar->width) - 1, XINT (bar->height) - 1);
+
+ /* Draw another line to make the extra-thick border on the right. */
+ XFillRectangle (x_current_display, w, gc,
+
+ /* x, y, width, height */
+ XINT (bar->width) - 2, 1, 1, XINT (bar->height) - 2);
+
+ UNBLOCK_INPUT;
+}
+
+/* Handle a mouse click on the scroll bar BAR. If *EMACS_EVENT's kind
+ is set to something other than no_event, it is enqueued.
+
+ This may be called from a signal handler, so we have to ignore GC
+ mark bits. */
+static void
+x_scroll_bar_handle_click (bar, event, emacs_event)
+ struct scroll_bar *bar;
+ XEvent *event;
+ struct input_event *emacs_event;
+{
+ if (XGCTYPE (bar->window) != Lisp_Window)
+ abort ();
+
+ emacs_event->kind = scroll_bar_click;
+ XSET (emacs_event->code, Lisp_Int, event->xbutton.button - Button1);
+ emacs_event->modifiers =
+ (x_convert_modifiers (event->xbutton.state)
+ | (event->type == ButtonRelease
+ ? up_modifier
+ : down_modifier));
+ emacs_event->frame_or_window = bar->window;
+ emacs_event->timestamp = event->xbutton.time;
+ {
+ int internal_height =
+ VERTICAL_SCROLL_BAR_INSIDE_HEIGHT (XINT (bar->height));
+ int top_range =
+ VERTICAL_SCROLL_BAR_TOP_RANGE (XINT (bar->height));
+ int y = event->xbutton.y - VERTICAL_SCROLL_BAR_TOP_BORDER;
+
+ if (y < 0) y = 0;
+ if (y > top_range) y = top_range;
+
+ if (y < XINT (bar->start))
+ emacs_event->part = scroll_bar_above_handle;
+ else if (y < XINT (bar->end) + VERTICAL_SCROLL_BAR_MIN_HANDLE)
+ emacs_event->part = scroll_bar_handle;
+ else
+ emacs_event->part = scroll_bar_below_handle;
+
+ /* If the user has just clicked on the handle, record where they're
+ holding it. */
+ if (event->type == ButtonPress
+ && emacs_event->part == scroll_bar_handle)
+ XSET (bar->dragging, Lisp_Int, y - XINT (bar->start));
+
+ /* If the user has released the handle, set it to its final position. */
+ if (event->type == ButtonRelease
+ && ! NILP (bar->dragging))
+ {
+ int new_start = y - XINT (bar->dragging);
+ int new_end = new_start + (XINT (bar->end) - XINT (bar->start));
+
+ x_scroll_bar_set_handle (bar, new_start, new_end, 0);
+ bar->dragging = Qnil;
+ }
+
+ /* Clicks on the handle are always reported as occuring at the top of
+ the handle. */
+ if (emacs_event->part == scroll_bar_handle)
+ emacs_event->x = bar->start;
+ else
+ XSET (emacs_event->x, Lisp_Int, y);
+
+ XSET (emacs_event->y, Lisp_Int, top_range);
+ }
+}
+
+/* Handle some mouse motion while someone is dragging the scroll bar.
+
+ This may be called from a signal handler, so we have to ignore GC
+ mark bits. */
+static void
+x_scroll_bar_note_movement (bar, event)
+ struct scroll_bar *bar;
+ XEvent *event;
+{
+ last_mouse_movement_time = event->xmotion.time;
+
+ mouse_moved = 1;
+ XSET (last_mouse_scroll_bar, Lisp_Vector, bar);
+
+ /* If we're dragging the bar, display it. */
+ if (! GC_NILP (bar->dragging))
+ {
+ /* Where should the handle be now? */
+ int new_start = event->xmotion.y - XINT (bar->dragging);
+
+ if (new_start != XINT (bar->start))
+ {
+ int new_end = new_start + (XINT (bar->end) - XINT (bar->start));
+
+ x_scroll_bar_set_handle (bar, new_start, new_end, 0);
+ }
+ }
+
+ /* 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);
+ }
+}
+
+/* Return information to the user about the current position of the mouse
+ on the scroll bar. */
+static void
+x_scroll_bar_report_motion (f, bar_window, part, x, y, time)
+ FRAME_PTR *f;
+ Lisp_Object *bar_window;
+ enum scroll_bar_part *part;
+ Lisp_Object *x, *y;
+ unsigned long *time;
+{
+ struct scroll_bar *bar = XSCROLL_BAR (last_mouse_scroll_bar);
+ int win_x, win_y;
+
+ BLOCK_INPUT;
+
+ /* Get the mouse's position relative to the scroll bar window, and
+ report that. */
+ {
+ Window dummy_window;
+ int dummy_coord;
+ unsigned int dummy_mask;
+
+ if (! XQueryPointer (x_current_display,
+ SCROLL_BAR_X_WINDOW (bar),
+
+ /* Root, child, root x and root y. */
+ &dummy_window, &dummy_window,
+ &dummy_coord, &dummy_coord,
+
+ /* Position relative to scroll bar. */
+ &win_x, &win_y,
+
+ /* Mouse buttons and modifier keys. */
+ &dummy_mask))
+ {
+ *f = 0;
+ goto done;
+ }
+ }
+
+ {
+ int inside_height = VERTICAL_SCROLL_BAR_INSIDE_HEIGHT (XINT (bar->height));
+ int top_range = VERTICAL_SCROLL_BAR_TOP_RANGE (XINT (bar->height));
+
+ win_y -= VERTICAL_SCROLL_BAR_TOP_BORDER;
+
+ if (! NILP (bar->dragging))
+ win_y -= XINT (bar->dragging);
+
+ if (win_y < 0)
+ win_y = 0;
+ if (win_y > top_range)
+ win_y = top_range;
+
+ *f = XFRAME (WINDOW_FRAME (XWINDOW (bar->window)));
+ *bar_window = bar->window;
+
+ if (! NILP (bar->dragging))
+ *part = scroll_bar_handle;
+ else if (win_y < XINT (bar->start))
+ *part = scroll_bar_above_handle;
+ else if (win_y < XINT (bar->end) + VERTICAL_SCROLL_BAR_MIN_HANDLE)
+ *part = scroll_bar_handle;
+ else
+ *part = scroll_bar_below_handle;
+
+ XSET (*x, Lisp_Int, win_y);
+ XSET (*y, Lisp_Int, top_range);
+ *time = last_mouse_movement_time;
+ }
+
+ mouse_moved = 0;
+ last_mouse_scroll_bar = Qnil;
+
+ done:
+ UNBLOCK_INPUT;
+}
+
+
+/* The screen has been cleared so we may have changed foreground or
+ background colors, and the scroll bars may need to be redrawn.
+ Clear out the scroll bars, and ask for expose events, so we can
+ redraw them. */
+
+x_scroll_bar_clear (f)
+ FRAME_PTR f;
+{
+ Lisp_Object bar;
+
+ for (bar = FRAME_SCROLL_BARS (f);
+ XTYPE (bar) == Lisp_Vector;
+ bar = XSCROLL_BAR (bar)->next)
+ XClearArea (x_current_display, SCROLL_BAR_X_WINDOW (XSCROLL_BAR (bar)),
+ 0, 0, 0, 0, True);
+}
+
+
+\f
+/* 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
+ sometimes don't work. */
+static Time enter_timestamp;
+
+/* This holds the state XLookupString needs to implement dead keys
+ and other tricks known as "compose processing". _X Window System_
+ says that a portable program can't use this, but Stephen Gildea assures
+ me that letting the compiler initialize it to zeros will work okay.
+
+ This must be defined outside of XTread_socket, for the same reasons
+ given for enter_timestamp, above. */
+static XComposeStatus compose_status;
+
+/* Communication with window managers. */
+Atom Xatom_wm_protocols;
+
+/* Kinds of protocol things we may receive. */
+Atom Xatom_wm_take_focus;
+Atom Xatom_wm_save_yourself;
+Atom Xatom_wm_delete_window;
+
+/* Other WM communication */
+Atom Xatom_wm_configure_denied; /* When our config request is denied */
+Atom Xatom_wm_window_moved; /* When the WM moves us. */
+
+/* Read events coming from the X server.
+ This routine is called by the SIGIO handler.
+ We return as soon as there are no more events to be read.
+
+ Events representing keys are stored in buffer BUFP,
+ which can hold up to NUMCHARS characters.
+ We return the number of characters stored into the buffer,
+ thus pretending to be `read'.
+
+ WAITP is nonzero if we should block until input arrives.
+ EXPECTED is nonzero if the caller knows input is available. */
+
+int
+XTread_socket (sd, bufp, numchars, waitp, expected)
+ register int sd;
+ register struct input_event *bufp;
+ register int numchars;
+ int waitp;
+ int expected;
+{
+ int count = 0;
+ int nbytes = 0;
+ int mask;
+ int items_pending; /* How many items are in the X queue. */
+ XEvent event;
+ struct frame *f;
+ int event_found;
+ int prefix;