"Fossies" - the Fresh Open Source Software Archive

Member "xterm-379/scrollbar.c" (31 Dec 2021, 26731 Bytes) of package /linux/misc/xterm-379.tgz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "scrollbar.c" see the Fossies "Dox" file reference documentation.

    1 /* $XTermId: scrollbar.c,v 1.214 2021/12/31 23:35:02 tom Exp $ */
    2 
    3 /*
    4  * Copyright 2000-2020,2021 by Thomas E. Dickey
    5  *
    6  *                         All Rights Reserved
    7  *
    8  * Permission is hereby granted, free of charge, to any person obtaining a
    9  * copy of this software and associated documentation files (the
   10  * "Software"), to deal in the Software without restriction, including
   11  * without limitation the rights to use, copy, modify, merge, publish,
   12  * distribute, sublicense, and/or sell copies of the Software, and to
   13  * permit persons to whom the Software is furnished to do so, subject to
   14  * the following conditions:
   15  *
   16  * The above copyright notice and this permission notice shall be included
   17  * in all copies or substantial portions of the Software.
   18  *
   19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
   20  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
   21  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
   22  * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
   23  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
   24  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
   25  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   26  *
   27  * Except as contained in this notice, the name(s) of the above copyright
   28  * holders shall not be used in advertising or otherwise to promote the
   29  * sale, use or other dealings in this Software without prior written
   30  * authorization.
   31  *
   32  *
   33  * Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
   34  *
   35  *                         All Rights Reserved
   36  *
   37  * Permission to use, copy, modify, and distribute this software and its
   38  * documentation for any purpose and without fee is hereby granted,
   39  * provided that the above copyright notice appear in all copies and that
   40  * both that copyright notice and this permission notice appear in
   41  * supporting documentation, and that the name of Digital Equipment
   42  * Corporation not be used in advertising or publicity pertaining to
   43  * distribution of the software without specific, written prior permission.
   44  *
   45  *
   46  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
   47  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
   48  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
   49  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
   50  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
   51  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
   52  * SOFTWARE.
   53  */
   54 
   55 #include <xterm.h>
   56 
   57 #include <X11/Xatom.h>
   58 
   59 #if defined(HAVE_LIB_XAW)
   60 #include <X11/Xaw/Scrollbar.h>
   61 #elif defined(HAVE_LIB_XAW3D)
   62 #include <X11/Xaw3d/Scrollbar.h>
   63 #elif defined(HAVE_LIB_XAW3DXFT)
   64 #include <X11/Xaw3dxft/Scrollbar.h>
   65 #elif defined(HAVE_LIB_NEXTAW)
   66 #include <X11/neXtaw/Scrollbar.h>
   67 #elif defined(HAVE_LIB_XAWPLUS)
   68 #include <X11/XawPlus/Scrollbar.h>
   69 #endif
   70 
   71 #if defined(HAVE_XKBQUERYEXTENSION)
   72 #include <X11/extensions/XKB.h>
   73 #include <X11/XKBlib.h>
   74 #endif
   75 
   76 #include <data.h>
   77 #include <error.h>
   78 #include <menu.h>
   79 #include <xstrings.h>
   80 
   81 /*
   82  * The scrollbar's border overlaps the border of the vt100 window.  If there
   83  * is no border for the vt100, there can be no border for the scrollbar.
   84  */
   85 #define SCROLLBAR_BORDER(xw) (TScreenOf(xw)->scrollBarBorder)
   86 #if OPT_TOOLBAR
   87 #define ScrollBarBorder(xw) (BorderWidth(xw) ? SCROLLBAR_BORDER(xw) : 0)
   88 #else
   89 #define ScrollBarBorder(xw) SCROLLBAR_BORDER(xw)
   90 #endif
   91 
   92 /* Event handlers */
   93 
   94 static void ScrollTextTo PROTO_XT_CALLBACK_ARGS;
   95 static void ScrollTextUpDownBy PROTO_XT_CALLBACK_ARGS;
   96 
   97 /* Resize the text window for a terminal screen, modifying the
   98  * appropriate WM_SIZE_HINTS and taking advantage of bit gravity.
   99  */
  100 void
  101 DoResizeScreen(XtermWidget xw)
  102 {
  103     TScreen *screen = TScreenOf(xw);
  104 
  105     int border = 2 * screen->border;
  106     int min_wide = border + screen->fullVwin.sb_info.width;
  107     int min_high = border;
  108     XtGeometryResult geomreqresult;
  109     Dimension reqWidth, reqHeight, repWidth, repHeight;
  110 #ifndef NO_ACTIVE_ICON
  111     VTwin *saveWin = WhichVWin(screen);
  112 
  113     /* all units here want to be in the normal font units */
  114     WhichVWin(screen) = &screen->fullVwin;
  115 #endif /* NO_ACTIVE_ICON */
  116 
  117     /*
  118      * I'm going to try to explain, as I understand it, why we
  119      * have to do XGetWMNormalHints and XSetWMNormalHints here,
  120      * although I can't guarantee that I've got it right.
  121      *
  122      * In a correctly written toolkit program, the Shell widget
  123      * parses the user supplied geometry argument.  However,
  124      * because of the way xterm does things, the VT100 widget does
  125      * the parsing of the geometry option, not the Shell widget.
  126      * The result of this is that the Shell widget doesn't set the
  127      * correct window manager hints, and doesn't know that the
  128      * user has specified a geometry.
  129      *
  130      * The XtVaSetValues call below tells the Shell widget to
  131      * change its hints.  However, since it's confused about the
  132      * hints to begin with, it doesn't get them all right when it
  133      * does the SetValues -- it undoes some of what the VT100
  134      * widget did when it originally set the hints.
  135      *
  136      * To fix this, we do the following:
  137      *
  138      * 1. Get the sizehints directly from the window, going around
  139      *    the (confused) shell widget.
  140      * 2. Call XtVaSetValues to let the shell widget know which
  141      *    hints have changed.  Note that this may not even be
  142      *    necessary, since we're going to right ahead after that
  143      *    and set the hints ourselves, but it's good to put it
  144      *    here anyway, so that when we finally do fix the code so
  145      *    that the Shell does the right thing with hints, we
  146      *    already have the XtVaSetValues in place.
  147      * 3. We set the sizehints directly, this fixing up whatever
  148      *    damage was done by the Shell widget during the
  149      *    XtVaSetValues.
  150      *
  151      * Gross, huh?
  152      *
  153      * The correct fix is to redo VTRealize, VTInitialize and
  154      * VTSetValues so that font processing happens early enough to
  155      * give back responsibility for the size hints to the Shell.
  156      *
  157      * Someday, we hope to have time to do this.  Someday, we hope
  158      * to have time to completely rewrite xterm.
  159      */
  160 
  161     TRACE(("DoResizeScreen\n"));
  162 
  163 #if 1               /* ndef nothack */
  164     /*
  165      * NOTE: the hints and the XtVaSetValues() must match.
  166      */
  167     TRACE(("%s@%d -- ", __FILE__, __LINE__));
  168     TRACE_WM_HINTS(xw);
  169     getXtermSizeHints(xw);
  170 
  171     xtermSizeHints(xw, ScrollbarWidth(screen));
  172 
  173     /* These are obsolete, but old clients may use them */
  174     xw->hints.width = MaxCols(screen) * FontWidth(screen) + xw->hints.min_width;
  175     xw->hints.height = MaxRows(screen) * FontHeight(screen) + xw->hints.min_height;
  176 #if OPT_MAXIMIZE
  177     /* assure single-increment resize for fullscreen */
  178     if (xw->work.ewmh[0].mode) {
  179     xw->hints.width_inc = 1;
  180     xw->hints.height_inc = 1;
  181     }
  182 #endif /* OPT_MAXIMIZE */
  183 #endif
  184 
  185     XSetWMNormalHints(screen->display, VShellWindow(xw), &xw->hints);
  186 
  187     reqWidth = (Dimension) (MaxCols(screen) * FontWidth(screen) + min_wide);
  188     reqHeight = (Dimension) (MaxRows(screen) * FontHeight(screen) + min_high);
  189 
  190 #if OPT_MAXIMIZE
  191     /* compensate for fullscreen mode */
  192     if (xw->work.ewmh[0].mode) {
  193     Screen *xscreen = DefaultScreenOfDisplay(xw->screen.display);
  194     reqWidth = (Dimension) WidthOfScreen(xscreen);
  195     reqHeight = (Dimension) HeightOfScreen(xscreen);
  196     ScreenResize(xw, reqWidth, reqHeight, &xw->flags);
  197     }
  198 #endif /* OPT_MAXIMIZE */
  199 
  200     TRACE(("...requesting screensize chars %dx%d, pixels %dx%d\n",
  201        MaxRows(screen),
  202        MaxCols(screen),
  203        reqHeight, reqWidth));
  204 
  205     geomreqresult = REQ_RESIZE((Widget) xw, reqWidth, reqHeight,
  206                    &repWidth, &repHeight);
  207 
  208     if (geomreqresult == XtGeometryAlmost) {
  209     TRACE(("...almost, retry screensize %dx%d\n", repHeight, repWidth));
  210     geomreqresult = REQ_RESIZE((Widget) xw, repWidth,
  211                    repHeight, NULL, NULL);
  212     }
  213 
  214     if (geomreqresult != XtGeometryYes) {
  215     /* The resize wasn't successful, so we might need to adjust
  216        our idea of how large the screen is. */
  217     TRACE(("...still no (%d) - resize the core-class\n", geomreqresult));
  218     xw->core.widget_class->core_class.resize((Widget) xw);
  219     }
  220 #if 1               /* ndef nothack */
  221     /*
  222      * XtMakeResizeRequest() has the undesirable side-effect of clearing
  223      * the window manager's hints, even on a failed request.  This would
  224      * presumably be fixed if the shell did its own work.
  225      */
  226     if (xw->hints.flags
  227     && repHeight
  228     && repWidth) {
  229     xw->hints.height = repHeight;
  230     xw->hints.width = repWidth;
  231     TRACE_HINTS(&xw->hints);
  232     XSetWMNormalHints(screen->display, VShellWindow(xw), &xw->hints);
  233     }
  234 #endif
  235     XSync(screen->display, False);  /* synchronize */
  236     if (xtermAppPending())
  237     xevents(xw);
  238 
  239 #ifndef NO_ACTIVE_ICON
  240     WhichVWin(screen) = saveWin;
  241 #endif /* NO_ACTIVE_ICON */
  242 }
  243 
  244 static Widget
  245 CreateScrollBar(XtermWidget xw, int x, int y, int height)
  246 {
  247     Widget result;
  248     Arg args[6];
  249 
  250     XtSetArg(args[0], XtNx, x);
  251     XtSetArg(args[1], XtNy, y);
  252     XtSetArg(args[2], XtNheight, height);
  253     XtSetArg(args[3], XtNreverseVideo, xw->misc.re_verse);
  254     XtSetArg(args[4], XtNorientation, XtorientVertical);
  255     XtSetArg(args[5], XtNborderWidth, ScrollBarBorder(xw));
  256 
  257     result = XtCreateWidget("scrollbar", scrollbarWidgetClass,
  258                 (Widget) xw, args, XtNumber(args));
  259     XtAddCallback(result, XtNscrollProc, ScrollTextUpDownBy, 0);
  260     XtAddCallback(result, XtNjumpProc, ScrollTextTo, 0);
  261     return (result);
  262 }
  263 
  264 void
  265 ScrollBarReverseVideo(Widget scrollWidget)
  266 {
  267     XtermWidget xw = getXtermWidget(scrollWidget);
  268 
  269     if (xw != 0) {
  270     SbInfo *sb = &(TScreenOf(xw)->fullVwin.sb_info);
  271     Arg args[4];
  272     Cardinal nargs = XtNumber(args);
  273 
  274     /*
  275      * Remember the scrollbar's original colors.
  276      */
  277     if (sb->rv_cached == False) {
  278         XtSetArg(args[0], XtNbackground, &(sb->bg));
  279         XtSetArg(args[1], XtNforeground, &(sb->fg));
  280         XtSetArg(args[2], XtNborderColor, &(sb->bdr));
  281         XtSetArg(args[3], XtNborderPixmap, &(sb->bdpix));
  282         XtGetValues(scrollWidget, args, nargs);
  283         sb->rv_cached = True;
  284         sb->rv_active = 0;
  285     }
  286 
  287     sb->rv_active = !(sb->rv_active);
  288     if (sb->rv_active) {
  289         XtSetArg(args[0], XtNbackground, sb->fg);
  290         XtSetArg(args[1], XtNforeground, sb->bg);
  291     } else {
  292         XtSetArg(args[0], XtNbackground, sb->bg);
  293         XtSetArg(args[1], XtNforeground, sb->fg);
  294     }
  295     nargs = 2;      /* don't set border_pixmap */
  296     if (sb->bdpix == XtUnspecifiedPixmap) {
  297         /* if not pixmap then pixel */
  298         if (sb->rv_active) {
  299         /* keep border visible */
  300         XtSetArg(args[2], XtNborderColor, args[1].value);
  301         } else {
  302         XtSetArg(args[2], XtNborderColor, sb->bdr);
  303         }
  304         nargs = 3;
  305     }
  306     XtSetValues(scrollWidget, args, nargs);
  307     }
  308 }
  309 
  310 void
  311 ScrollBarDrawThumb(XtermWidget xw, int mode)
  312 {
  313     TScreen *screen = TScreenOf(xw);
  314 
  315     if (screen->scrollWidget != 0) {
  316     int thumbTop, thumbHeight, totalHeight;
  317 
  318 #if USE_DOUBLE_BUFFER
  319     if (resource.buffered) {
  320         if (mode == 1) {
  321         screen->buffered_sb++;
  322         return;
  323         } else if (mode == 2) {
  324         if (screen->buffered_sb == 0)
  325             return;
  326         }
  327     }
  328     screen->buffered_sb = 0;
  329 #else
  330     (void) mode;
  331 #endif
  332 
  333     thumbTop = ROW2INX(screen, screen->savedlines);
  334     thumbHeight = MaxRows(screen);
  335     totalHeight = thumbHeight + screen->savedlines;
  336 
  337     XawScrollbarSetThumb(screen->scrollWidget,
  338                  ((float) thumbTop) / (float) totalHeight,
  339                  ((float) thumbHeight) / (float) totalHeight);
  340     }
  341 }
  342 
  343 void
  344 ResizeScrollBar(XtermWidget xw)
  345 {
  346     TScreen *screen = TScreenOf(xw);
  347 
  348     if (screen->scrollWidget != 0) {
  349     int height = screen->fullVwin.height + screen->border * 2;
  350     int width = screen->scrollWidget->core.width;
  351     int ypos = -ScrollBarBorder(xw);
  352 #ifdef SCROLLBAR_RIGHT
  353     int xpos = ((xw->misc.useRight)
  354             ? (screen->fullVwin.fullwidth -
  355                screen->scrollWidget->core.width -
  356                BorderWidth(screen->scrollWidget))
  357             : -ScrollBarBorder(xw));
  358 #else
  359     int xpos = -ScrollBarBorder(xw);
  360 #endif
  361 
  362     TRACE(("ResizeScrollBar at %d,%d %dx%d\n", ypos, xpos, height, width));
  363 
  364     XtConfigureWidget(
  365                  screen->scrollWidget,
  366                  (Position) xpos,
  367                  (Position) ypos,
  368                  (Dimension) width,
  369                  (Dimension) height,
  370                  BorderWidth(screen->scrollWidget));
  371     ScrollBarDrawThumb(xw, 1);
  372     }
  373 }
  374 
  375 void
  376 WindowScroll(XtermWidget xw, int top, Bool always)
  377 {
  378     TScreen *screen = TScreenOf(xw);
  379 
  380     (void) always;
  381 #if OPT_SCROLL_LOCK
  382     if (((screen->allowScrollLock && screen->scroll_lock)
  383      || (screen->autoScrollLock && top < 0))
  384     && !always) {
  385     if (screen->scroll_dirty) {
  386         screen->scroll_dirty = False;
  387         ScrnRefresh(xw, 0, 0,
  388             LastRowNumber(screen) + 1,
  389             MaxCols(screen), False);
  390     }
  391     } else
  392 #endif
  393     {
  394     int i;
  395 
  396     if (top < -screen->savedlines) {
  397         top = -screen->savedlines;
  398     } else if (top > 0) {
  399         top = 0;
  400     }
  401 
  402     if ((i = screen->topline - top) != 0) {
  403         int lines;
  404         int scrolltop, scrollheight, refreshtop;
  405 
  406         if (screen->cursor_state)
  407         HideCursor(xw);
  408         lines = i > 0 ? i : -i;
  409         if (lines > MaxRows(screen))
  410         lines = MaxRows(screen);
  411         scrollheight = screen->max_row - lines + 1;
  412         if (i > 0)
  413         refreshtop = scrolltop = 0;
  414         else {
  415         scrolltop = lines;
  416         refreshtop = scrollheight;
  417         }
  418         scrolling_copy_area(xw, scrolltop, scrollheight, -i);
  419         screen->topline = top;
  420 
  421         ScrollSelection(screen, i, True);
  422 
  423         xtermClear2(xw,
  424             OriginX(screen),
  425             OriginY(screen) + refreshtop * FontHeight(screen),
  426             (unsigned) Width(screen),
  427             (unsigned) (lines * FontHeight(screen)));
  428         ScrnRefresh(xw, refreshtop, 0, lines, MaxCols(screen), False);
  429 
  430 #if OPT_BLINK_CURS || OPT_BLINK_TEXT
  431         RestartBlinking(xw);
  432 #endif
  433     }
  434     }
  435     ScrollBarDrawThumb(xw, 1);
  436 }
  437 
  438 #ifdef SCROLLBAR_RIGHT
  439 /*
  440  * Adjust the scrollbar position if we're asked to turn on scrollbars for the
  441  * first time (or after resizing) after the xterm is already running.  That
  442  * makes the window grow after we've initially configured the scrollbar's
  443  * position.  (There must be a better way).
  444  */
  445 void
  446 updateRightScrollbar(XtermWidget xw)
  447 {
  448     TScreen *screen = TScreenOf(xw);
  449 
  450     if (xw->misc.useRight
  451     && screen->fullVwin.fullwidth < xw->core.width)
  452     XtVaSetValues(screen->scrollWidget,
  453               XtNx, screen->fullVwin.fullwidth - BorderWidth(screen->scrollWidget),
  454               (XtPointer) 0);
  455 }
  456 #endif
  457 
  458 void
  459 ScrollBarOn(XtermWidget xw, Bool init)
  460 {
  461     TScreen *screen = TScreenOf(xw);
  462 
  463     if (screen->fullVwin.sb_info.width || IsIcon(screen))
  464     return;
  465 
  466     TRACE(("ScrollBarOn(init %s)\n", BtoS(init)));
  467     if (init) {         /* then create it only */
  468     if (screen->scrollWidget == 0) {
  469         /* make it a dummy size and resize later */
  470         screen->scrollWidget = CreateScrollBar(xw,
  471                            -ScrollBarBorder(xw),
  472                            -ScrollBarBorder(xw),
  473                            5);
  474         if (screen->scrollWidget == NULL) {
  475         Bell(xw, XkbBI_MinorError, 0);
  476         }
  477     }
  478     } else if (!screen->scrollWidget || !XtIsRealized((Widget) xw)) {
  479     Bell(xw, XkbBI_MinorError, 0);
  480     Bell(xw, XkbBI_MinorError, 0);
  481     } else {
  482 
  483     ResizeScrollBar(xw);
  484     xtermAddInput(screen->scrollWidget);
  485     XtRealizeWidget(screen->scrollWidget);
  486     TRACE_TRANS("scrollbar", screen->scrollWidget);
  487 
  488     screen->fullVwin.sb_info.rv_cached = False;
  489 
  490     screen->fullVwin.sb_info.width = (screen->scrollWidget->core.width
  491                       + BorderWidth(screen->scrollWidget));
  492 
  493     TRACE(("setting scrollbar width %d = %d + %d\n",
  494            screen->fullVwin.sb_info.width,
  495            screen->scrollWidget->core.width,
  496            BorderWidth(screen->scrollWidget)));
  497 
  498     ScrollBarDrawThumb(xw, 1);
  499     DoResizeScreen(xw);
  500 
  501 #ifdef SCROLLBAR_RIGHT
  502     updateRightScrollbar(xw);
  503 #endif
  504 
  505     XtMapWidget(screen->scrollWidget);
  506     update_scrollbar();
  507     if (screen->visbuf) {
  508         xtermClear(xw);
  509         Redraw();
  510     }
  511     }
  512 }
  513 
  514 void
  515 ScrollBarOff(XtermWidget xw)
  516 {
  517     TScreen *screen = TScreenOf(xw);
  518 
  519     if (!screen->fullVwin.sb_info.width || IsIcon(screen))
  520     return;
  521 
  522     TRACE(("ScrollBarOff\n"));
  523     if (XtIsRealized((Widget) xw)) {
  524     XtUnmapWidget(screen->scrollWidget);
  525     screen->fullVwin.sb_info.width = 0;
  526     DoResizeScreen(xw);
  527     update_scrollbar();
  528     if (screen->visbuf) {
  529         xtermClear(xw);
  530         Redraw();
  531     }
  532     } else {
  533     Bell(xw, XkbBI_MinorError, 0);
  534     }
  535 }
  536 
  537 /*
  538  * Toggle the visibility of the scrollbars.
  539  */
  540 void
  541 ToggleScrollBar(XtermWidget xw)
  542 {
  543     TScreen *screen = TScreenOf(xw);
  544 
  545     if (IsIcon(screen)) {
  546     Bell(xw, XkbBI_MinorError, 0);
  547     } else {
  548     TRACE(("ToggleScrollBar" TRACE_L "\n"));
  549     if (screen->fullVwin.sb_info.width) {
  550         ScrollBarOff(xw);
  551     } else {
  552         ScrollBarOn(xw, False);
  553     }
  554     update_scrollbar();
  555     TRACE((TRACE_R " ToggleScrollBar\n"));
  556     }
  557 }
  558 
  559 /*ARGSUSED*/
  560 static void
  561 ScrollTextTo(
  562         Widget scrollbarWidget,
  563         XtPointer client_data GCC_UNUSED,
  564         XtPointer call_data)
  565 {
  566     XtermWidget xw = getXtermWidget(scrollbarWidget);
  567 
  568     if (xw != 0) {
  569     float *topPercent = (float *) call_data;
  570     TScreen *screen = TScreenOf(xw);
  571     int thumbTop;       /* relative to first saved line */
  572     int newTopLine;
  573 
  574     /*
  575      * screen->savedlines : Number of offscreen text lines,
  576      * MaxRows(screen)    : Number of onscreen  text lines,
  577      */
  578     thumbTop = (int) (*topPercent
  579               * (float) (screen->savedlines + MaxRows(screen)));
  580     newTopLine = thumbTop - screen->savedlines;
  581     WindowScroll(xw, newTopLine, True);
  582     }
  583 }
  584 
  585 /*ARGSUSED*/
  586 static void
  587 ScrollTextUpDownBy(
  588               Widget scrollbarWidget,
  589               XtPointer client_data GCC_UNUSED,
  590               XtPointer call_data)
  591 {
  592     XtermWidget xw = getXtermWidget(scrollbarWidget);
  593 
  594     if (xw != 0) {
  595     long pixels = (long) call_data;
  596 
  597     TScreen *screen = TScreenOf(xw);
  598     int rowOnScreen, newTopLine;
  599 
  600     rowOnScreen = (int) (pixels / FontHeight(screen));
  601     if (rowOnScreen == 0) {
  602         if (pixels < 0)
  603         rowOnScreen = -1;
  604         else if (pixels > 0)
  605         rowOnScreen = 1;
  606     }
  607     newTopLine = ROW2INX(screen, rowOnScreen);
  608     WindowScroll(xw, newTopLine, True);
  609     }
  610 }
  611 
  612 /*
  613  * assume that b is alphabetic and allow plural
  614  */
  615 static int
  616 CompareWidths(const char *a, const char *b, int *modifier)
  617 {
  618     int result;
  619     char ca, cb;
  620 
  621     *modifier = 0;
  622     if (!a || !b)
  623     return 0;
  624 
  625     for (;;) {
  626     ca = x_toupper(*a);
  627     cb = x_toupper(*b);
  628     if (ca != cb || ca == '\0')
  629         break;      /* if not eq else both nul */
  630     a++;
  631     b++;
  632     }
  633     if (cb != '\0')
  634     return 0;
  635 
  636     if (ca == 'S')
  637     ca = *++a;
  638 
  639     switch (ca) {
  640     case '+':
  641     case '-':
  642     *modifier = (ca == '-' ? -1 : 1) * atoi(a + 1);
  643     result = 1;
  644     break;
  645 
  646     case '\0':
  647     result = 1;
  648     break;
  649 
  650     default:
  651     result = 0;
  652     break;
  653     }
  654     return result;
  655 }
  656 
  657 static long
  658 params_to_pixels(TScreen *screen, String *params, Cardinal n)
  659 {
  660     int mult = 1;
  661     const char *s;
  662     int modifier;
  663 
  664     switch (n > 2 ? 2 : n) {
  665     case 2:
  666     s = params[1];
  667     if (CompareWidths(s, "PAGE", &modifier)) {
  668         mult = (MaxRows(screen) + modifier) * FontHeight(screen);
  669     } else if (CompareWidths(s, "HALFPAGE", &modifier)) {
  670         mult = ((MaxRows(screen) + modifier) * FontHeight(screen)) / 2;
  671     } else if (CompareWidths(s, "PIXEL", &modifier)) {
  672         mult = 1;
  673     } else {
  674         /* else assume that it is Line */
  675         mult = FontHeight(screen);
  676     }
  677     mult *= atoi(params[0]);
  678     TRACE(("params_to_pixels(%s,%s) = %d\n", params[0], params[1], mult));
  679     break;
  680     case 1:
  681     mult = atoi(params[0]) * FontHeight(screen);    /* lines */
  682     TRACE(("params_to_pixels(%s) = %d\n", params[0], mult));
  683     break;
  684     default:
  685     mult = screen->scrolllines * FontHeight(screen);
  686     TRACE(("params_to_pixels() = %d\n", mult));
  687     break;
  688     }
  689     return mult;
  690 }
  691 
  692 static long
  693 AmountToScroll(Widget w, String *params, Cardinal nparams)
  694 {
  695     long result = 0;
  696     XtermWidget xw;
  697 
  698     if ((xw = getXtermWidget(w)) != 0) {
  699     TScreen *screen = TScreenOf(xw);
  700     if (nparams <= 2
  701         || screen->send_mouse_pos == MOUSE_OFF) {
  702         result = params_to_pixels(screen, params, nparams);
  703     }
  704     }
  705     return result;
  706 }
  707 
  708 static void
  709 AlternateScroll(Widget w, long amount)
  710 {
  711     XtermWidget xw;
  712     TScreen *screen;
  713 
  714     if ((xw = getXtermWidget(w)) != 0 &&
  715     (screen = TScreenOf(xw)) != 0 &&
  716     screen->alternateScroll && screen->whichBuf) {
  717     ANSI reply;
  718 
  719     amount /= FontHeight(screen);
  720     memset(&reply, 0, sizeof(reply));
  721     reply.a_type = ((xw->keyboard.flags & MODE_DECCKM)
  722             ? ANSI_SS3
  723             : ANSI_CSI);
  724     if (amount > 0) {
  725         reply.a_final = 'B';
  726     } else {
  727         amount = -amount;
  728         reply.a_final = 'A';
  729     }
  730     while (amount-- > 0) {
  731         unparseseq(xw, &reply);
  732     }
  733     } else {
  734     ScrollTextUpDownBy(w, (XtPointer) 0, (XtPointer) amount);
  735     }
  736 }
  737 
  738 /*ARGSUSED*/
  739 void
  740 HandleScrollTo(
  741           Widget w,
  742           XEvent *event GCC_UNUSED,
  743           String *params,
  744           Cardinal *nparams)
  745 {
  746     XtermWidget xw;
  747     TScreen *screen;
  748 
  749     if ((xw = getXtermWidget(w)) != 0 &&
  750     (screen = TScreenOf(xw)) != 0 &&
  751     *nparams > 0) {
  752     long amount;
  753     int value;
  754     int to_top = (screen->topline - screen->savedlines);
  755     if (!x_strcasecmp(params[0], "begin")) {
  756         amount = to_top * FontHeight(screen);
  757     } else if (!x_strcasecmp(params[0], "end")) {
  758         amount = -to_top * FontHeight(screen);
  759     } else if ((value = atoi(params[0])) >= 0) {
  760         amount = (value + to_top) * FontHeight(screen);
  761     } else {
  762         amount = 0;
  763     }
  764     AlternateScroll(w, amount);
  765     }
  766 }
  767 
  768 /*ARGSUSED*/
  769 void
  770 HandleScrollForward(
  771                Widget xw,
  772                XEvent *event GCC_UNUSED,
  773                String *params,
  774                Cardinal *nparams)
  775 {
  776     long amount;
  777 
  778     if ((amount = AmountToScroll(xw, params, *nparams)) != 0) {
  779     AlternateScroll(xw, amount);
  780     }
  781 }
  782 
  783 /*ARGSUSED*/
  784 void
  785 HandleScrollBack(
  786             Widget xw,
  787             XEvent *event GCC_UNUSED,
  788             String *params,
  789             Cardinal *nparams)
  790 {
  791     long amount;
  792 
  793     if ((amount = -AmountToScroll(xw, params, *nparams)) != 0) {
  794     AlternateScroll(xw, amount);
  795     }
  796 }
  797 
  798 #if OPT_SCROLL_LOCK
  799 #define SCROLL_LOCK_LED 3
  800 
  801 #ifdef HAVE_XKBQUERYEXTENSION
  802 /*
  803  * Check for Xkb on client and server.
  804  */
  805 static int
  806 have_xkb(Display *dpy)
  807 {
  808     static int initialized = -1;
  809 
  810     if (initialized < 0) {
  811     int xkbmajor = XkbMajorVersion;
  812     int xkbminor = XkbMinorVersion;
  813     int xkbopcode, xkbevent, xkberror;
  814 
  815     initialized = 0;
  816     if (XkbLibraryVersion(&xkbmajor, &xkbminor)
  817         && XkbQueryExtension(dpy,
  818                  &xkbopcode,
  819                  &xkbevent,
  820                  &xkberror,
  821                  &xkbmajor,
  822                  &xkbminor)) {
  823         TRACE(("we have Xkb\n"));
  824         initialized = 1;
  825 #if OPT_TRACE
  826         {
  827         XkbDescPtr xkb;
  828         unsigned int mask;
  829 
  830         xkb = XkbGetKeyboard(dpy, XkbAllComponentsMask, XkbUseCoreKbd);
  831         if (xkb != NULL) {
  832             int n;
  833 
  834             TRACE(("XkbGetKeyboard ok\n"));
  835             for (n = 0; n < XkbNumVirtualMods; ++n) {
  836             if (xkb->names->vmods[n] != 0) {
  837                 char *modStr = XGetAtomName(xkb->dpy,
  838                             xkb->names->vmods[n]);
  839                 if (modStr != 0) {
  840                 XkbVirtualModsToReal(xkb,
  841                              (unsigned) (1 << n),
  842                              &mask);
  843                 TRACE(("  name[%d] %s (%#x)\n", n, modStr, mask));
  844                 XFree(modStr);
  845                 }
  846             }
  847             }
  848             XkbFreeKeyboard(xkb, 0, True);
  849         }
  850         }
  851 #endif
  852     }
  853     }
  854     return initialized;
  855 }
  856 
  857 static Boolean
  858 getXkbLED(Display *dpy, const char *name, Boolean *result)
  859 {
  860     Atom my_atom;
  861     Boolean success = False;
  862     Bool state;
  863 
  864     if (have_xkb(dpy)) {
  865     my_atom = XInternAtom(dpy, name, False);
  866     if ((my_atom != None) &&
  867         XkbGetNamedIndicator(dpy, my_atom, NULL, &state, NULL, NULL)) {
  868         *result = (Boolean) state;
  869         success = True;
  870     }
  871     }
  872 
  873     return success;
  874 }
  875 
  876 /*
  877  * Use Xkb if we have it (still unreliable, but slightly better than hardcoded).
  878  */
  879 static Boolean
  880 showXkbLED(Display *dpy, const char *name, Bool enable)
  881 {
  882     Atom my_atom;
  883     Boolean result = False;
  884 
  885     if (have_xkb(dpy)) {
  886     my_atom = XInternAtom(dpy, name, False);
  887     if ((my_atom != None) &&
  888         XkbGetNamedIndicator(dpy, my_atom, NULL, NULL, NULL, NULL) &&
  889         XkbSetNamedIndicator(dpy, my_atom, True, enable, False, NULL)) {
  890         result = True;
  891     }
  892     }
  893 
  894     return result;
  895 }
  896 #endif
  897 
  898 /*
  899  * xlsatoms agrees with this list.  However Num/Caps lock are generally
  900  * unusable due to special treatment in X.  They are used here for
  901  * completeness.
  902  */
  903 static const char *led_table[] =
  904 {
  905     "Num Lock",
  906     "Caps Lock",
  907     "Scroll Lock"
  908 };
  909 
  910 static Boolean
  911 xtermGetLED(TScreen *screen, Cardinal led_number)
  912 {
  913     Display *dpy = screen->display;
  914     Boolean result = False;
  915 
  916 #ifdef HAVE_XKBQUERYEXTENSION
  917     if (!getXkbLED(dpy, led_table[led_number - 1], &result))
  918 #endif
  919     {
  920     XKeyboardState state;
  921     unsigned long my_bit = (unsigned long) (1 << (led_number - 1));
  922 
  923     XGetKeyboardControl(dpy, &state);
  924 
  925     result = (Boolean) ((state.led_mask & my_bit) != 0);
  926     }
  927 
  928     TRACE(("xtermGetLED %d:%s\n", led_number, BtoS(result)));
  929     return result;
  930 }
  931 
  932 /*
  933  * Display the given LED, preferably independent of keyboard state.
  934  */
  935 void
  936 xtermShowLED(TScreen *screen, Cardinal led_number, Bool enable)
  937 {
  938     TRACE(("xtermShowLED %d:%s\n", led_number, BtoS(enable)));
  939     if ((led_number >= 1) && (led_number <= XtNumber(led_table))) {
  940     Display *dpy = screen->display;
  941 
  942 #ifdef HAVE_XKBQUERYEXTENSION
  943     if (!showXkbLED(dpy, led_table[led_number - 1], enable))
  944 #endif
  945     {
  946         XKeyboardState state;
  947         XKeyboardControl values;
  948         unsigned long use_mask;
  949         unsigned long my_bit = (unsigned long) (1 << (led_number - 1));
  950 
  951         XGetKeyboardControl(dpy, &state);
  952         use_mask = state.led_mask;
  953         if (enable) {
  954         use_mask |= my_bit;
  955         } else {
  956         use_mask &= ~my_bit;
  957         }
  958 
  959         if (state.led_mask != use_mask) {
  960         values.led = (int) led_number;
  961         values.led_mode = enable;
  962         XChangeKeyboardControl(dpy, KBLed | KBLedMode, &values);
  963         }
  964     }
  965     }
  966 }
  967 
  968 void
  969 xtermClearLEDs(TScreen *screen)
  970 {
  971     Display *dpy = screen->display;
  972     XKeyboardControl values;
  973 
  974     TRACE(("xtermClearLEDs\n"));
  975 #ifdef HAVE_XKBQUERYEXTENSION
  976     ShowScrollLock(screen, False);
  977 #endif
  978     memset(&values, 0, sizeof(values));
  979     XChangeKeyboardControl(dpy, KBLedMode, &values);
  980 }
  981 
  982 void
  983 ShowScrollLock(TScreen *screen, Bool enable)
  984 {
  985     xtermShowLED(screen, SCROLL_LOCK_LED, enable);
  986 }
  987 
  988 void
  989 GetScrollLock(TScreen *screen)
  990 {
  991     if (screen->allowScrollLock)
  992     screen->scroll_lock = xtermGetLED(screen, SCROLL_LOCK_LED);
  993 }
  994 
  995 void
  996 SetScrollLock(TScreen *screen, Bool enable)
  997 {
  998     if (screen->allowScrollLock) {
  999     if (screen->scroll_lock != enable) {
 1000         TRACE(("SetScrollLock %s\n", BtoS(enable)));
 1001         screen->scroll_lock = (Boolean) enable;
 1002         ShowScrollLock(screen, enable);
 1003     }
 1004     }
 1005 }
 1006 
 1007 /* ARGSUSED */
 1008 void
 1009 HandleScrollLock(Widget w,
 1010          XEvent *event GCC_UNUSED,
 1011          String *params,
 1012          Cardinal *param_count)
 1013 {
 1014     XtermWidget xw;
 1015 
 1016     if ((xw = getXtermWidget(w)) != 0) {
 1017     TScreen *screen = TScreenOf(xw);
 1018 
 1019     if (screen->allowScrollLock) {
 1020 
 1021         switch (decodeToggle(xw, params, *param_count)) {
 1022         case toggleOff:
 1023         SetScrollLock(screen, False);
 1024         break;
 1025         case toggleOn:
 1026         SetScrollLock(screen, True);
 1027         break;
 1028         case toggleAll:
 1029         SetScrollLock(screen, !screen->scroll_lock);
 1030         break;
 1031         }
 1032     }
 1033     }
 1034 }
 1035 #endif