"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