# HG changeset patch # User Jim Blandy # Date 727025654 0 # Node ID 5e245540d06f2bfcf96a0228ebddf6769197acdb # Parent 5179baee010b5be086e7694a61c058359f02e7c1 Make scrollbar structures into lisp objects, so that they can be GC'd; this allows windows and scrollbars can refer to each other without worrying about dangling pointers. * xterm.h (struct x_display): vertical_scrollbars and judge_timestamp members deleted. (struct scrollbar): Redesigned to be a template for a Lisp_Vector. (SCROLLBAR_VEC_SIZE, XSCROLLBAR, SCROLLBAR_PACK, SCROLLBAR_UNPACK, SCROLLBAR_X_WINDOW, SET_SCROLLBAR_X_WINDOW, VERTICAL_SCROLLBAR_INSIDE_WIDTH, VERTICAL_SCROLLBAR_TOP_RANGE, VERTICAL_SCROLLBAR_INSIDE_HEIGHT, VERTICAL_SCROLLBAR_MIN_HANDLE): New macros, to help deal with the lispy structures, and deal with the graphics. * frame.h (WINDOW_VERTICAL_SCROLLBAR): Macro deleted. (struct frame): New fields `scrollbars' and `condemned_scrollbars', for use by the scrollbar implementation. [MULTI_FRAME and not MULTI_FRAME] (FRAME_SCROLLBARS, FRAME_CONDEMNED_SCROLLBARS): Accessors for the new field. * window.h (struct window): Doc fix for vertical_scrollbar field. * frame.c (make_frame): Initialize the `scrollbars' and `condemned_scrollbars' fields of the new frame. * alloc.c (mark_object): Mark the `scrollbars' and `condemned_scrollbars' slots of frames. * xterm.c (x_window_to_scrollbar): Scrollbars are chained on frames' scrollbar field, not their x.display->vertical_scrollbars field. (x_scrollbar_create, x_scrollbar_set_handle, x_scrollbar_move, x_scrollbar_remove, XTset_vertical_scrollbar, XTcondemn_scrollbars, XTredeem_scrollbar, XTjudge_scrollbars, x_scrollbar_expose, x_scrollbar_handle_click, x_scrollbar_handle_motion): Substantially rewritten to correct typos and brainos, and to accomodate the lispy structures. * xterm.c (x_scrollbar_background_expose): Function deleted; we don't want anything in the background there after all. (XTread_socket): Don't call x_scrollbar_background_expose. We don't care. * xterm.h (CHAR_TO_PIXEL_WIDTH, CHAR_TO_PIXEL_HEIGHT, PIXEL_TO_CHAR_WIDTH, PIXEL_TO_CHAR_HEIGHT): Rewritten, using: (CHAR_TO_PIXEL_ROW, CHAR_TO_PIXEL_COL, PIXEL_TO_CHAR_ROW, PIXEL_TO_CHAR_COL): New macros. * xfns.c [not HAVE_X11] (Fx_create_frame): Use the PIXEL_TO_CHAR_{HEIGHT,WIDTH} macros to figure the frame's character size, and the CHAR_TO_PIXEL* macros for vice versa. * xterm.c (XTwrite_glyphs, XTclear_end_of_line, stufflines, scraplines, dumprectangle, pixel_to_glyph_coords, x_draw_box, clear_cursor, x_display_bar_cursor, x_draw_single_glyph, x_set_mouse_position): Use the CHAR_TO_PIXEL_* macros. * xterm.c (x_wm_set_size_hint): The max_width and max_height members of the size_hints are expressed in pixels, not columns. * xterm.c (x_set_window_size): Remove ibw var; it's not used. Set FRAME_WIDTH (f) to cols instead of rows. Duh. * xterm.c (pixel_to_glyph_coords): Properly set *bounds to the character cell bounding the position, even when the position is off the frame. * termhooks.h (mouse_position_hook): Doc fix. (set_vertical_scrollbar_hook): This doesn't return anything any more, and doesn't take a struct scrollbar * argument any more. (condemn_scrollbars_hook, redeem_scrollbar_hook, judge_scrollbars_hook): Doc fixes. * term.c (mouse_position_hook): Doc fix. (set_vertical_scrollbar_hook): This doesn't return anything any more. Doc fixes. * keyboard.c (kbd_buffer_get_event): Receive the scrollbar's window from *mouse_position_hook and pass it to make_lispy_movement, instead of working with a pointer to a struct scrollbar. (make_lispy_event): We don't need a window_from_scrollbar function anymore; we are given the window directly in *EVENT. Unify the code which generates text-area mouse clicks and scrollbar clicks; use the same code to distinguish clicks from drags on the scrollbar as in the text area. Distinguish clicks from drags by storing a copy of the lispy position list returned as part of the event. (button_down_location): Make this a lisp vector, rather than an array of random structures. (struct mouse_position): Remove this; it's been replaced by a lisp list. (make_lispy_movement): Accept the scrollbar's window as a parameter, rather than the scrollbar itself. If FRAME is zero, assume that the other arguments are garbage. (syms_of_keyboard): No need to staticpro each window of button_down_location now; just initialize and staticpro it. * window.c (window_from_scrollbar): Function deleted; no longer needed. * xdisp.c (redisplay_window): Just pass the window to set_vertical_scrollbar hook; don't pass the scrollbar object too. * xterm.c (XTmouse_position): Don't return a pointer to the scrollbar for scrollbar motion; instead, return the scrollbar's window. * xterm.c (XTmouse_position): Entirely rewritten, using XTranslateCoordinates. Call x_scrollbar_report_motion to handle scrollbar movement events. (x_scrollbar_report_motion): New function, to help out XTmouse_position. * termhooks.h (struct input_event): Replace the frame member with a Lisp_Object member by the name of frame_or_window. Doc fixes. Remove the scrollbar member; instead, use frame_or_window to hold the window whose scrollbar was clicked. * keyboard.c (kbd_buffer_store_event, kbd_buffer_get_event, make_lispy_event): Adjust references to frame member of struct input_event to use frame_or_window now. * xterm.c (construct_mouse_click, XTread_socket): Same. * xterm.c (last_mouse_bar, last_mouse_bar_frame, last_mouse_part, last_mouse_scroll_range_start, last_mouse_scroll_range_end): Replaced with... (last_mouse_scrollbar): New variable. (note_mouse_movement): Clear last_mouse_scrollbar when we have receieved a new motion. (syms_of_xterm): Staticpro last_mouse_scrollbar. * xterm.c (note_mouse_position): Renamed to note_mouse_movement, because that's what it really does. (x_scrollbar_handle_motion): Renamed to x_scrollbar_note_movement, for consistency. (XTread_socket): Adjusted. * xterm.c (XTset_scrollbar): Renamed to XTset_vertical_scrollbar. (x_term_init): Adjusted. * emacs.c (shut_down_emacs): New function. (fatal_error_signal, Fkill_emacs): Call it, instead of writing it out. * xterm.c (x_connection_closed): Call shut_down_emacs instead of Fkill_emacs; the latter will try to perform operations on the X server and die a horrible death. * lisp.h (shut_down_emacs): Add extern declaration for it. * xterm.c (x_error_quitter): Move the abort call to after we print the error message. No harm in that. diff -r 5179baee010b -r 5e245540d06f src/xterm.c --- a/src/xterm.c Thu Jan 14 15:19:55 1993 +0000 +++ b/src/xterm.c Thu Jan 14 15:34:14 1993 +0000 @@ -1,5 +1,5 @@ /* X Communication module for terminals which understand the X protocol. - Copyright (C) 1989, 1992 Free Software Foundation, Inc. + Copyright (C) 1989, 1992, 1993 Free Software Foundation, Inc. This file is part of GNU Emacs. @@ -571,11 +571,9 @@ } dumpglyphs (f, - (curs_x * FONT_WIDTH (f->display.x->font) - + f->display.x->internal_border_width), - (curs_y * FONT_HEIGHT (f->display.x->font) - + f->display.x->internal_border_width), - start, len, highlight, f->display.x->font); + CHAR_TO_PIXEL_COL (f, curs_x), + CHAR_TO_PIXEL_ROW (f, curs_y), + start, len, highlight, f->display.x->font); /* If we drew on top of the cursor, note that it is turned off. */ if (curs_y == f->phys_cursor_y @@ -628,17 +626,15 @@ #ifdef HAVE_X11 XClearArea (x_current_display, FRAME_X_WINDOW (f), - curs_x * FONT_WIDTH (f->display.x->font) - + f->display.x->internal_border_width, - curs_y * FONT_HEIGHT (f->display.x->font) - + f->display.x->internal_border_width, + CHAR_TO_PIXEL_COL (f, curs_x), + CHAR_TO_PIXEL_ROW (f, curs_y), FONT_WIDTH (f->display.x->font) * (first_unused - curs_x), FONT_HEIGHT (f->display.x->font), False); #else /* ! defined (HAVE_X11) */ XPixSet (FRAME_X_WINDOW (f), - curs_x * FONT_WIDTH (f->display.x->font) + f->display.x->internal_border_width, - curs_y * FONT_HEIGHT (f->display.x->font) + f->display.x->internal_border_width, + CHAR_TO_PIXEL_COL (f, curs_x), + CHAR_TO_PIXEL_ROW (f, curs_y), FONT_WIDTH (f->display.x->font) * (first_unused - curs_x), FONT_HEIGHT (f->display.x->font), f->display.x->background_pixel); @@ -843,14 +839,14 @@ #ifdef HAVE_X11 XCopyArea (x_current_display, FRAME_X_WINDOW (f), FRAME_X_WINDOW (f), f->display.x->normal_gc, - intborder, topregion * FONT_HEIGHT (f->display.x->font) + intborder, + intborder, CHAR_TO_PIXEL_ROW (f, topregion), f->width * FONT_WIDTH (f->display.x->font), length * FONT_HEIGHT (f->display.x->font), intborder, - newtop * FONT_HEIGHT (f->display.x->font) + intborder); + CHAR_TO_PIXEL_ROW (f, newtop)); #else /* ! defined (HAVE_X11) */ XMoveArea (FRAME_X_WINDOW (f), - intborder, topregion * FONT_HEIGHT (f->display.x->font) + intborder, - intborder, newtop * FONT_HEIGHT (f->display.x->font) + intborder, + intborder, CHAR_TO_PIXEL_ROW (f, topregion), + intborder, CHAR_TO_PIXEL_ROW (f, newtop), f->width * FONT_WIDTH (f->display.x->font), length * FONT_HEIGHT (f->display.x->font)); /* Now we must process any ExposeRegion events that occur @@ -867,13 +863,13 @@ { #ifdef HAVE_X11 XClearArea (x_current_display, FRAME_X_WINDOW (f), intborder, - topregion * FONT_HEIGHT (f->display.x->font) + intborder, + CHAR_TO_PIXEL_ROW (f, topregion), f->width * FONT_WIDTH (f->display.x->font), n * FONT_HEIGHT (f->display.x->font), False); #else /* ! defined (HAVE_X11) */ XPixSet (FRAME_X_WINDOW (f), intborder, - topregion * FONT_HEIGHT (f->display.x->font) + intborder, + CHAR_TO_PIXEL_ROW (f, topregion), f->width * FONT_WIDTH (f->display.x->font), n * FONT_HEIGHT (f->display.x->font), f->display.x->background_pixel); @@ -905,12 +901,12 @@ { #ifdef HAVE_X11 XClearArea (x_current_display, FRAME_X_WINDOW (f), intborder, - curs_y * FONT_HEIGHT (f->display.x->font) + intborder, + CHAR_TO_PIXEL_ROW (f, curs_y), f->width * FONT_WIDTH (f->display.x->font), (flexlines - curs_y) * FONT_HEIGHT (f->display.x->font), False); #else /* ! defined (HAVE_X11) */ XPixSet (FRAME_X_WINDOW (f), - intborder, curs_y * FONT_HEIGHT (f->display.x->font) + intborder, + intborder, CHAR_TO_PIXEL_ROW (f, curs_y), f->width * FONT_WIDTH (f->display.x->font), (flexlines - curs_y) * FONT_HEIGHT (f->display.x->font), f->display.x->background_pixel); @@ -923,20 +919,20 @@ XCopyArea (x_current_display, FRAME_X_WINDOW (f), FRAME_X_WINDOW (f), f->display.x->normal_gc, intborder, - (curs_y + n) * FONT_HEIGHT (f->display.x->font) + intborder, + CHAR_TO_PIXEL_ROW (f, curs_y + n), f->width * FONT_WIDTH (f->display.x->font), (flexlines - (curs_y + n)) * FONT_HEIGHT (f->display.x->font), - intborder, curs_y * FONT_HEIGHT (f->display.x->font) + intborder); + intborder, CHAR_TO_PIXEL_ROW (f, curs_y)); XClearArea (x_current_display, FRAME_X_WINDOW (f), intborder, - (flexlines - n) * FONT_HEIGHT (f->display.x->font) + intborder, + CHAR_TO_PIXEL_ROW (f, flexlines - n), f->width * FONT_WIDTH (f->display.x->font), n * FONT_HEIGHT (f->display.x->font), False); #else /* ! defined (HAVE_X11) */ XMoveArea (FRAME_X_WINDOW (f), intborder, - (curs_y + n) * FONT_HEIGHT (f->display.x->font) + intborder, - intborder, curs_y * FONT_HEIGHT (f->display.x->font) + intborder, + CHAR_TO_PIXEL_ROW (f, curs_y + n), + intborder, CHAR_TO_PIXEL_ROW (f, curs_y), f->width * FONT_WIDTH (f->display.x->font), (flexlines - (curs_y + n)) * FONT_HEIGHT (f->display.x->font)); /* Now we must process any ExposeRegion events that occur @@ -945,7 +941,7 @@ may want to copy this area to another area. */ x_read_exposes (); XPixSet (FRAME_X_WINDOW (f), intborder, - (flexlines - n) * FONT_HEIGHT (f->display.x->font) + intborder, + CHAR_TO_PIXEL_ROW (f, flexlines - n), f->width * FONT_WIDTH (f->display.x->font), n * FONT_HEIGHT (f->display.x->font), f->display.x->background_pixel); #endif /* ! defined (HAVE_X11) */ @@ -996,30 +992,31 @@ if (FRAME_GARBAGED_P (f)) return; - top -= f->display.x->internal_border_width; - left -= f->display.x->internal_border_width; - /* Express rectangle as four edges, instead of position-and-size. */ bottom = top + rows; right = left + cols; #ifndef HAVE_X11 /* Window manger does this for X11. */ - /* If the rectangle includes any of the internal border area, - redisplay the border emphasis. */ - if (top < 0 || left < 0 - || bottom > f->height * FONT_HEIGHT (f->display.x->font) - || right > f->width * FONT_WIDTH (f->display.x->font)) - dumpborder (f, 0); + { + int intborder = f->display.x->internal_border_width; + + /* If the rectangle includes any of the internal border area, + redisplay the border emphasis. */ + if (top < intborder || left < intborder + || bottom > intborder + f->height * FONT_HEIGHT (f->display.x->font) + || right > intborder + f->width * FONT_WIDTH (f->display.x->font)) + dumpborder (f, 0); + } #endif /* 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. */ - top /= FONT_HEIGHT (f->display.x->font); - left /= FONT_WIDTH (f->display.x->font); + top = PIXEL_TO_CHAR_ROW (f, top); + left = PIXEL_TO_CHAR_COL (f, left); bottom += (FONT_HEIGHT (f->display.x->font) - 1); right += (FONT_WIDTH (f->display.x->font) - 1); - bottom /= FONT_HEIGHT (f->display.x->font); - right /= FONT_WIDTH (f->display.x->font); + bottom = PIXEL_TO_CHAR_ROW (f, bottom); + right = PIXEL_TO_CHAR_COL (f, right); /* Clip the rectangle to what can be visible. */ if (left < 0) @@ -1058,12 +1055,10 @@ continue; dumpglyphs (f, - (left * FONT_WIDTH (f->display.x->font) - + f->display.x->internal_border_width), - (y * FONT_HEIGHT (f->display.x->font) - + f->display.x->internal_border_width), - line, min (cols, active_frame->used[y] - left), - active_frame->highlight[y], f->display.x->font); + CHAR_TO_PIXEL_COL (f, left), + CHAR_TO_PIXEL_ROW (f, y), + line, min (cols, active_frame->used[y] - left), + active_frame->highlight[y], f->display.x->font); } /* Turn the cursor on if we turned it off. */ @@ -1313,36 +1308,25 @@ register int *x, *y; XRectangle *bounds; { - int ibw = f->display.x->internal_border_width; - int width, height; - FONT_TYPE *font = f->display.x->font; - - width = FONT_WIDTH (font); - height = FONT_HEIGHT (font); - - /* What line is it on? */ - if (pix_y < ibw) - *y = 0; - else if (pix_y > f->display.x->pixel_height - ibw) - *y = FRAME_HEIGHT (f) - 1; - else - *y = (pix_y - ibw) / height; - - /* And what column? */ - if (pix_x < ibw) - *x = 0; - else if (pix_x > f->display.x->pixel_width - ibw) - *x = FRAME_WIDTH (f) - 1; - else - *x = (pix_x - ibw) / width; + pix_x = PIXEL_TO_CHAR_COL (f, pix_x); + pix_y = PIXEL_TO_CHAR_ROW (f, pix_y); if (bounds) { - bounds->width = width; - bounds->height = height; - bounds->x = ibw + (*x * width); - bounds->y = ibw + (*y * height); + bounds->width = FONT_WIDTH (f->display.x->font); + bounds->height = FONT_HEIGHT (f->display.x->font); + bounds->x = CHAR_TO_PIXEL_COL (f, pix_x); + bounds->y = CHAR_TO_PIXEL_ROW (f, pix_y); } + + if (pix_x < 0) pix_x = 0; + else if (pix_x > f->width) pix_x = f->width; + + if (pix_y < 0) pix_y = 0; + else if (pix_y > f->height) pix_y = f->height; + + *x = pix_x; + *y = pix_y; } /* Any buttons grabbed. */ @@ -1492,7 +1476,7 @@ pixel_to_glyph_coords (f, event->x, event->y, &column, &row, NULL); XFASTINT (result->x) = column; XFASTINT (result->y) = row; - result->frame = f; + XSET (result->frame_or_window, Lisp_Frame, f); } } @@ -1520,14 +1504,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; +/* The scrollbar in which the last X motion event occurred. + + If the last X motion event occured in a scrollbar, we set this + so XTmouse_position can know whether to report a scrollbar motion or + an ordinary motion. + + If the last X motion event didn't occur in a scrollbar, we set this + to Qnil, to tell XTmouse_position to return an ordinary motion event. */ +static Lisp_Object last_mouse_scrollbar; /* This is a hack. We would really prefer that XTmouse_position would return the time associated with the position it returns, but there @@ -1545,7 +1530,7 @@ 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_position (frame, event) +note_mouse_movement (frame, event) FRAME_PTR frame; XMotionEvent *event; @@ -1557,7 +1542,10 @@ || event->x >= last_mouse_glyph.x + last_mouse_glyph.width || event->y < last_mouse_glyph.y || event->y >= last_mouse_glyph.y + last_mouse_glyph.height) - mouse_moved = 1; + { + mouse_moved = 1; + last_mouse_scrollbar = Qnil; + } else { /* It's on the same glyph. Call XQueryPointer so we'll get an @@ -1572,98 +1560,142 @@ } } +static struct scrollbar *x_window_to_scrollbar (); +static void x_scrollbar_report_motion (); + /* Return the current position of the mouse. + If the mouse movement started in a scrollbar, set *f, *bar_window, + and *part to the frame, window, and scrollbar part that the mouse + is over. Set *x and *y to the portion and whole of the mouse's + position on the scrollbar. + + If the mouse movement started elsewhere, set *f to the frame the + mouse is on, *bar_window to nil, and *x and *y to the character cell + the mouse is over. + + Set *time to the server timestamp for the time at which the mouse + was at this position. + This clears the mouse_moved flag, so we can wait for the next mouse - position. This also calls XQueryPointer, which will cause the - server to give us another MotionNotify when the mouse moves again. - */ + movement. This also calls XQueryPointer, which will cause the + server to give us another MotionNotify when the mouse moves + again. */ static void -XTmouse_position (f, bar, part, x, y, time) +XTmouse_position (f, bar_window, part, x, y, time) FRAME_PTR *f; - struct scrollbar **bar; + Lisp_Object *bar_window; enum scrollbar_part *part; Lisp_Object *x, *y; unsigned long *time; { - int ix, iy, dummy; - Display *d = x_current_display; - Window guess, root, child; - BLOCK_INPUT; - /* I would like to have an X function that just told me the - innermost window containing the mouse. - - /* There doesn't seem to be any way to just get the innermost window - containing the pointer, no matter what X frame it's on; you have - to guess a window, and then X will tell you which one of that - window's children it's in. If the pointer isn't in any of that - window's children, it gives you a root window that contains it. - - So we start with the selected frame's window and chase down - branches under the guidance of XQueryPointer until we hit a leaf - (all of the Emacs windows we care about are leaf windows). If at - any time XQueryPointer returns false, that means that the current - window does not contain the pointer any more (perhaps it moved), - so we start with the root window XQueryPointer has given us and - start again. */ - - guess = FRAME_X_WINDOW (selected_frame); - for (;;) - if (XQueryPointer (d, guess, &root, &child, - &dummy, &dummy, &ix, &iy, (unsigned int *) &dummy)) - { - if (child == None) - /* Guess is a leaf window, and it contains the pointer. */ - break; - else - guess = child; - } - else - /* When XQueryPointer returns False, the pointer isn't in guess - anymore, but root is the root window of the frame we should - try instead. */ - guess = root; - - 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)); - } + if (! NILP (last_mouse_scrollbar)) + x_scrollbar_report_motion (f, bar_window, part, x, y, time); else { - *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); - } + Window root; + int root_x, root_y; + + Window dummy_window; + int dummy; + + mouse_moved = 0; + last_mouse_scrollbar = 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 scrollbars? */ + if (! *f) + { + struct scrollbar *bar = x_window_to_scrollbar (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; + } + } } - 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 - return the time of the last MotionNotify event we received. Note - that the use of motion hints means that this isn't guaranteed to - be accurate at all. */ - *time = last_mouse_movement_time; - UNBLOCK_INPUT; } @@ -1673,19 +1705,17 @@ /* Scrollbar support. */ -/* Map an X window that implements a scroll bar to the struct - scrollbar representing it. */ +/* Given an X window ID, find the struct scrollbar which manages 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; + Lisp_Object bar; /* All elements of Vframe_list should be frames. */ if (XTYPE (frame) != Lisp_Frame) @@ -1693,32 +1723,26 @@ /* 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; + for (bar = FRAME_SCROLLBARS (XFRAME (frame)); + ! NILP (bar); + bar = XSCROLLBAR(bar)->next) + if (SCROLLBAR_X_WINDOW (XSCROLLBAR (bar)) == window_id) + return XSCROLLBAR (bar); } return 0; } - -/* Open a new X window to serve as a scrollbar. */ +/* Open a new X window to serve as a scrollbar, and return the + scrollbar vector for it. */ static struct scrollbar * -x_scrollbar_create (frame, top, left, width, height) - FRAME_PTR frame; +x_scrollbar_create (window, top, left, width, height) + struct window *window; 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. */ + FRAME_PTR frame = XFRAME (WINDOW_FRAME (window)); struct scrollbar *bar = - (struct scrollbar *) malloc (sizeof (struct scrollbar)); - - if (! bar) - return 0; + XSCROLLBAR (Fmake_vector (make_number (SCROLLBAR_VEC_SIZE), Qnil)); BLOCK_INPUT; @@ -1726,144 +1750,153 @@ 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.background_pixel = frame->display.x->background_pixel; + a.event_mask = (ButtonPressMask | ButtonReleaseMask + | ButtonMotionMask + | ExposureMask); 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); + mask = (CWBackPixel | CWEventMask | CWCursor); + + SET_SCROLLBAR_X_WINDOW + (bar, + XCreateWindow (x_current_display, FRAME_X_WINDOW (frame), + + /* Position and size of scrollbar. */ + left, top, width, height, + + /* Border width, depth, class, and visual. */ + 0, 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; + 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 = d->vertical_scrollbars; - d->vertical_scrollbars = bar; - - XMapWindow (x_current_display, bar->window); + bar->next = FRAME_SCROLLBARS (frame); + bar->prev = Qnil; + XSET (FRAME_SCROLLBARS (frame), Lisp_Vector, bar); + if (! NILP (bar->next)) + XSET (XSCROLLBAR (bar->next)->prev, Lisp_Vector, bar); + + XMapWindow (x_current_display, SCROLLBAR_X_WINDOW (bar)); UNBLOCK_INPUT; + + return bar; } -/* Draw BAR's handle in the proper position. */ +/* 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 scrollbar + 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_scrollbar_set_handle (bar, start, end) +x_scrollbar_set_handle (bar, start, end, rebuild) struct scrollbar *bar; int start, end; + int rebuild; { + int dragging = ! NILP (bar->dragging); + Window w = SCROLLBAR_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 = (bar->width - - VERTICAL_SCROLLBAR_LEFT_BORDER - - VERTICAL_SCROLLBAR_RIGHT_BORDER); - int inside_height = (bar->height - - VERTICAL_SCROLLBAR_TOP_BORDER - - VERTICAL_SCROLLBAR_BOTTOM_BORDER); + int inside_width = VERTICAL_SCROLLBAR_INSIDE_WIDTH (XINT (bar->width)); + int inside_height = VERTICAL_SCROLLBAR_INSIDE_HEIGHT (XINT (bar->height)); + int top_range = VERTICAL_SCROLLBAR_TOP_RANGE (XINT (bar->height)); /* 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); + { + 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 scrollbar. */ + 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_SCROLLBAR_MIN_HANDLE pixels + below top positions, to make sure the handle is always at least + that many pixels tall. */ + end += VERTICAL_SCROLLBAR_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_SCROLLBAR_LEFT_BORDER, + VERTICAL_SCROLLBAR_TOP_BORDER, + inside_width, start, + False); /* Draw the handle itself. */ - XFillRectangle (x_current_display, bar->window, - bar->frame->display.x->normal_gc, + XFillRectangle (x_current_display, w, 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; + VERTICAL_SCROLLBAR_LEFT_BORDER, + VERTICAL_SCROLLBAR_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_SCROLLBAR_LEFT_BORDER, + VERTICAL_SCROLLBAR_TOP_BORDER + end, + inside_width, inside_height - end, + False); + } 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; -} - +/* Move a scrollbar around on the screen, to accomodate changing + window configurations. */ static void x_scrollbar_move (bar, top, left, width, height) struct scrollbar *bar; @@ -1880,107 +1913,196 @@ 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); + 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, SCROLLBAR_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; } -/* 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) +/* Destroy the X window for BAR, and set its Emacs window's scrollbar + to nil. */ +static void +x_scrollbar_remove (bar) struct scrollbar *bar; +{ + FRAME_PTR f = XFRAME (WINDOW_FRAME (XWINDOW (bar->window))); + + BLOCK_INPUT; + + /* Destroy the window. */ + XDestroyWindow (x_current_display, SCROLLBAR_X_WINDOW (bar)); + + /* Disassociate this scrollbar from its window. */ + XWINDOW (bar->window)->vertical_scrollbar = 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 scrollbar, + create one. */ +static void +XTset_vertical_scrollbar (window, portion, whole, position) 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_top = CHAR_TO_PIXEL_ROW (f, top); + int pixel_left = CHAR_TO_PIXEL_COL (f, left); int pixel_width = VERTICAL_SCROLLBAR_PIXEL_WIDTH (f); int pixel_height = VERTICAL_SCROLLBAR_PIXEL_HEIGHT (f, height); + struct scrollbar *bar; + /* Does the scrollbar exist yet? */ - if (! bar) - bar = x_scrollbar_create (f, + if (NILP (window->vertical_scrollbar)) + bar = x_scrollbar_create (window, 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); + { + /* It may just need to be moved and resized. */ + bar = XSCROLLBAR (window->vertical_scrollbar); + 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) + if (NILP (bar->dragging)) { - 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); + int top_range = + VERTICAL_SCROLLBAR_TOP_RANGE (pixel_height); + + if (whole == 0) + x_scrollbar_set_handle (bar, 0, top_range, 0); + else + { + int start = (position * top_range) / whole; + int end = ((position + portion) * top_range) / whole; + + x_scrollbar_set_handle (bar, start, end, 0); + } } - return bar; + XSET (window->vertical_scrollbar, 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 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. */ + 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* scrollbars are to be removed, and then save a scrollbar + from the fiery pit when we actually redisplay its 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. */ + `*redeem_scrollbar_hook' is applied to its window 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++; + /* 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_SCROLLBARS (frame))) + abort (); + + /* Move them all to the "condemned" list. */ + FRAME_CONDEMNED_SCROLLBARS (frame) = FRAME_SCROLLBARS (frame); + FRAME_SCROLLBARS (frame) = Qnil; } -/* Unmark BAR for deletion in this judgement cycle. */ +/* Unmark WINDOW's scrollbar for deletion in this judgement cycle. + Note that WINDOW isn't necessarily condemned at all. */ static void -XTredeem_scrollbar (bar) - struct scrollbar *bar; +XTredeem_scrollbar (window) + struct window *window; { - bar->judge_timestamp = bar->frame->display.x->judge_timestamp; + struct scrollbar *bar; + + /* We can't redeem this window's scrollbar if it doesn't have one. */ + if (NILP (window->vertical_scrollbar)) + abort (); + + bar = XSCROLLBAR (window->vertical_scrollbar); + + /* 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_SCROLLBARS (f), window->vertical_scrollbar)) + /* It's not condemned. Everything's fine. */ + return; + else if (EQ (FRAME_CONDEMNED_SCROLLBARS (f), + window->vertical_scrollbar)) + FRAME_CONDEMNED_SCROLLBARS (f) = bar->next; + else + /* If its prev pointer is nil, it must be at the front of + one or the other! */ + abort (); + } + else + XSCROLLBAR (bar->prev)->next = bar->next; + + if (! NILP (bar->next)) + XSCROLLBAR (bar->next)->prev = bar->prev; + + bar->next = FRAME_SCROLLBARS (f); + bar->prev = Qnil; + XSET (FRAME_SCROLLBARS (f), Lisp_Vector, bar); + if (! NILP (bar->next)) + XSET (XSCROLLBAR (bar->next)->prev, Lisp_Vector, bar); + } } /* 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; +XTjudge_scrollbars (f) + FRAME_PTR f; { - int judge_timestamp = frame->display.x->judge_timestamp; - struct scrollbar *bar, *next; - - for (bar = frame->display.x->vertical_scrollbars; bar; bar = next) + Lisp_Object bar, next; + + for (bar = FRAME_CONDEMNED_SCROLLBARS (f); + ! NILP (bar); + bar = next) { - next = bar->next; - if (bar->judge_timestamp < judge_timestamp) - x_scrollbar_remove (bar); + struct scrollbar *b = XSCROLLBAR (bar); + + x_scrollbar_remove (b); + + next = b->next; + b->next = b->prev = Qnil; } + + FRAME_CONDEMNED_SCROLLBARS (f) = Qnil; + + /* Now there should be no references to the condemned scrollbars, + and they should get garbage-collected. */ } @@ -1990,51 +2112,24 @@ struct scrollbar *bar; XEvent *event; { + Window w = SCROLLBAR_X_WINDOW (bar); + GC gc = XFRAME (WINDOW_FRAME (XWINDOW (bar->window)))->display.x->normal_gc; + 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_scrollbar_set_handle (bar, XINT (bar->start), XINT (bar->end), 1); + + /* Draw a one-pixel border just inside the edges of the scrollbar. */ + XDrawRectangle (x_current_display, w, 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); + 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; } @@ -2047,78 +2142,85 @@ XEvent *event; struct input_event *emacs_event; { + if (XTYPE (bar->window) != Lisp_Window) + abort (); + emacs_event->kind = scrollbar_click; - XSETINT (emacs_event->code, event->xbutton.button - Button1); + 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->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->frame_or_window = bar->window; 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; - } + { + int internal_height = + VERTICAL_SCROLLBAR_INSIDE_HEIGHT (XINT (bar->height)); + int top_range = + VERTICAL_SCROLLBAR_TOP_RANGE (XINT (bar->height)); + int y = event->xbutton.y - VERTICAL_SCROLLBAR_TOP_BORDER; + + if (y < 0) y = 0; + if (y > top_range) y = top_range; + + if (y < XINT (bar->start)) + emacs_event->part = scrollbar_above_handle; + else if (y < XINT (bar->end) + VERTICAL_SCROLLBAR_MIN_HANDLE) + emacs_event->part = scrollbar_handle; + else + emacs_event->part = scrollbar_below_handle; + + /* If the user has just clicked on the handle, record where they're + holding it. */ + if (event->type == ButtonPress + && emacs_event->part == scrollbar_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_scrollbar_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 == scrollbar_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 scrollbar. */ static void -x_scrollbar_handle_motion (bar, event) +x_scrollbar_note_movement (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); + XSET (last_mouse_scrollbar, Lisp_Vector, bar); /* If we're dragging the bar, display it. */ - if (bar->dragging != -1) + if (! NILP (bar->dragging)) { /* Where should the handle be now? */ - int new_start = event->xmotion.x - bar->dragging; - - if (new_start != bar->start) + int new_start = event->xmotion.y - XINT (bar->dragging); + + if (new_start != XINT (bar->start)) { - int new_end = new_start + (bar->end - bar->start); + int new_end = new_start + (XINT (bar->end) - XINT (bar->start)); - x_scrollbar_set_handle (bar, new_start, new_end); + x_scrollbar_set_handle (bar, new_start, new_end, 0); } } @@ -2134,6 +2236,79 @@ } } +/* Return information to the user about the current position of the mouse + on the scrollbar. */ +static void +x_scrollbar_report_motion (f, bar_window, part, x, y, time) + FRAME_PTR *f; + Lisp_Object *bar_window; + enum scrollbar_part *part; + Lisp_Object *x, *y; + unsigned long *time; +{ + struct scrollbar *bar = XSCROLLBAR (last_mouse_scrollbar); + int win_x, win_y; + + /* Get the mouse's position relative to the scrollbar window, and + report that. */ + { + Window dummy_window; + int dummy_coord; + unsigned int dummy_mask; + + if (! XQueryPointer (x_current_display, + SCROLLBAR_X_WINDOW (bar), + + /* Root, child, root x and root y. */ + &dummy_window, &dummy_window, + &dummy_coord, &dummy_coord, + + /* Position relative to scrollbar. */ + &win_x, &win_y, + + /* Mouse buttons and modifier keys. */ + &dummy_mask)) + { + *f = 0; + return; + } + } + + { + int inside_height = VERTICAL_SCROLLBAR_INSIDE_HEIGHT (XINT (bar->height)); + int top_range = VERTICAL_SCROLLBAR_TOP_RANGE (XINT (bar->height)); + + win_y -= VERTICAL_SCROLLBAR_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 = scrollbar_handle; + else if (win_y < XINT (bar->start)) + *part = scrollbar_above_handle; + else if (win_y < XINT (bar->end) + VERTICAL_SCROLLBAR_MIN_HANDLE) + *part = scrollbar_handle; + else + *part = scrollbar_below_handle; + + XSET (*x, Lisp_Int, win_y); + XSET (*y, Lisp_Int, top_range); + *time = last_mouse_movement_time; + } + + mouse_moved = 0; + last_mouse_scrollbar = Qnil; +} + /* The main X event-reading loop - XTread_socket. */ @@ -2322,7 +2497,6 @@ 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 @@ -2345,7 +2519,6 @@ event.xgraphicsexpose.x, event.xgraphicsexpose.y, event.xgraphicsexpose.width, event.xgraphicsexpose.height); - x_scrollbar_background_expose (f, &event); } break; @@ -2477,7 +2650,7 @@ { bufp->kind = non_ascii_keystroke; XSET (bufp->code, Lisp_Int, (unsigned) keysym - 0xff50); - bufp->frame = f; + XSET (bufp->frame_or_window, Lisp_Frame, f); bufp->modifiers = x_convert_modifiers (modifiers); bufp->timestamp = event.xkey.time; bufp++; @@ -2494,7 +2667,7 @@ *copy_buffer |= METABIT; bufp->kind = ascii_keystroke; XSET (bufp->code, Lisp_Int, *copy_buffer); - bufp->frame = f; + XSET (bufp->frame_or_window, Lisp_Frame, f); bufp->timestamp = event.xkey.time; bufp++; } @@ -2503,7 +2676,7 @@ { bufp->kind = ascii_keystroke; XSET (bufp->code, Lisp_Int, copy_buffer[i]); - bufp->frame = f; + XSET (bufp->frame_or_window, Lisp_Frame, f); bufp->timestamp = event.xkey.time; bufp++; } @@ -2553,7 +2726,7 @@ bufp->kind = ascii_keystroke; XSET (bufp->code, Lisp_Int, where_mapping[i]); XSET (bufp->time, Lisp_Int, event.xkey.time); - bufp->frame = f; + XSET (bufp->frame_or_window, Lisp_Frame, f); bufp++; } count += nbytes; @@ -2668,14 +2841,14 @@ { f = x_window_to_frame (event.xmotion.window); if (f) - note_mouse_position (f, &event.xmotion); + note_mouse_movement (f, &event.xmotion); else { struct scrollbar *bar = x_window_to_scrollbar (event.xmotion.window); if (bar) - x_scrollbar_handle_motion (bar, &event); + x_scrollbar_note_movement (bar, &event); } } break; @@ -2768,13 +2941,13 @@ { bufp->kind = ascii_keystroke; bufp->code = (char) 'X' & 037; /* C-x */ - bufp->frame = f; + XSET (bufp->frame_or_window, Lisp_Frame, f); XSET (bufp->time, Lisp_Int, event.xkey.time); bufp++; bufp->kind = ascii_keystroke; bufp->code = (char) 0; /* C-@ */ - bufp->frame = f; + XSET (bufp->frame_or_window, Lisp_Frame, f); XSET (bufp->time, Lisp_Int, event.xkey.time); bufp++; @@ -2914,15 +3087,12 @@ x_draw_box (f) struct frame *f; { - int left = f->cursor_x * FONT_WIDTH (f->display.x->font) - + f->display.x->internal_border_width; - int top = f->cursor_y * FONT_HEIGHT (f->display.x->font) - + f->display.x->internal_border_width; + int left = CHAR_TO_PIXEL_COL (f, f->cursor_x); + int top = CHAR_TO_PIXEL_ROW (f, f->cursor_y); int width = FONT_WIDTH (f->display.x->font); int height = FONT_HEIGHT (f->display.x->font); #ifdef HAVE_X11 - /* Perhaps we should subtract 1 from width and height... */ XDrawRectangle (x_current_display, FRAME_X_WINDOW (f), f->display.x->cursor_gc, left, top, width - 1, height - 1); @@ -2962,18 +3132,10 @@ #ifdef HAVE_X11 x_display_cursor (f, 0); -#if 0 - XClearArea (x_current_display, FRAME_X_WINDOW (f), - f->phys_cursor_x * FONT_WIDTH (f->display.x->font) - + f->display.x->internal_border_width, - f->phys_cursor_y * FONT_HEIGHT (f->display.x->font) - + f->display.x->internal_border_width, - FONT_WIDTH (f->display.x->font) + 1, FONT_HEIGHT (f->display.x->font) + 1, False); -#endif /* ! 0 */ #else /* ! defined (HAVE_X11) */ XPixSet (FRAME_X_WINDOW (f), - f->phys_cursor_x * FONT_WIDTH (f->display.x->font) + f->display.x->internal_border_width, - f->phys_cursor_y * FONT_HEIGHT (f->display.x->font) + f->display.x->internal_border_width, + CHAR_TO_PIXEL_COL (f, f->phys_cursor_x), + CHAR_TO_PIXEL_ROW (f, f->phys_cursor_y), FONT_WIDTH (f->display.x->font), FONT_HEIGHT (f->display.x->font), f->display.x->background_pixel); #endif /* ! defined (HAVE_X11) */ @@ -2998,10 +3160,8 @@ if (phys_x >= 0 && (!on || phys_x != f->cursor_x || phys_y != f->cursor_y)) { - x1 = phys_x * FONT_WIDTH (f->display.x->font) - + f->display.x->internal_border_width; - y1 = phys_y * FONT_HEIGHT (f->display.x->font) - + f->display.x->internal_border_width - 1; + x1 = CHAR_TO_PIXEL_COL (f, phys_x); + y1 = CHAR_TO_PIXEL_ROW (f, phys_y) - 1; y2 = y1 + FONT_HEIGHT (f->display.x->font) + 1; XDrawLine (x_current_display, FRAME_X_WINDOW (f), @@ -3012,10 +3172,8 @@ if (on && f == x_highlight_frame) { - x1 = f->cursor_x * FONT_WIDTH (f->display.x->font) - + f->display.x->internal_border_width; - y1 = f->cursor_y * FONT_HEIGHT (f->display.x->font) - + f->display.x->internal_border_width - 1; + x1 = CHAR_TO_PIXEL_COL (f, f->cursor_x); + y1 = CHAR_TO_PIXEL_ROW (f, f->cursor_y) - 1; y2 = y1 + FONT_HEIGHT (f->display.x->font) + 1; XDrawLine (x_current_display, FRAME_X_WINDOW (f), @@ -3042,10 +3200,8 @@ int highlight; { dumpglyphs (f, - (column * FONT_WIDTH (f->display.x->font) - + f->display.x->internal_border_width), - (row * FONT_HEIGHT (f->display.x->font) - + f->display.x->internal_border_width), + CHAR_TO_PIXEL_COL (f, column), + CHAR_TO_PIXEL_ROW (f, row), &glyph, 1, highlight, f->display.x->font); } @@ -3311,16 +3467,18 @@ /* Handling X errors. */ -/* A handler for SIGPIPE, when it occurs on the X server's connection. - This basically does an orderly shutdown of Emacs. */ +/* Shut down Emacs in an orderly fashion, because of a SIGPIPE on the + X server's connection, or an error reported via the X protocol. */ static SIGTYPE x_connection_closed () { if (_Xdebug) abort (); - else - Fkill_emacs (make_number (70)); + + shut_down_emacs (0); + + exit (70); } /* An X error handler which prints an error message and then kills Emacs. @@ -3333,10 +3491,6 @@ { 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. */ @@ -3344,6 +3498,10 @@ fprintf (stderr, "X protocol error: %s on protocol request %d\n", buf, error->request_code); + /* 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 (); + x_connection_closed (); } @@ -3613,7 +3771,6 @@ { int pixelwidth, pixelheight; int mask; - int ibw = f->display.x->internal_border_width; BLOCK_INPUT; @@ -3635,7 +3792,7 @@ change request eventually, and we'll hear how it went when the ConfigureNotify event gets here. */ FRAME_WIDTH (f) = cols; - FRAME_WIDTH (f) = rows; + FRAME_HEIGHT (f) = rows; PIXEL_WIDTH (f) = pixelwidth; PIXEL_HEIGHT (f) = pixelheight; @@ -3647,10 +3804,11 @@ x_set_resize_hint (f) struct frame *f; { - - XSetResizeHint (FRAME_X_WINDOW (f), 2 * f->display.x->internal_border_width, + XSetResizeHint (FRAME_X_WINDOW (f), + 2 * f->display.x->internal_border_width, 2 * f->display.x->internal_border_width, - FONT_WIDTH (f->display.x->font), FONT_HEIGHT (f->display.x->font)); + FONT_WIDTH (f->display.x->font), + FONT_HEIGHT (f->display.x->font)); } #endif /* HAVE_X11 */ @@ -3664,12 +3822,8 @@ x_raise_frame (f); - 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); + pix_x = CHAR_TO_PIXEL_COL (f, x) + FONT_WIDTH (f->display.x->font) / 2; + pix_y = CHAR_TO_PIXEL_ROW (f, y) + 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); @@ -4016,8 +4170,8 @@ 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 = PIXEL_TO_CHAR_WIDTH (f, x_screen_width); - size_hints.max_height = PIXEL_TO_CHAR_HEIGHT (f, x_screen_height); + size_hints.max_width = x_screen_width - CHAR_TO_PIXEL_WIDTH (f, 0); + size_hints.max_height = x_screen_height - CHAR_TO_PIXEL_HEIGHT (f, 0); { int base_width, base_height; @@ -4128,17 +4282,6 @@ extern int old_fcntl_owner; #endif /* ! defined (F_SETOWN) */ - { - fprintf (stderr, "\ -Hey! The Emacs 19 sources currently contain incomplete changes -for scrollbars, and don't run properly under X! - -It is supposed to work under termcap, though. - --JimB"); - } - - x_focus_frame = x_highlight_frame = 0; x_current_display = XOpenDisplay (display_name); @@ -4243,7 +4386,7 @@ reassert_line_highlight_hook = XTreassert_line_highlight; mouse_position_hook = XTmouse_position; frame_rehighlight_hook = XTframe_rehighlight; - set_vertical_scrollbar_hook = XTset_scrollbar; + set_vertical_scrollbar_hook = XTset_vertical_scrollbar; condemn_scrollbars_hook = XTcondemn_scrollbars; redeem_scrollbar_hook = XTredeem_scrollbar; judge_scrollbars_hook = XTjudge_scrollbars; @@ -4274,6 +4417,8 @@ { staticpro (&invocation_name); invocation_name = Qnil; + + staticpro (&last_mouse_scrollbar); } #endif /* ! defined (HAVE_X11) */ #endif /* ! defined (HAVE_X_WINDOWS) */