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