Add GNU LGPL headers to all .c .C and .h files
[oweals/cde.git] / cde / lib / DtTerm / TermPrim / TermPrimScroll.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 #ifndef lint
24 #ifdef  VERBOSE_REV_INFO
25 static char rcs_id[] = "$XConsortium: TermPrimScroll.c /main/1 1996/04/21 19:19:17 drk $";
26 #endif  /* VERBOSE_REV_INFO */
27 #endif  /* lint */
28
29 /*                                                                      *
30  * (c) Copyright 1993, 1994 Hewlett-Packard Company                     *
31  * (c) Copyright 1993, 1994 International Business Machines Corp.       *
32  * (c) Copyright 1993, 1994 Sun Microsystems, Inc.                      *
33  * (c) Copyright 1993, 1994 Novell, Inc.                                *
34  */
35
36 #include "TermHeader.h"
37 #include "TermPrimDebug.h"
38 #include "TermPrimP.h"
39 #include "TermPrimI.h"
40 #include "TermPrimData.h"
41 #include "TermPrimBuffer.h"
42
43 static void
44 waitOnCopyArea(Widget w)
45 {
46     DtTermPrimitiveWidget tw = (DtTermPrimitiveWidget) w;
47     struct termData *tpd = tw->term.tpd;
48     XEvent event;
49     XEvent *ev = &event;
50
51     while (tpd->scrollInProgress) {
52         (void) XWindowEvent(XtDisplay(w), XtWindow(w), ExposureMask, ev);
53         switch (ev->type) {
54         case Expose:
55             Debug('e', fprintf(stderr,
56                     ">>_waitOnCopyArea() Expose event received\n"));
57
58             /* refresh the border... */
59             (void) _DtTermPrimDrawShadow(w);
60
61             /* just refresh the exposed area...
62              */
63             Debug('e', fprintf(stderr,
64                     ">>_waitOnCopyArea() exposing Expose area\n"));
65             Debug('e', fprintf(stderr,
66                     ">>                  x=%d  y=%d  width=%d  height=%d\n",
67                     ev->xexpose.x, ev->xexpose.y,
68                     ev->xexpose.width, ev->xexpose.height));
69             (void) _DtTermPrimExposeText(w, ev->xexpose.x, ev->xexpose.y,
70                     ev->xexpose.width, ev->xexpose.height, True);
71             break;
72
73         case GraphicsExpose:
74             /* refresh the exposed area and, if this is the last graphics
75              * exposure, we are done and can fall through to the noExpose
76              * code...
77              */
78             Debug('e', fprintf(stderr,
79                     ">>_DtTermPrimScrollWait() GraphicsExpose event received, count=%d\n",
80                     ev->xgraphicsexpose.count));
81             Debug('e', fprintf(stderr,
82                     ">>_waitOnCopyArea() exposing GraphicsExpose area\n"));
83             Debug('e', fprintf(stderr,
84                     ">>                  x=%d  y=%d  width=%d  height=%d\n",
85                     ev->xgraphicsexpose.x, ev->xgraphicsexpose.y,
86                     ev->xgraphicsexpose.width, ev->xgraphicsexpose.height));
87             (void) _DtTermPrimExposeText(w,
88                     ev->xgraphicsexpose.x, ev->xgraphicsexpose.y,
89                     ev->xgraphicsexpose.width, ev->xgraphicsexpose.height,
90                     False);
91             if (ev->xgraphicsexpose.count > 0) {
92                 /* more to come... */
93                 break;
94             }
95
96         case NoExpose:
97             /* we are done... */
98             Debug('e', fprintf(stderr,
99                     ">>_waitOnCopyArea() NoExpose event received\n"));
100             tpd->scrollInProgress = False;
101             break;
102         }
103     }
104 }
105
106 void
107 _DtTermPrimScrollWait(Widget w)
108 {
109     DtTermPrimitiveWidget tw = (DtTermPrimitiveWidget) w;
110     struct termData *tpd = tw->term.tpd;
111     int i;
112     int exposeY;
113     int exposeHeight;
114     int scrolledLines;
115
116     Debug('s', fprintf(stderr, ">>_DtTermPrimScrollWait() starting\n"));
117     Debug('s', fprintf(stderr,
118             ">>_DtTermPrimScrollWait() scrollLines=%d, scrollsPending=%d\n",
119             tpd->scroll.jump.scrollLines, tpd->scroll.jump.scrollsPending));
120
121     /* make sure the cursor is off... */
122     (void) _DtTermPrimCursorOff(w);
123
124     if (tpd->scroll.jump.scrollLines != 0) {
125         /* flush so that we can be sure the output was visible before we
126          * scroll it off...
127          */
128         (void) XFlush(XtDisplay(w));
129
130         /* figure out the height of the copy area... */
131         if (tpd->scroll.jump.scrollLines > 0) {
132             /* scrolling memory up... */
133             Debug('s', fprintf(stderr,
134                     ">>_DtTermPrimScrollWait() up,  scrollLines=%d  cellHeight=%d\n",
135                     tpd->scroll.jump.scrollLines, tpd->cellHeight));
136             if (tpd->scroll.jump.scrollLines >=
137                     tpd->scrollBottomRow - tpd->scrollTopRow + 1) {
138                 /* a full screen or more -- we can avoid the copy area... */
139                 tpd->scrollHeight = 0;
140                 exposeY = tpd->offsetY + tpd->cellHeight *
141                         (tpd->scrollTopRow);
142                 exposeHeight = tpd->cellHeight *
143                         (tpd->scrollBottomRow - tpd->scrollTopRow + 1);
144                 Debug('s', fprintf(stderr,
145                         ">>_DtTermPrimScrollWait()  height=%d    expose Y=%d  height=%d\n",
146                         tpd->scrollHeight, exposeY, exposeHeight));
147             } else {
148                 tpd->scrollHeight = tpd->cellHeight *
149                         (tpd->scrollBottomRow - tpd->scrollTopRow + 1 -
150                         tpd->scroll.jump.scrollLines);
151                 tpd->scrollSrcY = tpd->offsetY + tpd->cellHeight *
152                         (tpd->scrollTopRow + tpd->scroll.jump.scrollLines);
153                 tpd->scrollDestY = tpd->offsetY + tpd->cellHeight *
154                         (tpd->scrollTopRow);
155                 exposeY = tpd->offsetY + tpd->cellHeight *
156                         (tpd->scrollBottomRow + 1 -
157                         tpd->scroll.jump.scrollLines);
158                 exposeHeight = tpd->cellHeight *
159                         (tpd->scroll.jump.scrollLines);
160                 Debug('s', fprintf(stderr,
161                         ">>_DtTermPrimScrollWait()  height=%d  SrcY=%d  DestY=%d    expose Y=%d  height=%d\n",
162                         tpd->scrollHeight, tpd->scrollSrcY, tpd->scrollDestY,
163                         exposeY, exposeHeight));
164             }
165         } else {
166             /* scrolling memory down... */
167             Debug('s', fprintf(stderr,
168                     ">>_DtTermPrimScrollWait() down,  scrollLines=%d  cellHeight=%d\n",
169                     tpd->scroll.jump.scrollLines, tpd->cellHeight));
170             if (tpd->scroll.jump.scrollLines <=
171                     -(tpd->scrollBottomRow - tpd->scrollTopRow + 1)) {
172                 /* a full screen or more -- we can avoid the copy area... */
173                 tpd->scrollHeight = 0;
174                 exposeY = tpd->offsetY + tpd->cellHeight *
175                         (tpd->scrollTopRow);
176                 exposeHeight = tpd->cellHeight *
177                         (tpd->scrollBottomRow - tpd->scrollTopRow + 1);
178                 Debug('s', fprintf(stderr,
179                         ">>_DtTermPrimScrollWait()  height=%d    expose Y=%d  height=%d\n",
180                         tpd->scrollHeight, exposeY, exposeHeight));
181             } else {
182                 /* remember: scrollLines is **NEGATIVE**...
183                  */
184                 tpd->scrollHeight = tpd->cellHeight * (tpd->scrollBottomRow -
185                         tpd->scrollTopRow + 1 - -tpd->scroll.jump.scrollLines);
186                 tpd->scrollSrcY = tpd->offsetY + tpd->cellHeight *
187                         (tpd->scrollTopRow);
188                 tpd->scrollDestY = tpd->offsetY + tpd->cellHeight *
189                         (tpd->scrollTopRow + -tpd->scroll.jump.scrollLines);
190                 exposeY = tpd->offsetY + tpd->cellHeight *
191                         (tpd->scrollTopRow);
192                 exposeHeight = tpd->cellHeight *
193                         (-tpd->scroll.jump.scrollLines);
194                 Debug('s', fprintf(stderr,
195                         ">>_DtTermPrimScrollWait()  height=%d  SrcY=%d  DestY=%d    expose Y=%d  height=%d\n",
196                         tpd->scrollHeight, tpd->scrollSrcY, tpd->scrollDestY,
197                         exposeY, exposeHeight));
198             }
199         }
200
201         /* we need to do a copy area... */
202         if (tpd->scrollHeight > 0) {
203             /* calculate scroll area... */
204             tpd->scrollWidth = tpd->cellWidth * tw->term.columns;
205             tpd->scrollSrcX = tpd->offsetX;
206             tpd->scrollDestX = tpd->offsetX;
207
208             (void) XCopyArea(XtDisplay(w),
209                                                 /* Display              */
210                         XtWindow(w),            /* Source               */
211                         XtWindow(w),            /* Destination          */
212                         tpd->renderGC.gc,       /* GC                   */
213                         tpd->scrollSrcX,                /* source X             */
214                         tpd->scrollSrcY,                /* source Y             */
215                         tpd->scrollWidth,       /* width                */
216                         tpd->scrollHeight,      /* height               */
217                         tpd->scrollDestX,       /* destination X        */
218                         tpd->scrollDestY);      /* destination Y        */
219             tpd->scrollInProgress = True;
220         }
221
222         /* expose the exposed area.  We need to expose the exposed area
223          * as well as any lines that have been changed.  While we really
224          * could just expose the exposed area and then go back and fill
225          * in any remaining lines that have the flag set, we will first
226          * fill in any lines above the expose area with their flag set,
227          * fill in the expose area, and then fill in any lines below
228          * the expose area with their flag set.  This fills things in
229          * from top to bottom which is much more pleasing visually.
230          */
231         Debug('s', fprintf(stderr,
232                 ">>_DtTermPrimScrollWait() exposing scroll area\n"));
233         Debug('s', fprintf(stderr,
234                 ">>             x=%d  y=%d  width=%d  height=%d\n",
235                 tpd->offsetX,
236                 exposeY,
237                 tpd->cellWidth * tw->term.columns,
238                 exposeHeight));
239
240         /* set scrollLines == 0, or renderText will not render them... */
241         scrolledLines = tpd->scroll.jump.scrollLines;
242         tpd->scroll.jump.scrollLines = 0;
243         /* clear the scrolled flag... */
244         tpd->scroll.jump.scrolled = False;
245
246         /* refresh any lines above the expose zone that have their
247          * scrollRefreshRows flag set...
248          */
249         for (i = 0; i < (exposeY - tpd->offsetX) / tpd->cellHeight; i++) {
250             if (tpd->scrollRefreshRows[i]) {
251                 (void) _DtTermPrimRefreshText(w, 0, i, tw->term.columns, i);
252             }
253         }
254         /* expose the refresh expose area... */
255         (void) _DtTermPrimExposeText(w,
256                 tpd->offsetX,
257                 exposeY,
258                 tpd->cellWidth * tw->term.columns,
259                 exposeHeight,
260                 False);
261
262         /* refresh any lines below the expose zone that have their
263          * scrollRefreshRows flag set...
264          */
265         for (i = (exposeY - tpd->offsetX + exposeHeight) / tpd->cellHeight;
266                 i < tw->term.rows; i++) {
267             if (tpd->scrollRefreshRows[i]) {
268                 (void) _DtTermPrimRefreshText(w, 0, i, tw->term.columns, i);
269             }
270         }
271
272         if (tpd->scrollHeight > 0)
273             tpd->scroll.jump.scrollsPending++;
274
275         (void) _DtTermPrimCursorUpdate(w);
276     } else {
277         /* scrollLines == 0...
278          */
279
280         /* flush so that we can be sure the output was visible before we
281          * scroll it off...
282          */
283         (void) XFlush(XtDisplay(w));
284
285         /* clear the scrolled flag... */
286         tpd->scroll.jump.scrolled = False;
287
288         /* refresh any lines below the expose zone that have their
289          * scrollRefreshRows flag set...
290          */
291
292         for (i = 0; i < tw->term.rows; i++) {
293             if (tpd->scrollRefreshRows[i]) {
294                 (void) _DtTermPrimRefreshText(w, 0, i, tw->term.columns, i);
295             }
296         }
297     }
298
299     while (tpd->scroll.jump.scrollsPending > 0) {
300         (void) waitOnCopyArea(w);
301         tpd->scroll.jump.scrollsPending--;
302     }
303
304     Debug('s', fprintf(stderr, ">>_DtTermPrimScrollWait() finished\n"));
305 }
306     
307 static void
308 doActualScroll(Widget w, int lines)
309 {
310     DtTermPrimitiveWidget tw = (DtTermPrimitiveWidget) w;
311     struct termData *tpd = tw->term.tpd;
312     int exposeY;
313     int exposeHeight;
314     int i;
315
316     /* make sure the cursor is off... */
317     (void) _DtTermPrimCursorOff(w);
318
319     /* figure out the height of the copy area... */
320     if (lines > 0) {
321         /* scrolling memory up... */
322         if (lines >= tpd->scrollBottomRow - tpd->scrollTopRow + 1) {
323             /* we are scrolling a full screen or more, so there is nothing
324              * to copy...
325              */
326             tpd->scrollHeight = 0;
327             exposeY = tpd->offsetY + tpd->cellHeight * tpd->scrollTopRow;
328             exposeHeight = tpd->cellHeight *
329                     (tpd->scrollBottomRow - tpd->scrollTopRow + 1);
330         } else {
331             tpd->scrollHeight = tpd->cellHeight *
332                     (tpd->scrollBottomRow - tpd->scrollTopRow + 1 - lines);
333             tpd->scrollSrcY = tpd->offsetY + tpd->cellHeight *
334                     (tpd->scrollTopRow + lines);
335             tpd->scrollDestY = tpd->offsetY + tpd->cellHeight *
336                     (tpd->scrollTopRow);
337
338             /* expose bottom lines lines... */
339             exposeY = tpd->offsetY + tpd->cellHeight *
340                     (tpd->scrollBottomRow + 1 - lines);
341             exposeHeight = tpd->cellHeight * lines;
342         }
343     } else {
344         /* scrolling memory down... */
345         /* lines are negative -- make positive... */
346         lines = -lines;
347         if (lines >= tw->term.rows) {
348             /* we are scrolling a full screen or more, so there is nothing
349              * to copy...
350              */
351             tpd->scrollHeight = 0;
352             exposeY = tpd->offsetY + tpd->cellHeight * tpd->scrollTopRow;
353             exposeHeight = tpd->cellHeight *
354                     (tpd->scrollBottomRow - tpd->scrollTopRow + 1);
355         } else {
356             tpd->scrollHeight = tpd->cellHeight *
357                     (tpd->scrollBottomRow - tpd->scrollTopRow + 1 - lines);
358             tpd->scrollSrcY = tpd->offsetY + tpd->cellHeight *
359                     (tpd->scrollTopRow);
360             tpd->scrollDestY = tpd->offsetY + tpd->cellHeight *
361                     (tpd->scrollTopRow + lines);
362
363             /* expose top lines lines... */
364             exposeY = tpd->offsetY + tpd->cellHeight *
365                     (tpd->scrollTopRow);
366             exposeHeight = tpd->cellHeight * lines;
367         }
368     }
369
370     /* scroll the display... */
371     if (tpd->scrollHeight > 0) {
372         /* calculate scroll area... */
373         tpd->scrollWidth = tpd->cellWidth * tw->term.columns;
374         tpd->scrollSrcX = tpd->offsetX;
375         tpd->scrollDestX = tpd->offsetX;
376
377         /* copy the area... */
378         XCopyArea(XtDisplay(w),                 /* Display              */
379                 XtWindow(w),                    /* Source               */
380                 XtWindow(w),                    /* Destination          */
381                 tpd->renderGC.gc,               /* GC                   */
382                 tpd->scrollSrcX,                        /* source X             */
383                 tpd->scrollSrcY,                        /* source Y             */
384                 tpd->scrollWidth,               /* width                */
385                 tpd->scrollHeight,              /* height               */
386                 tpd->scrollDestX,               /* destination X        */
387                 tpd->scrollDestY);              /* destination Y        */
388         tpd->scrollInProgress = True;
389     }
390
391     /* clear the old area... */
392     (void) XFillRectangle(XtDisplay(w),         /* Display              */
393             XtWindow(w),                        /* Drawable             */
394             tpd->clearGC.gc,                    /* GC                   */
395             tpd->offsetX,                       /* x                    */
396             exposeY,                            /* y                    */
397             tw->term.columns * tpd->cellWidth,
398                                                 /* width                */
399             exposeHeight);                      /* height               */
400
401     /* expose the old area... */
402     (void) _DtTermPrimExposeText(w,
403             tpd->offsetX,
404             exposeY,
405             tw->term.columns * tpd->cellWidth,
406             exposeHeight, False);
407
408     (void) _DtTermPrimCursorUpdate(w);
409 }
410
411 /**************************************************************************
412  *  Function:
413  *      _DtTermPrimScrollText(): scroll the terminal window
414  *
415  *  Parameters:
416  *      Widget w: widget to scroll
417  *      int lines: lines to scroll
418  *
419  *  Returns:
420  *      <nothing>
421  *
422  *  Notes:
423  *
424  *      This function will scroll the terminals window.  It supports
425  *      both jump scroll and non-jump scroll (single line at a time).
426  *      In jump scroll, the maximum number of lines that will be
427  *      scrolled at a time is the length of the display.  This insures
428  *      that all text will be displayed for at least an instant.
429  *
430  *      In jump scroll mode, scrolling is performed as follows:
431  *
432  *      - Scroll request is made:
433  *
434  *          + Number of lines to scroll is incremented.
435  *
436  *          + If the number of lines is < the length of the screen,
437  *            rendering is discontinued and the actual scroll request is
438  *            delayed until the the cursor is turned back on (i.e.,
439  *            select(2) says there is no more input on the pty).
440  *
441  *          + If the number of lines is >= the length of the screen, the
442  *            copy area is performed, and the world blocks on exposure
443  *            events on the window.  When a no-expose request, or the
444  *            last graphics expose request is received, any remaining
445  *            lines will be queue'ed up.
446  *
447  *      - The code that processes data coming over the pty determines
448  *        via select(2) that there is no data to be read.
449  *
450  *          + The copy area is performed, and the world blocks on
451  *            exposure events on the window.  When a no-expose request,
452  *            or the last graphics expose request is received, the
453  *            scroll is complete.
454  *
455  *
456  *      In non-jump scroll mode, the following actions are performed:
457  *
458  *      - If there is no scroll in progress, the copy area is performed.
459  *        Any additional input is processed as normal.
460  *
461  *      - When the generated no-expose or final graphics expose event is
462  *        processed, the pending scroll flag is cleared.
463  *
464  *      - If there is a scroll in progress, the new scroll is queued up.
465  *        Any remaining pty input will be queued and the pty will be
466  *        dropped from Xt input select.
467  *
468  *      - When the generated no-expose or final graphics expose event is
469  *        processed, the queued copy area will be performed.  Any queued
470  *        pty input will be processed and the pty will be added to the
471  *        Xt input select.
472  */
473
474 void
475 _DtTermPrimScrollText(Widget w, short lines)
476 {
477     DtTermPrimitiveWidget tw = (DtTermPrimitiveWidget) w;
478     struct termData *tpd = tw->term.tpd;
479     short oldTopRow;
480     short newTopRow;
481     int lines2;
482     int i;
483
484     /* figure out what our new top row will be.  It is limitied by the
485      * beginning of memory (i.e., we can't scroll above line 0) and the
486      * lastUsedRow (i.e., we can't scroll the lastUsedRow off the screen).
487      * we will then use this value to clip lines...
488      */
489     oldTopRow = tpd->topRow;
490     if (lines > 0) {
491         newTopRow = tpd->topRow + lines;
492         /* don't scroll past the lastUsedRow... */
493         if (newTopRow > (tpd->lastUsedRow - tpd->scrollLockTopRow)) {
494             newTopRow = tpd->lastUsedRow - tpd->scrollLockTopRow;
495         }
496     } else {
497         if (tpd->useHistoryBuffer) {
498             if ((tpd->topRow + tpd->lastUsedHistoryRow) >= -lines)
499                 newTopRow = tpd->topRow - -lines;
500             else
501                 newTopRow = -tpd->lastUsedHistoryRow;
502         } else {
503             if (tpd->topRow >= -lines)
504                 newTopRow = tpd->topRow - -lines;
505             else
506                 newTopRow = 0;
507         }
508     }
509
510     /* adjust lines to the clipped newTopRow... */
511     lines = newTopRow - oldTopRow;
512
513     /* we don't need to do any for 0... */
514     if (0 == lines)
515         return;
516
517     if (tw->term.jumpScroll &&
518             ((lines + tpd->scroll.jump.scrollLines) >
519             (tw->term.rows -
520                 tpd->scrollLockTopRow -
521                 (tw->term.rows - 1 - tpd->scrollLockBottomRow))) ||
522             ((lines + tpd->scroll.jump.scrollLines) <
523             -(tw->term.rows -
524                 tpd->scrollLockTopRow -
525                 (tw->term.rows - 1 - tpd->scrollLockBottomRow))) || 
526             (tpd->scrollTopRow != tpd->scrollLockTopRow) ||
527             (tpd->scrollBottomRow != tpd->scrollLockBottomRow)) {
528         /* scroll out the queued up jump scroll lines... */
529         if (tpd->scroll.jump.scrollLines != 0) {
530             (void) _DtTermPrimScrollWait(w);
531         }
532     }
533
534     /* move the memory locked region in screen memory...
535      */
536     /* Assuming a 24 line buffer with lock on 10 and 20 and 30 lines
537      * of screen buffer, with one below and scrolling 3 lines, we do
538      * the following:
539      *
540      *  oooooLLLLLLLLLL          LLLLo
541      *  ||||||||||||||||||||||||||||||
542      *  000000000011111111112222222222
543      *  012345678901234567890123456789
544      *
545      * move lines 15-17 to the top of the buffer:
546      *
547      *  ooooooooLLLLLLLLLL       LLLLo
548      *  ||||||||||||||||||||||||||||||
549      *  111000000000011111112222222222
550      *  567012345678901234890123456789
551      *
552      * move line 29 to the bottom of the unlocked area:
553      * 
554      *  ooooooooLLLLLLLLLL        LLLL
555      *  ||||||||||||||||||||||||||||||
556      *  111000000000011111112222222222
557      *  567012345678901234890123495678
558      *
559      * clear the top2 lines and move it to the bottom of the
560      * unlocked area:
561      * 
562      *  ooooooLLLLLLLLLL          LLLL
563      *  ||||||||||||||||||||||||||||||
564      *  100000000001111111222221122222
565      *  701234567890123489012345695678
566      * 
567      */
568
569     /* if we have a history buffer and we are going to be scrolling lines
570      * off of the buffer, we should move them into the history buffer.
571      */
572     if ((tpd->scrollLockTopRow == 0) &&
573             (tpd->scrollLockBottomRow >= (tw->term.rows - 1))) {
574         /*DKS*/
575     }
576
577     /* first move the top lock area... */
578     if (tpd->scrollLockTopRow > 0) {
579         (void) _DtTermPrimBufferMoveLockArea(tpd->termBuffer,
580                 tpd->topRow + lines, tpd->topRow, tpd->scrollLockTopRow);
581     }
582
583     /* now move the bottom lock area... */
584     if (tpd->scrollLockBottomRow < (tw->term.rows - 1)) {
585         /* if we are moving up, let's move as many rows as we can from
586          * bottom offscreen memory...
587          */
588         lines2 = 0;
589
590         if ((lines > 0) && (tpd->bufferRows > (tpd->topRow + tw->term.rows))) {
591             /* clip lines2 to the number of lines that we can obtain
592              * from the bottom of the buffer...
593              */
594             lines2 = lines;
595             if (lines2 > (tpd->bufferRows - (tpd->topRow + tw->term.rows))) {
596                 lines2 = tpd->bufferRows - (tpd->topRow + tw->term.rows);
597             }
598
599             if (lines2 > 0) {
600                 /* move them... */
601                 (void) _DtTermPrimBufferMoveLockArea(tpd->termBuffer,
602                         tpd->topRow + tpd->scrollLockBottomRow - lines2,
603                         tpd->topRow + tpd->scrollLockBottomRow,
604                         (tw->term.rows - 1) - tpd->scrollLockBottomRow);
605             }
606         }
607
608         /* figure out how many lines we will need to take from the
609          * top...
610          */
611         lines2 = lines - lines2;
612         if (lines2 > 0) {
613             for (i = 0; i < lines2; i++) {
614                 (void) _DtTermPrimBufferClearLine(tpd->termBuffer, i, 0);
615             }
616             (void) _DtTermPrimBufferMoveLockArea(tpd->termBuffer,
617                     0,
618                     lines2,
619                     tpd->topRow + tpd->scrollLockBottomRow + 1 - lines);
620             newTopRow -= lines2;
621         }
622     }
623
624     /* set topRow... */
625     tpd->topRow = newTopRow;
626
627     if (tw->term.jumpScroll) {
628         /* jump scroll...
629          */
630         /* queue up the lines for scrolling... */
631         tpd->scroll.jump.scrollLines += lines;
632         tpd->scroll.jump.scrolled = True;
633         tpd->scrollTopRow = tpd->scrollLockTopRow;
634         tpd->scrollBottomRow = tpd->scrollLockBottomRow;
635
636         /* scroll out the scrollRefreshRows flags now... */
637         /* NOTE: we loose the refresh flag for all rows that are scrolled
638          * off.  The result of this is that if we do a scroll up followed
639          * by a scroll down, we will (at a minimum) refresh the top and
640          * bottom lines.  One workaround would be to tripple the buffer
641          * and keep the lines that get scrolled off the top or bottom.
642          * This would probably break something, since there are times
643          * that the scrolled off line gets modified or even cleared (such
644          * as delete line off of the top of the display), so this might
645          * not be a very good idea.
646          */
647         if (lines > 0) {
648             /* scroll them up... */
649             for (i = tpd->scrollTopRow;
650                     i <= tpd->scrollBottomRow - lines; i++) {
651                 tpd->scrollRefreshRows[i] =
652                         tpd->scrollRefreshRows[i + lines];
653             }
654             /* set the rest... */
655             for (; i <= tpd->scrollBottomRow; i++) {
656                 tpd->scrollRefreshRows[i] = True;
657             }
658         } else {
659             /* remember, lines is negative... */
660             for (i = tpd->scrollBottomRow;
661                     i >= tpd->scrollTopRow + -lines; i--) {
662                 tpd->scrollRefreshRows[i] =
663                         tpd->scrollRefreshRows[i - -lines];
664             }
665             for (; i >= tpd->scrollTopRow; i--) {
666                 tpd->scrollRefreshRows[i] = True;
667             }
668         }
669     } else {
670         /* non jump-scroll...
671          */
672         if (tpd->scrollInProgress) {
673             /* let's queue up the scroll and bail out...
674              */
675             tpd->scroll.nojump.pendingScrollLines += lines;
676             /*DKS!!! v v v v v v v v v v v v v v v v v v v v v v */
677             if (tpd->scroll.nojump.pendingScrollLines > 1)
678                 (void) fprintf(stderr,
679                         "tell Dave _DtTermPrimScrollText has pendingScrollLines=%d\n",
680                         tpd->scroll.nojump.pendingScrollLines);
681             /*DKS!!! ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ */
682             tpd->scroll.nojump.pendingScroll = True;
683             tpd->scroll.nojump.pendingScrollTopRow = tpd->scrollLockTopRow;
684             tpd->scroll.nojump.pendingScrollBottomRow = tpd->scrollLockBottomRow;
685             (void) _DtTermPrimStartOrStopPtyInput(w);
686             return;
687         }
688
689         /* no scroll in progress, let's scroll it... */
690         tpd->scrollTopRow = tpd->scrollLockTopRow;
691         tpd->scrollBottomRow = tpd->scrollLockBottomRow;
692         (void) doActualScroll(w, lines);
693     }
694
695     /*  This case:    (tpd->scrollLockTopRow > 0)
696         is handled elsewhere.
697      */
698     if ( lines > 0 && tpd->scrollLockBottomRow < (tw->term.rows-1)) {
699              _DtTermPrimSelectMoveLines(w, tpd->scrollLockTopRow + lines, 
700                          tpd->scrollLockTopRow,
701                          tpd->scrollLockBottomRow - tpd->scrollLockTopRow);
702     }
703
704     return;
705 }
706
707 void
708 _DtTermPrimScrollTextTo(Widget w, short topRow)
709 {
710     DtTermPrimitiveWidget tw = (DtTermPrimitiveWidget) w;
711     struct termData *tpd = tw->term.tpd;
712     int oldTopRow;
713
714     if (topRow == tpd->topRow) {
715         /* already there...  */
716         return;
717     }
718
719     (void) _DtTermPrimScrollText(w, topRow - tpd->topRow);
720     return;
721 }
722
723 void
724 _DtTermPrimScrollTextArea(Widget w, short scrollStart, short scrollLength,
725         short scrollDistance)
726 {
727     DtTermPrimitiveWidget tw = (DtTermPrimitiveWidget) w;
728     struct termData *tpd = tw->term.tpd;
729     int oldTopRow;
730     int exposeY;
731     int exposeHeight;
732     int i;
733
734 #ifdef  NOTDEF
735     if (scrollDistance > 0) {
736         /* scrolling text up... */
737         if (tpd->lastUsedRow <= tpd->topRow + scrollStart) {
738             /* scrolling past lastUsedRow -- nothing to scroll... */
739             return;
740         }
741     }
742 #endif  /* NOTDEF */
743     /* clip the length... */
744     if (scrollStart + scrollLength > tw->term.rows) {
745         scrollLength = tw->term.rows - scrollStart;
746     }
747
748     /* we don't need to do anything for 0 distance... */
749     if (0 == scrollDistance) {
750         return;
751     }
752
753     /* if we are in jumpscroll mode and:
754      *      -our region differs (scrollTopRow and scrollBottomRow), or
755      *      -we are going to queue more lines than the scroll region
756      * we need to complete the queued scroll...
757      */
758     if (tw->term.jumpScroll &&
759             ((scrollDistance + tpd->scroll.jump.scrollLines > scrollLength) ||
760             (scrollDistance + tpd->scroll.jump.scrollLines < -scrollLength) ||
761             (tpd->scrollTopRow != scrollStart) ||
762             (tpd->scrollBottomRow != scrollStart + scrollLength - 1))) {
763         /* scroll out the queued up jump scroll lines... */
764         if (tpd->scroll.jump.scrolled != 0) {
765             (void) _DtTermPrimScrollWait(w);
766         }
767     }
768
769     if (tw->term.jumpScroll) {
770         /* jump scroll...
771          */
772         /* queue up the lines for scrolling... */
773         tpd->scroll.jump.scrollLines += scrollDistance;
774         tpd->scroll.jump.scrolled = True;
775         tpd->scrollTopRow = scrollStart;
776         tpd->scrollBottomRow = scrollStart + scrollLength - 1;
777
778         /* scroll out the scrollRefreshRows flags now... */
779         if (scrollDistance > 0) {
780             /* scroll them up... */
781             for (i = tpd->scrollTopRow;
782                     i <= tpd->scrollBottomRow - scrollDistance; i++) {
783                 tpd->scrollRefreshRows[i] =
784                         tpd->scrollRefreshRows[i + scrollDistance];
785             }
786             /* set the rest... */
787             for (; i <= tpd->scrollBottomRow; i++) {
788                 tpd->scrollRefreshRows[i] = True;
789             }
790         } else {
791             for (i = tpd->scrollBottomRow;
792                     i >= tpd->scrollTopRow + -scrollDistance; i--) {
793                 tpd->scrollRefreshRows[i] =
794                         tpd->scrollRefreshRows[i - -scrollDistance];
795             }
796             for (; i >= tpd->scrollTopRow; i--) {
797                 tpd->scrollRefreshRows[i] = True;
798             }
799         }
800     } else {
801         /* non jump scroll...
802          */
803         if (tpd->scrollInProgress) {
804             /* let's queue up the scroll and bail out...
805              */
806             tpd->scroll.nojump.pendingScrollLines += scrollDistance;
807             /*DKS!!! v v v v v v v v v v v v v v v v v v v v v v */
808             if (tpd->scroll.nojump.pendingScrollLines > 1)
809                 (void) fprintf(stderr,
810                         "tell Dave _DtTermPrimScrollText has pendingScrollLines=%d\n",
811                         tpd->scroll.nojump.pendingScrollLines);
812             /*DKS!!! ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ */
813             tpd->scroll.nojump.pendingScroll = True;
814             tpd->scroll.nojump.pendingScrollTopRow = scrollStart;
815             tpd->scroll.nojump.pendingScrollBottomRow =
816                     scrollStart + scrollLength - 1;
817             (void) _DtTermPrimStartOrStopPtyInput(w);
818             return;
819         }
820
821         /* no scroll in progress, let's scroll it... */
822         tpd->scrollTopRow = scrollStart;
823         tpd->scrollBottomRow = scrollStart + scrollLength - 1;
824         (void) doActualScroll(w, scrollDistance);
825         tpd->scrollInProgress = True;
826     }
827 }
828
829 /**************************************************************************
830  *  Function:
831  *      _DtTermPrimScrollComplete(): finish a scroll.  This is normall called
832  *              to complete a non-jump scroll, or if it is necessary to
833  *              complete and flush any scroll.
834  *
835  *  Parameters:
836  *      Widget w: widget to scroll
837  *
838  *  Returns:
839  *      <nothing>
840  *
841  *  Notes:
842  *
843  *      This function is used in non-jump scroll only!!!
844  *
845  *      This function is called by the Term widget when a no-expose or
846  *      final graphics expose event is received (which was generated by
847  *      the previous scroll's copy area).  It will invoke the scroll that
848  *      was queued up and caused pty input to be blocked.
849  */
850
851 void
852 _DtTermPrimScrollComplete(Widget w, Boolean flush)
853 {
854     DtTermPrimitiveWidget tw = (DtTermPrimitiveWidget) w;
855     struct termData *tpd = tw->term.tpd;
856
857     if (tw->term.jumpScroll) {
858         /* jump scroll...
859          */
860         if (flush) {
861             /* we need to finish any pending scrolls...
862              */
863             /* In jump scroll, all we need to do is to invoke the scroll.
864              * _DtTermPrimScrollWait() will perform the actual copy area and
865              * wait for the generated graphics expose / no expose events...
866              */
867             if (tpd->scroll.jump.scrolled) {
868                 Debug('s', fprintf(stderr,
869                         ">>_DtTermPrimScrollComplete() calling _DtTermPrimScrollWait()\n"));
870                 (void) _DtTermPrimScrollWait(w);
871             }
872         }
873     } else {
874         /* non-jump scroll...
875          */
876
877         if (flush && tpd->scrollInProgress) {
878             /* if we have a pending scroll, we need to wait for it to
879              * complete.  Normally, we would service this out of the
880              * XtMainLoop() dispatch loop, but for some reason we need
881              * to do it now.
882              */
883             (void) waitOnCopyArea(w);
884             tpd->scrollInProgress = False;
885         }
886
887         if (!tw->term.jumpScroll && tpd->scroll.nojump.pendingScroll) {
888             /* do the queued scroll... */
889             tpd->scrollTopRow = tpd->scroll.nojump.pendingScrollTopRow;
890             tpd->scrollBottomRow = tpd->scroll.nojump.pendingScrollBottomRow;
891             (void) doActualScroll(w, tpd->scroll.nojump.pendingScrollLines);
892
893             /* no lines pending, but there is a scroll in progress... */
894             tpd->scroll.nojump.pendingScrollLines = 0;
895             tpd->scrollInProgress = True;
896             tpd->scroll.nojump.pendingScroll = False;
897
898             if (flush) {
899                 /* wait for it to complete...
900                  */
901                 (void) waitOnCopyArea(w);
902                 tpd->scrollInProgress = False;
903
904                 /* If there is any pending input or if we need to reinstall
905                  * the input select, force a pty read...
906                  */
907                 (void) XtAppAddTimeOut(XtWidgetToApplicationContext(w),
908                         0, _DtTermPrimForcePtyRead, (XtPointer) w);
909             }
910         }
911     }
912 }
913
914 void
915 _DtTermPrimScrollCompleteIfNecessary(Widget w, short scrollTopRow,
916         short scrollBottomRow, short lines)
917 {
918     DtTermPrimitiveWidget tw = (DtTermPrimitiveWidget) w;
919     struct termData *tpd = tw->term.tpd;
920     short maxJumpScrollLines;
921
922     if ((scrollTopRow != tpd->scrollTopRow) ||
923             (scrollBottomRow != tpd->scrollBottomRow)) {
924         (void) _DtTermPrimScrollComplete(w, True);
925         return;
926     }
927
928     if (tw->term.jumpScroll) {
929         maxJumpScrollLines = tpd->scrollBottomRow - tpd->scrollTopRow + 1;
930         if ((lines + tpd->scroll.jump.scrollLines > maxJumpScrollLines) ||
931                 (lines + tpd->scroll.jump.scrollLines < -maxJumpScrollLines))
932             (void) _DtTermPrimScrollComplete(w, True);
933             return;
934     } else {
935         if (!tw->term.jumpScroll && tpd->scroll.nojump.pendingScroll) {
936             (void) _DtTermPrimScrollComplete(w, True);
937             return;
938         }
939     }
940 }