summaryrefslogtreecommitdiff
path: root/scrollbar.c
diff options
context:
space:
mode:
Diffstat (limited to 'scrollbar.c')
-rw-r--r--scrollbar.c955
1 files changed, 955 insertions, 0 deletions
diff --git a/scrollbar.c b/scrollbar.c
new file mode 100644
index 0000000..12e8a4a
--- /dev/null
+++ b/scrollbar.c
@@ -0,0 +1,955 @@
+/* $XTermId: scrollbar.c,v 1.182 2011/09/03 12:13:42 tom Exp $ */
+
+/*
+ * Copyright 2000-2010,2011 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_NEXTAW)
+#include <X11/neXtaw/Scrollbar.h>
+#elif defined(HAVE_LIB_XAWPLUS)
+#include <X11/XawPlus/Scrollbar.h>
+#endif
+
+#if defined(HAVE_XKBQUERYEXTENSION) && defined(HAVE_X11_XKBLIB_H) && defined(HAVE_X11_EXTENSIONS_XKB_H)
+#include <X11/extensions/XKB.h>
+#include <X11/XKBlib.h>
+#else
+#undef HAVE_XKBQUERYEXTENSION
+#endif
+
+#include <data.h>
+#include <error.h>
+#include <menu.h>
+#include <xcharmouse.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 (screen->fullscreen) {
+ 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 (screen->fullscreen) {
+ 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();
+
+#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(Widget scrollWidget)
+{
+ XtermWidget xw = getXtermWidget(scrollWidget);
+
+ if (xw != 0) {
+ TScreen *screen = TScreenOf(xw);
+ int thumbTop, thumbHeight, totalHeight;
+
+ thumbTop = ROW2INX(screen, screen->savedlines);
+ thumbHeight = MaxRows(screen);
+ totalHeight = thumbHeight + screen->savedlines;
+
+ XawScrollbarSetThumb(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(screen->scrollWidget);
+ }
+}
+
+void
+WindowScroll(XtermWidget xw, int top, Bool always GCC_UNUSED)
+{
+ TScreen *screen = TScreenOf(xw);
+ int i, lines;
+ int scrolltop, scrollheight, refreshtop;
+
+#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
+ {
+ if (top < -screen->savedlines) {
+ top = -screen->savedlines;
+ } else if (top > 0) {
+ top = 0;
+ }
+
+ if ((i = screen->topline - top) != 0) {
+
+ 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);
+
+ XClearArea(
+ screen->display,
+ VWindow(screen),
+ OriginX(screen),
+ OriginY(screen) + refreshtop * FontHeight(screen),
+ (unsigned) Width(screen),
+ (unsigned) (lines * FontHeight(screen)),
+ False);
+ ScrnRefresh(xw, refreshtop, 0, lines, MaxCols(screen), False);
+
+#if OPT_BLINK_CURS || OPT_BLINK_TEXT
+ RestartBlinking(screen);
+#endif
+ }
+ }
+ ScrollBarDrawThumb(screen->scrollWidget);
+}
+
+#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(screen->scrollWidget);
+ 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]);
+ break;
+ case 1:
+ mult = atoi(params[0]) * FontHeight(screen); /* lines */
+ break;
+ default:
+ mult = screen->scrolllines * FontHeight(screen);
+ 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;
+}
+
+/*ARGSUSED*/
+void
+HandleScrollForward(
+ Widget xw,
+ XEvent * event GCC_UNUSED,
+ String * params,
+ Cardinal *nparams)
+{
+ long amount;
+
+ if ((amount = AmountToScroll(xw, params, *nparams)) != 0) {
+ ScrollTextUpDownBy(xw, (XtPointer) 0, (XtPointer) amount);
+ }
+}
+
+/*ARGSUSED*/
+void
+HandleScrollBack(
+ Widget xw,
+ XEvent * event GCC_UNUSED,
+ String * params,
+ Cardinal *nparams)
+{
+ long amount;
+
+ if ((amount = -AmountToScroll(xw, params, *nparams)) != 0) {
+ ScrollTextUpDownBy(xw, (XtPointer) 0, (XtPointer) 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;
+ int n;
+ char *modStr;
+
+ xkb = XkbGetKeyboard(dpy, XkbAllComponentsMask, XkbUseCoreKbd);
+ if (xkb != NULL) {
+
+ TRACE(("XkbGetKeyboard ok\n"));
+ for (n = 0; n < XkbNumVirtualMods; ++n) {
+ if (xkb->names->vmods[n] != 0) {
+ 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) {
+ 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) {
+ /*
+ * The default action (used with KeyRelease event) is to cycle the
+ * state on/off.
+ */
+ if (*param_count == 0) {
+ SetScrollLock(screen, !screen->scroll_lock);
+ TRACE(("HandleScrollLock ->%d\n",
+ screen->scroll_lock));
+ } else {
+ SetScrollLock(screen, atoi(params[0]));
+ TRACE(("HandleScrollLock(%s) ->%d\n",
+ params[0],
+ screen->scroll_lock));
+ }
+ }
+ }
+}
+#endif