Edit

IABSD.fr/xenocara/app/xterm/scrollbar.c

Branch :

  • Show log

    Commit

  • Author : matthieu
    Date : 2019-12-29 08:54:02
    Hash : 150aa6e9
    Message : Update to xterm 351. tested and ok solene@ jca@

  • app/xterm/scrollbar.c
  • /* $XTermId: scrollbar.c,v 1.208 2019/07/19 00:40:41 tom Exp $ */
    
    /*
     * Copyright 2000-2018,2019 by Thomas E. Dickey
     *
     *                         All Rights Reserved
     *
     * Permission is hereby granted, free of charge, to any person obtaining a
     * copy of this software and associated documentation files (the
     * "Software"), to deal in the Software without restriction, including
     * without limitation the rights to use, copy, modify, merge, publish,
     * distribute, sublicense, and/or sell copies of the Software, and to
     * permit persons to whom the Software is furnished to do so, subject to
     * the following conditions:
     *
     * The above copyright notice and this permission notice shall be included
     * in all copies or substantial portions of the Software.
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
     * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
     * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
     * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
     * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
     * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     *
     * Except as contained in this notice, the name(s) of the above copyright
     * holders shall not be used in advertising or otherwise to promote the
     * sale, use or other dealings in this Software without prior written
     * authorization.
     *
     *
     * Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
     *
     *                         All Rights Reserved
     *
     * Permission to use, copy, modify, and distribute this software and its
     * documentation for any purpose and without fee is hereby granted,
     * provided that the above copyright notice appear in all copies and that
     * both that copyright notice and this permission notice appear in
     * supporting documentation, and that the name of Digital Equipment
     * Corporation not be used in advertising or publicity pertaining to
     * distribution of the software without specific, written prior permission.
     *
     *
     * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
     * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
     * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
     * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
     * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
     * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
     * SOFTWARE.
     */
    
    #include <xterm.h>
    
    #include <X11/Xatom.h>
    
    #if defined(HAVE_LIB_XAW)
    #include <X11/Xaw/Scrollbar.h>
    #elif defined(HAVE_LIB_XAW3D)
    #include <X11/Xaw3d/Scrollbar.h>
    #elif defined(HAVE_LIB_XAW3DXFT)
    #include <X11/Xaw3dxft/Scrollbar.h>
    #elif defined(HAVE_LIB_NEXTAW)
    #include <X11/neXtaw/Scrollbar.h>
    #elif defined(HAVE_LIB_XAWPLUS)
    #include <X11/XawPlus/Scrollbar.h>
    #endif
    
    #if defined(HAVE_XKBQUERYEXTENSION)
    #include <X11/extensions/XKB.h>
    #include <X11/XKBlib.h>
    #endif
    
    #include <data.h>
    #include <error.h>
    #include <menu.h>
    #include <xstrings.h>
    
    /*
     * The scrollbar's border overlaps the border of the vt100 window.  If there
     * is no border for the vt100, there can be no border for the scrollbar.
     */
    #define SCROLLBAR_BORDER(xw) (TScreenOf(xw)->scrollBarBorder)
    #if OPT_TOOLBAR
    #define ScrollBarBorder(xw) (BorderWidth(xw) ? SCROLLBAR_BORDER(xw) : 0)
    #else
    #define ScrollBarBorder(xw) SCROLLBAR_BORDER(xw)
    #endif
    
    /* Event handlers */
    
    static void ScrollTextTo PROTO_XT_CALLBACK_ARGS;
    static void ScrollTextUpDownBy PROTO_XT_CALLBACK_ARGS;
    
    /* Resize the text window for a terminal screen, modifying the
     * appropriate WM_SIZE_HINTS and taking advantage of bit gravity.
     */
    void
    DoResizeScreen(XtermWidget xw)
    {
        TScreen *screen = TScreenOf(xw);
    
        int border = 2 * screen->border;
        int min_wide = border + screen->fullVwin.sb_info.width;
        int min_high = border;
        XtGeometryResult geomreqresult;
        Dimension reqWidth, reqHeight, repWidth, repHeight;
    #ifndef NO_ACTIVE_ICON
        VTwin *saveWin = WhichVWin(screen);
    
        /* all units here want to be in the normal font units */
        WhichVWin(screen) = &screen->fullVwin;
    #endif /* NO_ACTIVE_ICON */
    
        /*
         * I'm going to try to explain, as I understand it, why we
         * have to do XGetWMNormalHints and XSetWMNormalHints here,
         * although I can't guarantee that I've got it right.
         *
         * In a correctly written toolkit program, the Shell widget
         * parses the user supplied geometry argument.  However,
         * because of the way xterm does things, the VT100 widget does
         * the parsing of the geometry option, not the Shell widget.
         * The result of this is that the Shell widget doesn't set the
         * correct window manager hints, and doesn't know that the
         * user has specified a geometry.
         *
         * The XtVaSetValues call below tells the Shell widget to
         * change its hints.  However, since it's confused about the
         * hints to begin with, it doesn't get them all right when it
         * does the SetValues -- it undoes some of what the VT100
         * widget did when it originally set the hints.
         *
         * To fix this, we do the following:
         *
         * 1. Get the sizehints directly from the window, going around
         *    the (confused) shell widget.
         * 2. Call XtVaSetValues to let the shell widget know which
         *    hints have changed.  Note that this may not even be
         *    necessary, since we're going to right ahead after that
         *    and set the hints ourselves, but it's good to put it
         *    here anyway, so that when we finally do fix the code so
         *    that the Shell does the right thing with hints, we
         *    already have the XtVaSetValues in place.
         * 3. We set the sizehints directly, this fixing up whatever
         *    damage was done by the Shell widget during the
         *    XtVaSetValues.
         *
         * Gross, huh?
         *
         * The correct fix is to redo VTRealize, VTInitialize and
         * VTSetValues so that font processing happens early enough to
         * give back responsibility for the size hints to the Shell.
         *
         * Someday, we hope to have time to do this.  Someday, we hope
         * to have time to completely rewrite xterm.
         */
    
        TRACE(("DoResizeScreen\n"));
    
    #if 1				/* ndef nothack */
        /*
         * NOTE: the hints and the XtVaSetValues() must match.
         */
        TRACE(("%s@%d -- ", __FILE__, __LINE__));
        TRACE_WM_HINTS(xw);
        getXtermSizeHints(xw);
    
        xtermSizeHints(xw, ScrollbarWidth(screen));
    
        /* These are obsolete, but old clients may use them */
        xw->hints.width = MaxCols(screen) * FontWidth(screen) + xw->hints.min_width;
        xw->hints.height = MaxRows(screen) * FontHeight(screen) + xw->hints.min_height;
    #if OPT_MAXIMIZE
        /* assure single-increment resize for fullscreen */
        if (xw->work.ewmh[0].mode) {
    	xw->hints.width_inc = 1;
    	xw->hints.height_inc = 1;
        }
    #endif /* OPT_MAXIMIZE */
    #endif
    
        XSetWMNormalHints(screen->display, VShellWindow(xw), &xw->hints);
    
        reqWidth = (Dimension) (MaxCols(screen) * FontWidth(screen) + min_wide);
        reqHeight = (Dimension) (MaxRows(screen) * FontHeight(screen) + min_high);
    
    #if OPT_MAXIMIZE
        /* compensate for fullscreen mode */
        if (xw->work.ewmh[0].mode) {
    	Screen *xscreen = DefaultScreenOfDisplay(xw->screen.display);
    	reqWidth = (Dimension) WidthOfScreen(xscreen);
    	reqHeight = (Dimension) HeightOfScreen(xscreen);
    	ScreenResize(xw, reqWidth, reqHeight, &xw->flags);
        }
    #endif /* OPT_MAXIMIZE */
    
        TRACE(("...requesting screensize chars %dx%d, pixels %dx%d\n",
    	   MaxRows(screen),
    	   MaxCols(screen),
    	   reqHeight, reqWidth));
    
        geomreqresult = REQ_RESIZE((Widget) xw, reqWidth, reqHeight,
    			       &repWidth, &repHeight);
    
        if (geomreqresult == XtGeometryAlmost) {
    	TRACE(("...almost, retry screensize %dx%d\n", repHeight, repWidth));
    	geomreqresult = REQ_RESIZE((Widget) xw, repWidth,
    				   repHeight, NULL, NULL);
        }
    
        if (geomreqresult != XtGeometryYes) {
    	/* The resize wasn't successful, so we might need to adjust
    	   our idea of how large the screen is. */
    	TRACE(("...still no (%d) - resize the core-class\n", geomreqresult));
    	xw->core.widget_class->core_class.resize((Widget) xw);
        }
    #if 1				/* ndef nothack */
        /*
         * XtMakeResizeRequest() has the undesirable side-effect of clearing
         * the window manager's hints, even on a failed request.  This would
         * presumably be fixed if the shell did its own work.
         */
        if (xw->hints.flags
    	&& repHeight
    	&& repWidth) {
    	xw->hints.height = repHeight;
    	xw->hints.width = repWidth;
    	TRACE_HINTS(&xw->hints);
    	XSetWMNormalHints(screen->display, VShellWindow(xw), &xw->hints);
        }
    #endif
        XSync(screen->display, False);	/* synchronize */
        if (xtermAppPending())
    	xevents(xw);
    
    #ifndef NO_ACTIVE_ICON
        WhichVWin(screen) = saveWin;
    #endif /* NO_ACTIVE_ICON */
    }
    
    static Widget
    CreateScrollBar(XtermWidget xw, int x, int y, int height)
    {
        Widget result;
        Arg args[6];
    
        XtSetArg(args[0], XtNx, x);
        XtSetArg(args[1], XtNy, y);
        XtSetArg(args[2], XtNheight, height);
        XtSetArg(args[3], XtNreverseVideo, xw->misc.re_verse);
        XtSetArg(args[4], XtNorientation, XtorientVertical);
        XtSetArg(args[5], XtNborderWidth, ScrollBarBorder(xw));
    
        result = XtCreateWidget("scrollbar", scrollbarWidgetClass,
    			    (Widget) xw, args, XtNumber(args));
        XtAddCallback(result, XtNscrollProc, ScrollTextUpDownBy, 0);
        XtAddCallback(result, XtNjumpProc, ScrollTextTo, 0);
        return (result);
    }
    
    void
    ScrollBarReverseVideo(Widget scrollWidget)
    {
        XtermWidget xw = getXtermWidget(scrollWidget);
    
        if (xw != 0) {
    	SbInfo *sb = &(TScreenOf(xw)->fullVwin.sb_info);
    	Arg args[4];
    	Cardinal nargs = XtNumber(args);
    
    	/*
    	 * Remember the scrollbar's original colors.
    	 */
    	if (sb->rv_cached == False) {
    	    XtSetArg(args[0], XtNbackground, &(sb->bg));
    	    XtSetArg(args[1], XtNforeground, &(sb->fg));
    	    XtSetArg(args[2], XtNborderColor, &(sb->bdr));
    	    XtSetArg(args[3], XtNborderPixmap, &(sb->bdpix));
    	    XtGetValues(scrollWidget, args, nargs);
    	    sb->rv_cached = True;
    	    sb->rv_active = 0;
    	}
    
    	sb->rv_active = !(sb->rv_active);
    	if (sb->rv_active) {
    	    XtSetArg(args[0], XtNbackground, sb->fg);
    	    XtSetArg(args[1], XtNforeground, sb->bg);
    	} else {
    	    XtSetArg(args[0], XtNbackground, sb->bg);
    	    XtSetArg(args[1], XtNforeground, sb->fg);
    	}
    	nargs = 2;		/* don't set border_pixmap */
    	if (sb->bdpix == XtUnspecifiedPixmap) {
    	    /* if not pixmap then pixel */
    	    if (sb->rv_active) {
    		/* keep border visible */
    		XtSetArg(args[2], XtNborderColor, args[1].value);
    	    } else {
    		XtSetArg(args[2], XtNborderColor, sb->bdr);
    	    }
    	    nargs = 3;
    	}
    	XtSetValues(scrollWidget, args, nargs);
        }
    }
    
    void
    ScrollBarDrawThumb(XtermWidget xw, int mode)
    {
        TScreen *screen = TScreenOf(xw);
    
        if (screen->scrollWidget != 0) {
    	int thumbTop, thumbHeight, totalHeight;
    
    #if USE_DOUBLE_BUFFER
    	if (resource.buffered) {
    	    if (mode == 1) {
    		screen->buffered_sb++;
    		return;
    	    } else if (mode == 2) {
    		if (screen->buffered_sb == 0)
    		    return;
    	    }
    	}
    	screen->buffered_sb = 0;
    #else
    	(void) mode;
    #endif
    
    	thumbTop = ROW2INX(screen, screen->savedlines);
    	thumbHeight = MaxRows(screen);
    	totalHeight = thumbHeight + screen->savedlines;
    
    	XawScrollbarSetThumb(screen->scrollWidget,
    			     ((float) thumbTop) / (float) totalHeight,
    			     ((float) thumbHeight) / (float) totalHeight);
        }
    }
    
    void
    ResizeScrollBar(XtermWidget xw)
    {
        TScreen *screen = TScreenOf(xw);
    
        if (screen->scrollWidget != 0) {
    	int height = screen->fullVwin.height + screen->border * 2;
    	int width = screen->scrollWidget->core.width;
    	int ypos = -ScrollBarBorder(xw);
    #ifdef SCROLLBAR_RIGHT
    	int xpos = ((xw->misc.useRight)
    		    ? (screen->fullVwin.fullwidth -
    		       screen->scrollWidget->core.width -
    		       BorderWidth(screen->scrollWidget))
    		    : -ScrollBarBorder(xw));
    #else
    	int xpos = -ScrollBarBorder(xw);
    #endif
    
    	TRACE(("ResizeScrollBar at %d,%d %dx%d\n", ypos, xpos, height, width));
    
    	XtConfigureWidget(
    			     screen->scrollWidget,
    			     (Position) xpos,
    			     (Position) ypos,
    			     (Dimension) width,
    			     (Dimension) height,
    			     BorderWidth(screen->scrollWidget));
    	ScrollBarDrawThumb(xw, 1);
        }
    }
    
    void
    WindowScroll(XtermWidget xw, int top, Bool always GCC_UNUSED)
    {
        TScreen *screen = TScreenOf(xw);
    
    #if OPT_SCROLL_LOCK
        if (screen->allowScrollLock && (screen->scroll_lock && !always)) {
    	if (screen->scroll_dirty) {
    	    screen->scroll_dirty = False;
    	    ScrnRefresh(xw, 0, 0, MaxRows(screen), MaxCols(screen), False);
    	}
        } else
    #endif
        {
    	int i;
    
    	if (top < -screen->savedlines) {
    	    top = -screen->savedlines;
    	} else if (top > 0) {
    	    top = 0;
    	}
    
    	if ((i = screen->topline - top) != 0) {
    	    int lines;
    	    int scrolltop, scrollheight, refreshtop;
    
    	    if (screen->cursor_state)
    		HideCursor();
    	    lines = i > 0 ? i : -i;
    	    if (lines > MaxRows(screen))
    		lines = MaxRows(screen);
    	    scrollheight = screen->max_row - lines + 1;
    	    if (i > 0)
    		refreshtop = scrolltop = 0;
    	    else {
    		scrolltop = lines;
    		refreshtop = scrollheight;
    	    }
    	    scrolling_copy_area(xw, scrolltop, scrollheight, -i);
    	    screen->topline = top;
    
    	    ScrollSelection(screen, i, True);
    
    	    xtermClear2(xw,
    			OriginX(screen),
    			OriginY(screen) + refreshtop * FontHeight(screen),
    			(unsigned) Width(screen),
    			(unsigned) (lines * FontHeight(screen)));
    	    ScrnRefresh(xw, refreshtop, 0, lines, MaxCols(screen), False);
    
    #if OPT_BLINK_CURS || OPT_BLINK_TEXT
    	    RestartBlinking(xw);
    #endif
    	}
        }
        ScrollBarDrawThumb(xw, 1);
    }
    
    #ifdef SCROLLBAR_RIGHT
    /*
     * Adjust the scrollbar position if we're asked to turn on scrollbars for the
     * first time (or after resizing) after the xterm is already running.  That
     * makes the window grow after we've initially configured the scrollbar's
     * position.  (There must be a better way).
     */
    void
    updateRightScrollbar(XtermWidget xw)
    {
        TScreen *screen = TScreenOf(xw);
    
        if (xw->misc.useRight
    	&& screen->fullVwin.fullwidth < xw->core.width)
    	XtVaSetValues(screen->scrollWidget,
    		      XtNx, screen->fullVwin.fullwidth - BorderWidth(screen->scrollWidget),
    		      (XtPointer) 0);
    }
    #endif
    
    void
    ScrollBarOn(XtermWidget xw, Bool init)
    {
        TScreen *screen = TScreenOf(xw);
    
        if (screen->fullVwin.sb_info.width || IsIcon(screen))
    	return;
    
        TRACE(("ScrollBarOn(init %s)\n", BtoS(init)));
        if (init) {			/* then create it only */
    	if (screen->scrollWidget == 0) {
    	    /* make it a dummy size and resize later */
    	    screen->scrollWidget = CreateScrollBar(xw,
    						   -ScrollBarBorder(xw),
    						   -ScrollBarBorder(xw),
    						   5);
    	    if (screen->scrollWidget == NULL) {
    		Bell(xw, XkbBI_MinorError, 0);
    	    }
    	}
        } else if (!screen->scrollWidget || !XtIsRealized((Widget) xw)) {
    	Bell(xw, XkbBI_MinorError, 0);
    	Bell(xw, XkbBI_MinorError, 0);
        } else {
    
    	ResizeScrollBar(xw);
    	xtermAddInput(screen->scrollWidget);
    	XtRealizeWidget(screen->scrollWidget);
    	TRACE_TRANS("scrollbar", screen->scrollWidget);
    
    	screen->fullVwin.sb_info.rv_cached = False;
    
    	screen->fullVwin.sb_info.width = (screen->scrollWidget->core.width
    					  + BorderWidth(screen->scrollWidget));
    
    	TRACE(("setting scrollbar width %d = %d + %d\n",
    	       screen->fullVwin.sb_info.width,
    	       screen->scrollWidget->core.width,
    	       BorderWidth(screen->scrollWidget)));
    
    	ScrollBarDrawThumb(xw, 1);
    	DoResizeScreen(xw);
    
    #ifdef SCROLLBAR_RIGHT
    	updateRightScrollbar(xw);
    #endif
    
    	XtMapWidget(screen->scrollWidget);
    	update_scrollbar();
    	if (screen->visbuf) {
    	    xtermClear(xw);
    	    Redraw();
    	}
        }
    }
    
    void
    ScrollBarOff(XtermWidget xw)
    {
        TScreen *screen = TScreenOf(xw);
    
        if (!screen->fullVwin.sb_info.width || IsIcon(screen))
    	return;
    
        TRACE(("ScrollBarOff\n"));
        if (XtIsRealized((Widget) xw)) {
    	XtUnmapWidget(screen->scrollWidget);
    	screen->fullVwin.sb_info.width = 0;
    	DoResizeScreen(xw);
    	update_scrollbar();
    	if (screen->visbuf) {
    	    xtermClear(xw);
    	    Redraw();
    	}
        } else {
    	Bell(xw, XkbBI_MinorError, 0);
        }
    }
    
    /*
     * Toggle the visibility of the scrollbars.
     */
    void
    ToggleScrollBar(XtermWidget xw)
    {
        TScreen *screen = TScreenOf(xw);
    
        if (IsIcon(screen)) {
    	Bell(xw, XkbBI_MinorError, 0);
        } else {
    	TRACE(("ToggleScrollBar{{\n"));
    	if (screen->fullVwin.sb_info.width) {
    	    ScrollBarOff(xw);
    	} else {
    	    ScrollBarOn(xw, False);
    	}
    	update_scrollbar();
    	TRACE(("...ToggleScrollBar}}\n"));
        }
    }
    
    /*ARGSUSED*/
    static void
    ScrollTextTo(
    		Widget scrollbarWidget,
    		XtPointer client_data GCC_UNUSED,
    		XtPointer call_data)
    {
        XtermWidget xw = getXtermWidget(scrollbarWidget);
    
        if (xw != 0) {
    	float *topPercent = (float *) call_data;
    	TScreen *screen = TScreenOf(xw);
    	int thumbTop;		/* relative to first saved line */
    	int newTopLine;
    
    	/*
    	 * screen->savedlines : Number of offscreen text lines,
    	 * MaxRows(screen)    : Number of onscreen  text lines,
    	 */
    	thumbTop = (int) (*topPercent
    			  * (float) (screen->savedlines + MaxRows(screen)));
    	newTopLine = thumbTop - screen->savedlines;
    	WindowScroll(xw, newTopLine, True);
        }
    }
    
    /*ARGSUSED*/
    static void
    ScrollTextUpDownBy(
    		      Widget scrollbarWidget,
    		      XtPointer client_data GCC_UNUSED,
    		      XtPointer call_data)
    {
        XtermWidget xw = getXtermWidget(scrollbarWidget);
    
        if (xw != 0) {
    	long pixels = (long) call_data;
    
    	TScreen *screen = TScreenOf(xw);
    	int rowOnScreen, newTopLine;
    
    	rowOnScreen = (int) (pixels / FontHeight(screen));
    	if (rowOnScreen == 0) {
    	    if (pixels < 0)
    		rowOnScreen = -1;
    	    else if (pixels > 0)
    		rowOnScreen = 1;
    	}
    	newTopLine = ROW2INX(screen, rowOnScreen);
    	WindowScroll(xw, newTopLine, True);
        }
    }
    
    /*
     * assume that b is alphabetic and allow plural
     */
    static int
    CompareWidths(const char *a, const char *b, int *modifier)
    {
        int result;
        char ca, cb;
    
        *modifier = 0;
        if (!a || !b)
    	return 0;
    
        for (;;) {
    	ca = x_toupper(*a);
    	cb = x_toupper(*b);
    	if (ca != cb || ca == '\0')
    	    break;		/* if not eq else both nul */
    	a++, b++;
        }
        if (cb != '\0')
    	return 0;
    
        if (ca == 'S')
    	ca = *++a;
    
        switch (ca) {
        case '+':
        case '-':
    	*modifier = (ca == '-' ? -1 : 1) * atoi(a + 1);
    	result = 1;
    	break;
    
        case '\0':
    	result = 1;
    	break;
    
        default:
    	result = 0;
    	break;
        }
        return result;
    }
    
    static long
    params_to_pixels(TScreen *screen, String *params, Cardinal n)
    {
        int mult = 1;
        const char *s;
        int modifier;
    
        switch (n > 2 ? 2 : n) {
        case 2:
    	s = params[1];
    	if (CompareWidths(s, "PAGE", &modifier)) {
    	    mult = (MaxRows(screen) + modifier) * FontHeight(screen);
    	} else if (CompareWidths(s, "HALFPAGE", &modifier)) {
    	    mult = ((MaxRows(screen) + modifier) * FontHeight(screen)) / 2;
    	} else if (CompareWidths(s, "PIXEL", &modifier)) {
    	    mult = 1;
    	} else {
    	    /* else assume that it is Line */
    	    mult = FontHeight(screen);
    	}
    	mult *= atoi(params[0]);
    	TRACE(("params_to_pixels(%s,%s) = %d\n", params[0], params[1], mult));
    	break;
        case 1:
    	mult = atoi(params[0]) * FontHeight(screen);	/* lines */
    	TRACE(("params_to_pixels(%s) = %d\n", params[0], mult));
    	break;
        default:
    	mult = screen->scrolllines * FontHeight(screen);
    	TRACE(("params_to_pixels() = %d\n", mult));
    	break;
        }
        return mult;
    }
    
    static long
    AmountToScroll(Widget w, String *params, Cardinal nparams)
    {
        long result = 0;
        XtermWidget xw;
    
        if ((xw = getXtermWidget(w)) != 0) {
    	TScreen *screen = TScreenOf(xw);
    	if (nparams <= 2
    	    || screen->send_mouse_pos == MOUSE_OFF) {
    	    result = params_to_pixels(screen, params, nparams);
    	}
        }
        return result;
    }
    
    static void
    AlternateScroll(Widget w, long amount)
    {
        XtermWidget xw;
        TScreen *screen;
    
        if ((xw = getXtermWidget(w)) != 0 &&
    	(screen = TScreenOf(xw)) != 0 &&
    	screen->alternateScroll && screen->whichBuf) {
    	ANSI reply;
    
    	amount /= FontHeight(screen);
    	memset(&reply, 0, sizeof(reply));
    	reply.a_type = ((xw->keyboard.flags & MODE_DECCKM)
    			? ANSI_SS3
    			: ANSI_CSI);
    	if (amount > 0) {
    	    reply.a_final = 'B';
    	} else {
    	    amount = -amount;
    	    reply.a_final = 'A';
    	}
    	while (amount-- > 0) {
    	    unparseseq(xw, &reply);
    	}
        } else {
    	ScrollTextUpDownBy(w, (XtPointer) 0, (XtPointer) amount);
        }
    }
    
    /*ARGSUSED*/
    void
    HandleScrollTo(
    		  Widget w,
    		  XEvent *event GCC_UNUSED,
    		  String *params,
    		  Cardinal *nparams)
    {
        XtermWidget xw;
        TScreen *screen;
    
        if ((xw = getXtermWidget(w)) != 0 &&
    	(screen = TScreenOf(xw)) != 0 &&
    	*nparams > 0) {
    	long amount;
    	int value;
    	int to_top = (screen->topline - screen->savedlines);
    	if (!x_strcasecmp(params[0], "begin")) {
    	    amount = to_top * FontHeight(screen);
    	} else if (!x_strcasecmp(params[0], "end")) {
    	    amount = -to_top * FontHeight(screen);
    	} else if ((value = atoi(params[0])) >= 0) {
    	    amount = (value + to_top) * FontHeight(screen);
    	} else {
    	    amount = 0;
    	}
    	AlternateScroll(w, amount);
        }
    }
    
    /*ARGSUSED*/
    void
    HandleScrollForward(
    		       Widget xw,
    		       XEvent *event GCC_UNUSED,
    		       String *params,
    		       Cardinal *nparams)
    {
        long amount;
    
        if ((amount = AmountToScroll(xw, params, *nparams)) != 0) {
    	AlternateScroll(xw, amount);
        }
    }
    
    /*ARGSUSED*/
    void
    HandleScrollBack(
    		    Widget xw,
    		    XEvent *event GCC_UNUSED,
    		    String *params,
    		    Cardinal *nparams)
    {
        long amount;
    
        if ((amount = -AmountToScroll(xw, params, *nparams)) != 0) {
    	AlternateScroll(xw, amount);
        }
    }
    
    #if OPT_SCROLL_LOCK
    #define SCROLL_LOCK_LED 3
    
    #ifdef HAVE_XKBQUERYEXTENSION
    /*
     * Check for Xkb on client and server.
     */
    static int
    have_xkb(Display *dpy)
    {
        static int initialized = -1;
    
        if (initialized < 0) {
    	int xkbmajor = XkbMajorVersion;
    	int xkbminor = XkbMinorVersion;
    	int xkbopcode, xkbevent, xkberror;
    
    	initialized = 0;
    	if (XkbLibraryVersion(&xkbmajor, &xkbminor)
    	    && XkbQueryExtension(dpy,
    				 &xkbopcode,
    				 &xkbevent,
    				 &xkberror,
    				 &xkbmajor,
    				 &xkbminor)) {
    	    TRACE(("we have Xkb\n"));
    	    initialized = 1;
    #if OPT_TRACE
    	    {
    		XkbDescPtr xkb;
    		unsigned int mask;
    
    		xkb = XkbGetKeyboard(dpy, XkbAllComponentsMask, XkbUseCoreKbd);
    		if (xkb != NULL) {
    		    int n;
    
    		    TRACE(("XkbGetKeyboard ok\n"));
    		    for (n = 0; n < XkbNumVirtualMods; ++n) {
    			if (xkb->names->vmods[n] != 0) {
    			    char *modStr = XGetAtomName(xkb->dpy,
    							xkb->names->vmods[n]);
    			    if (modStr != 0) {
    				XkbVirtualModsToReal(xkb,
    						     (unsigned) (1 << n),
    						     &mask);
    				TRACE(("  name[%d] %s (%#x)\n", n, modStr, mask));
    			    }
    			}
    		    }
    		    XkbFreeKeyboard(xkb, 0, True);
    		}
    	    }
    #endif
    	}
        }
        return initialized;
    }
    
    static Boolean
    getXkbLED(Display *dpy, const char *name, Boolean *result)
    {
        Atom my_atom;
        Boolean success = False;
        Bool state;
    
        if (have_xkb(dpy)) {
    	my_atom = XInternAtom(dpy, name, False);
    	if ((my_atom != None) &&
    	    XkbGetNamedIndicator(dpy, my_atom, NULL, &state, NULL, NULL)) {
    	    *result = (Boolean) state;
    	    success = True;
    	}
        }
    
        return success;
    }
    
    /*
     * Use Xkb if we have it (still unreliable, but slightly better than hardcoded).
     */
    static Boolean
    showXkbLED(Display *dpy, const char *name, Bool enable)
    {
        Atom my_atom;
        Boolean result = False;
    
        if (have_xkb(dpy)) {
    	my_atom = XInternAtom(dpy, name, False);
    	if ((my_atom != None) &&
    	    XkbGetNamedIndicator(dpy, my_atom, NULL, NULL, NULL, NULL) &&
    	    XkbSetNamedIndicator(dpy, my_atom, True, enable, False, NULL)) {
    	    result = True;
    	}
        }
    
        return result;
    }
    #endif
    
    /*
     * xlsatoms agrees with this list.  However Num/Caps lock are generally
     * unusable due to special treatment in X.  They are used here for
     * completeness.
     */
    static const char *led_table[] =
    {
        "Num Lock",
        "Caps Lock",
        "Scroll Lock"
    };
    
    static Boolean
    xtermGetLED(TScreen *screen, Cardinal led_number)
    {
        Display *dpy = screen->display;
        Boolean result = False;
    
    #ifdef HAVE_XKBQUERYEXTENSION
        if (!getXkbLED(dpy, led_table[led_number - 1], &result))
    #endif
        {
    	XKeyboardState state;
    	unsigned long my_bit = (unsigned long) (1 << (led_number - 1));
    
    	XGetKeyboardControl(dpy, &state);
    
    	result = (Boolean) ((state.led_mask & my_bit) != 0);
        }
    
        TRACE(("xtermGetLED %d:%s\n", led_number, BtoS(result)));
        return result;
    }
    
    /*
     * Display the given LED, preferably independent of keyboard state.
     */
    void
    xtermShowLED(TScreen *screen, Cardinal led_number, Bool enable)
    {
        TRACE(("xtermShowLED %d:%s\n", led_number, BtoS(enable)));
        if ((led_number >= 1) && (led_number <= XtNumber(led_table))) {
    	Display *dpy = screen->display;
    
    #ifdef HAVE_XKBQUERYEXTENSION
    	if (!showXkbLED(dpy, led_table[led_number - 1], enable))
    #endif
    	{
    	    XKeyboardState state;
    	    XKeyboardControl values;
    	    unsigned long use_mask;
    	    unsigned long my_bit = (unsigned long) (1 << (led_number - 1));
    
    	    XGetKeyboardControl(dpy, &state);
    	    use_mask = state.led_mask;
    	    if (enable) {
    		use_mask |= my_bit;
    	    } else {
    		use_mask &= ~my_bit;
    	    }
    
    	    if (state.led_mask != use_mask) {
    		values.led = (int) led_number;
    		values.led_mode = enable;
    		XChangeKeyboardControl(dpy, KBLed | KBLedMode, &values);
    	    }
    	}
        }
    }
    
    void
    xtermClearLEDs(TScreen *screen)
    {
        Display *dpy = screen->display;
        XKeyboardControl values;
    
        TRACE(("xtermClearLEDs\n"));
    #ifdef HAVE_XKBQUERYEXTENSION
        ShowScrollLock(screen, False);
    #endif
        memset(&values, 0, sizeof(values));
        XChangeKeyboardControl(dpy, KBLedMode, &values);
    }
    
    void
    ShowScrollLock(TScreen *screen, Bool enable)
    {
        xtermShowLED(screen, SCROLL_LOCK_LED, enable);
    }
    
    void
    GetScrollLock(TScreen *screen)
    {
        if (screen->allowScrollLock)
    	screen->scroll_lock = xtermGetLED(screen, SCROLL_LOCK_LED);
    }
    
    void
    SetScrollLock(TScreen *screen, Bool enable)
    {
        if (screen->allowScrollLock) {
    	if (screen->scroll_lock != enable) {
    	    TRACE(("SetScrollLock %s\n", BtoS(enable)));
    	    screen->scroll_lock = (Boolean) enable;
    	    ShowScrollLock(screen, enable);
    	}
        }
    }
    
    /* ARGSUSED */
    void
    HandleScrollLock(Widget w,
    		 XEvent *event GCC_UNUSED,
    		 String *params,
    		 Cardinal *param_count)
    {
        XtermWidget xw;
    
        if ((xw = getXtermWidget(w)) != 0) {
    	TScreen *screen = TScreenOf(xw);
    
    	if (screen->allowScrollLock) {
    
    	    switch (decodeToggle(xw, params, *param_count)) {
    	    case toggleOff:
    		SetScrollLock(screen, False);
    		break;
    	    case toggleOn:
    		SetScrollLock(screen, True);
    		break;
    	    case toggleAll:
    		SetScrollLock(screen, !screen->scroll_lock);
    		break;
    	    }
    	}
        }
    }
    #endif