Add GNU LGPL headers to all .c .C and .h files
[oweals/cde.git] / cde / lib / DtTerm / TermPrim / TermPrimRender.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[] = "$TOG: TermPrimRender.c /main/3 1999/07/20 17:34:54 mgreess $";
26 #endif  /* VERBOSE_REV_INFO */
27 #endif  /* lint */
28
29 /*                                                                      *
30  * (c) Copyright 1993, 1994, 1996 Hewlett-Packard Company               *
31  * (c) Copyright 1993, 1994, 1996 International Business Machines Corp. *
32  * (c) Copyright 1993, 1994, 1996 Sun Microsystems, Inc.                *
33  * (c) Copyright 1993, 1994, 1996 Novell, Inc.                          *
34  * (c) Copyright 1996 Digital Equipment Corporation.                    *
35  * (c) Copyright 1996 FUJITSU LIMITED.                                  *
36  * (c) Copyright 1996 Hitachi.                                          *
37  */
38
39 #include "TermHeader.h"
40 #include "TermPrimDebug.h"
41 #include "TermPrimP.h"
42 #include "TermPrimI.h"
43 #include "TermPrimData.h"
44 #include "TermPrimLineDraw.h"
45 #include "TermPrimBufferP.h"
46 #include "TermPrimRenderP.h"
47 #include "TermPrimSelectP.h"
48 #include "TermPrimMessageCatI.h"
49 #include <limits.h>
50
51 #ifdef  DKS
52 void
53 _termSetRenderFont(Widget w, TermFont *termFont)
54 {
55     DtTermPrimitiveWidget tw = (DtTermPrimitiveWidget) w;
56     struct termData *tpd = tw->term.tpd;
57     int i;
58
59     /* DKS: ** alert ** alert ** alert ** alert ** alert **
60      *
61      *      The following opens up a memory leak that needs to be closed.
62      *      we need to free up both the list and any created entries...
63      */
64
65     /* do initialization... */
66     if (!tpd->renderTermFontsNum) {
67         tpd->renderTermFontsNum = 4;
68         tpd->renderTermFonts = (TermFont **) malloc((unsigned)
69                 tpd->renderTermFontsNum * sizeof(TermFont *));
70         (void) memset(tpd->renderTermFonts, '\0',
71                 tpd->renderTermFontsNum * sizeof(TermFont *));
72     }
73
74     /* replace the font if we already have one defined, else add it... */
75     for (i = 0; i < tpd->renderTermFontsNum; i++) {
76         if (tpd->renderTermFonts[i] && (tpd->renderTermFonts[i]->id ==
77                 termFont->id))
78             /* found an existing font with this id... */
79             break;
80     }
81
82     if (i >= tpd->renderTermFontsNum) {
83         /* we didn't find one, so let's add this one to the list... */
84         for (i = 0; i < tpd->renderTermFontsNum; i++) {
85             if (!tpd->renderTermFonts[i])
86                 break;
87         }
88
89         /* check for list full... */
90         if (i >= tpd->renderTermFontsNum) {
91             (void) fprintf(stderr,
92                     "setRenderFont: list full can't add font to widget 0x%lx\n",
93                     w);
94         }
95
96         /* we found an empty spot and need to malloc storage... */
97         tpd->renderTermFonts[i] = (TermFont *) malloc(sizeof(TermFont));
98         /* we don't need to initialize it as we will do a copy below... */
99     }
100
101     (void) memcpy(tpd->renderTermFonts[i], termFont, sizeof(TermFont));
102     if ((0 == tpd->cellWidth) || ('@' == termFont->id)) {
103         tpd->cellWidth = termFont->width;
104         tpd->cellHeight = termFont->height;
105
106         tw->term.widthInc = termFont->width;
107         tw->term.heightInc = termFont->height;
108         tw->term.ascent = termFont->ascent;
109     }
110 }
111 #endif  /* DKS */
112
113 void
114 _DtTermPrimBell(Widget w)
115 {
116     DtTermPrimitiveWidget tw = (DtTermPrimitiveWidget) w;
117     struct termData *tpd = tw->term.tpd;
118     int i;
119
120     if (tw->term.visualBell) {
121         /* the speed of this operation is not critical, so we will just
122          * use the standard text rendering GC and restore it after we
123          * are done...
124          */
125         if (tpd->renderGC.foreground !=
126                 tw->primitive.foreground ^ tw->core.background_pixel) {
127             tpd->renderGC.foreground = 
128                     tw->primitive.foreground ^ tw->core.background_pixel;
129             (void) XSetForeground(XtDisplay(w), tpd->renderGC.gc,
130                     tpd->renderGC.foreground);
131         }
132         (void) XSetFunction(XtDisplay(w), tpd->renderGC.gc, GXxor);
133         for (i = 0; i < 2; i++) {
134             (void) XFillRectangle(XtDisplay(w), /* Display              */
135                     XtWindow(w),                /* Drawable             */
136                     tpd->renderGC.gc,           /* GC                   */
137                     tpd->offsetX,               /* x                    */
138                     tpd->offsetY,               /* y                    */
139                     tw->term.columns * tpd->cellWidth,
140                                                 /* width                */
141                     tw->term.rows * tpd->cellHeight);
142                                                 /* height               */
143             (void) XSync(XtDisplay(w), 0);
144         }
145         /* restore the GC... */
146         (void) XSetFunction(XtDisplay(w), tpd->renderGC.gc, GXcopy);
147     } else {
148         (void) XBell(XtDisplay(w), 0);
149     }
150 }
151
152 void
153 _DtTermPrimRefreshText(Widget w, short startColumn, short startRow,
154         short endColumn, short endRow)
155 {
156     DtTermPrimitiveWidget tw = (DtTermPrimitiveWidget) w;
157     struct termData *tpd = tw->term.tpd;
158     TermBuffer tBuffer = tpd->termBuffer;
159     int lineWidth;
160     unsigned char *linePtr;
161     TermFont termFont;
162     int currentColorPair = 0;
163     int currentVideo = 0;
164     short chunkStartColumn;
165     short chunkWidth;
166     enhValues enhancements;
167     int i;
168     int lineNum;
169     unsigned long valueMask;
170     GC gc;
171     XGCValues values;
172     TermEnhInfoRec enhInfo;
173     Boolean checkSelection = False;
174     int selectionEnd;
175     Pixel tmpPixel;
176     XmTextPosition  begin, end;
177     
178     DebugF('t', 0, fprintf(stderr, ">>_DtTermPrimRefreshText() starting\n"));
179     DebugF('t', 0, fprintf(stderr,
180             ">>_DtTermPrimRefreshText() startCol=%hd  startRow=%hd  endCol=%hd  endRow=%hd\n",
181             startColumn, startRow, endColumn, endRow));
182
183     if (tpd->mbCurMax > 1)
184     {
185         _DtTermPrimRefreshTextWc(w, startColumn, startRow, endColumn, endRow);
186         return;
187     }
188
189     /* clip start/end x/y... */
190     if (startColumn <= 0)
191         startColumn = 0;
192     if (startRow <= 0)
193         startRow = 0;
194     if (endColumn >= tw->term.columns)
195         endColumn = tw->term.columns - 1;
196     if (endRow >= tw->term.rows)
197         endRow = tw->term.rows - 1;
198
199     /*
200     ** don't display if we are in jump scroll and in the process
201     ** of scrolling and inside the scroll region...
202     */
203     if (tw->term.jumpScroll && tpd->scroll.jump.scrolled) {
204         /* set all the scrollRefreshRows flags... */
205         for (; startRow <= endRow; startRow++) {
206             tpd->scrollRefreshRows[startRow] = True;
207         }
208
209         DebugF('t', 0,
210                 fprintf(stderr,
211                 ">>_DtTermPrimRefreshText() jump scroll in progress, no render\n"));
212         return;
213     }
214
215     if (!tpd->renderGC.gc) {
216         /* get a drawImageString GC... */
217         int i;
218         XGCValues values;
219
220         /***********************************************************
221          * renderGC...
222          */
223         /* set the GC fg and bg... */
224         values.foreground = tw->term.reverseVideo ?
225                 tw->core.background_pixel : tw->primitive.foreground;
226         values.background = tw->term.reverseVideo ?
227                 tw->primitive.foreground : tw->core.background_pixel;
228
229         tpd->renderGC.gc = XCreateGC(XtDisplay(w), XtWindow(w),
230                 GCForeground | GCBackground, &values);
231
232         /* set the GC cache values... */
233         tpd->renderGC.foreground = values.foreground;
234         tpd->renderGC.background = values.background;
235         tpd->renderGC.fid = (Font) 0;
236
237         /***********************************************************
238          * renderReverseGC...
239          */
240         values.foreground = tw->term.reverseVideo ?
241                 tw->primitive.foreground : tw->core.background_pixel;
242         values.background = tw->term.reverseVideo ?
243                 tw->core.background_pixel : tw->primitive.foreground;
244
245         tpd->renderReverseGC.gc = XCreateGC(XtDisplay(w), XtWindow(w),
246                 GCForeground | GCBackground, &values);
247
248         /* set the GC cache values... */
249         tpd->renderReverseGC.foreground = values.foreground;
250         tpd->renderReverseGC.background = values.background;
251         tpd->renderReverseGC.fid = (Font) 0;
252
253         /***********************************************************
254          * clearGC...
255          */
256         values.foreground = tw->term.reverseVideo ?
257                 tw->primitive.foreground : tw->core.background_pixel;
258         values.background = tw->term.reverseVideo ?
259                 tw->core.background_pixel : tw->primitive.foreground;
260
261         tpd->clearGC.gc = XCreateGC(XtDisplay(w), XtWindow(w),
262                 GCForeground | GCBackground, &values);
263
264         /* set the GC cache values... */
265         tpd->clearGC.foreground = values.foreground;
266         tpd->clearGC.background = values.background;
267         tpd->clearGC.fid = (Font) 0;
268     }
269
270 #ifdef SUN_MOTIF_PERF
271     /* use the clear GC... */
272     gc = tpd->clearGC.gc;
273
274     valueMask = (unsigned long) 0;
275     if (tpd->clearGC.foreground !=
276             tw->term.reverseVideo ?
277             tw->core.background_pixel : tw->primitive.foreground) {
278         values.foreground =
279                 tw->term.reverseVideo ?
280                 tw->primitive.foreground :
281                 tw->core.background_pixel;
282         tpd->clearGC.foreground = values.foreground;
283         valueMask |= GCForeground;
284     }
285     if (tpd->clearGC.background !=
286             tw->term.reverseVideo ?
287             tw->primitive.foreground :
288             tw->core.background_pixel) {
289         values.background =
290                 tw->term.reverseVideo ?
291                 tw->core.background_pixel :
292                 tw->primitive.foreground;
293         tpd->clearGC.background = values.background;
294         valueMask |= GCBackground;
295     }
296     if (valueMask) {
297         (void) XChangeGC(XtDisplay(w), tpd->clearGC.gc,
298                 valueMask, &values);
299     }
300     (void) XFillRectangle(XtDisplay(w),         /* Display              */
301                     XtWindow(w),                /* Drawable             */
302                     gc,                         /* GC                   */
303                     startColumn * tpd->cellWidth + tpd->offsetX,
304                                                 /* x                    */
305                     startRow * tpd->cellHeight + tpd->offsetY,
306                                                 /* y                    */
307                     (endColumn - startColumn + 1) * tpd->cellWidth,
308                                                 /* width                */
309                     (endRow - startRow + 1) * tpd->cellHeight);         
310                                                 /* height               */
311 #endif /* SUN_MOTIF_PERF */
312
313     for (; startRow <= endRow; startRow++) {
314         /* if we are refreshing a full line, then we can clear the
315          * scrollRefreshRows flag for this line...
316          */
317         if ((startColumn == 0) && (endColumn >= tw->term.columns - 1)) {
318             tpd->scrollRefreshRows[startRow] = False;
319         }
320
321         lineNum = startRow + tpd->topRow;
322         if (!tw->term.jumpScroll && tpd->scroll.nojump.pendingScroll) {
323             if (lineNum != tpd->scrollLockBottomRow)
324             {
325                 lineNum -= tpd->scroll.nojump.pendingScrollLines;
326             }
327         }
328
329         /* are we in the selected area?... */
330         if (tpd->useHistoryBuffer)
331         {
332             if (_DtTermPrimSelectGetSelection(w, &begin, &end) &&
333                 (begin < end) &&
334                 (lineNum >= (begin / (tpd->selectInfo->columns + 1)) - 
335                             tpd->lastUsedHistoryRow) &&
336                 (lineNum <= (end   / (tpd->selectInfo->columns + 1)) - 
337                             tpd->lastUsedHistoryRow)) {
338                 checkSelection = True;
339             } else {
340                 checkSelection = False;
341             }
342         }
343         else
344         {
345             if (_DtTermPrimSelectGetSelection(w, &begin, &end) &&
346                 (begin < end) &&
347                 (lineNum >= (begin / (tpd->selectInfo->columns + 1))) &&
348                 (lineNum <= (end   / (tpd->selectInfo->columns + 1))))  {
349                 checkSelection = True;
350             } else {
351                 checkSelection = False;
352             }
353         }
354
355         chunkStartColumn = startColumn;
356         if (startColumn > endColumn) {
357             /* nothing to render on this line... */
358             continue;
359         }
360
361         if (lineNum >= tpd->lastUsedRow) {
362             /* we are pointing to empty screen space below the last used
363              * line of the display...
364              */
365             lineWidth = 0;
366             linePtr = NULL;
367         } else if (lineNum < 0) {
368             if ((tpd->useHistoryBuffer) &&
369                     (-lineNum <= tpd->lastUsedHistoryRow)) {
370                 /* get a line out of the history buffer... */
371                 lineWidth = MAX(0, MIN(endColumn - startColumn + 1,
372                     _DtTermPrimBufferGetLineWidth(tpd->historyBuffer,
373                     tpd->lastUsedHistoryRow + lineNum) - startColumn));
374                 linePtr =
375                     _DtTermPrimBufferGetCharacterPointer(tpd->historyBuffer,
376                     tpd->lastUsedHistoryRow + lineNum, startColumn);
377             } else {
378                 /* we are above the history buffer.  Should not happen, but...
379                  */
380                 lineWidth = 0;
381                 linePtr = NULL;
382             }
383         } else {
384             /* get the line width and a pointer to the data... */
385             lineWidth = MAX(0, MIN(endColumn - startColumn + 1,
386                     _DtTermPrimBufferGetLineWidth(tBuffer, lineNum) -
387                     startColumn));
388             linePtr = _DtTermPrimBufferGetCharacterPointer(tBuffer, lineNum,
389                     startColumn);
390         }
391
392         while (lineWidth > 0) {
393             /* get the enhancement values for the first chunk of the
394              * string...
395              */
396             if (lineNum >= 0) {
397                 (void) _DtTermPrimBufferGetEnhancement(tBuffer,
398                                                 /* TermBuffer           */
399                         lineNum,                /* row                  */
400                         chunkStartColumn,       /* col                  */
401                         &enhancements,          /* enhancements         */
402                         &chunkWidth,            /* width                */
403                         countAll);              /* countWhich           */
404             } else {
405                 /* get it from the history buffer... */
406                 (void) _DtTermPrimBufferGetEnhancement(tpd->historyBuffer,
407                                                 /* TermBuffer           */
408                         tpd->lastUsedHistoryRow + lineNum,
409                                                 /* row                  */
410                         chunkStartColumn,       /* col                  */
411                         &enhancements,          /* enhancements         */
412                         &chunkWidth,            /* width                */
413                         countAll);              /* countWhich           */
414             }
415
416             /* clip chunkWidth... */
417             if (chunkWidth > lineWidth)
418                 chunkWidth = lineWidth;
419
420             /* set reasonable defaults for our render info... */
421             enhInfo.fg = tw->primitive.foreground;
422             enhInfo.bg = tw->core.background_pixel;
423             enhInfo.font = tpd->defaultTermFont;
424             enhInfo.flags = (unsigned long) 0;
425
426             /* set our font and color from the enhancements... */
427             if (ENH_PROC(tBuffer)) {
428                 (void) (*(ENH_PROC(tBuffer)))(w, enhancements, &enhInfo);
429             }
430
431             /* if we are in reverse video mode... */
432             if (tw->term.reverseVideo) {
433                 /* flip fg and bg... */
434                 tmpPixel = enhInfo.fg;
435                 enhInfo.fg = enhInfo.bg;
436                 enhInfo.bg = tmpPixel;
437             }
438
439             /* are we in the selection area?... */
440             if (checkSelection && 
441                 _DtTermPrimSelectIsInSelection(w, lineNum, chunkStartColumn,
442                                          chunkWidth, &chunkWidth)) {
443
444                 /* flip fg and bg... */
445                 tmpPixel = enhInfo.fg;
446                 enhInfo.fg = enhInfo.bg;
447                 enhInfo.bg = tmpPixel;
448             }
449                         
450             /* if secure, we will use a XFillRectangle, and we need
451              * foreground set to the background...
452              */
453
454             if (TermIS_SECURE(enhInfo.flags)) {
455                 /* render secure video locally...
456                  */
457                 /* set the renderReverseGC... */
458                 valueMask = (unsigned long) 0;
459                 if (tpd->renderReverseGC.foreground != enhInfo.bg) {
460                     tpd->renderReverseGC.foreground = enhInfo.bg;
461                     values.foreground = enhInfo.bg;
462                     valueMask |= GCForeground;
463                 }
464                 if (valueMask) {
465                     (void) XChangeGC(XtDisplay(w), tpd->renderReverseGC.gc,
466                             valueMask, &values);
467                 }
468                 (void) XFillRectangle(XtDisplay(w),
469                         XtWindow(w),
470                         tpd->renderReverseGC.gc,
471                         chunkStartColumn * tpd->cellWidth + tpd->offsetX,
472                         startRow * tpd->cellHeight + tpd->offsetY,
473                         tpd->cellWidth *  chunkWidth,
474                         tpd->cellHeight);
475
476                 /* underline as well... */
477                 if (TermIS_UNDERLINE(enhInfo.flags)) {
478                     valueMask = (unsigned long) 0;
479                     if (tpd->renderGC.foreground != enhInfo.fg) {
480                         tpd->renderGC.foreground = enhInfo.fg;
481                         values.foreground = enhInfo.fg;
482                         valueMask |= GCForeground;
483                     }
484                     if (valueMask) {
485                         (void) XChangeGC(XtDisplay(w), tpd->renderGC.gc,
486                                 valueMask, &values);
487                     }
488                     (void) XDrawLine(XtDisplay(w),
489                                                 /* Display              */
490                             XtWindow(w),        /* Drawable             */
491                             tpd->renderGC.gc,   /* GC                   */
492                             chunkStartColumn * tpd->cellWidth + tpd->offsetX,
493                                                 /* X1                   */
494                             startRow * tpd->cellHeight + tpd->offsetY +
495                             tpd->cellHeight - 1,
496                                                 /* Y1                   */
497                             (chunkStartColumn + chunkWidth) * tpd->cellWidth +
498                             tpd->offsetX,       /* X2                   */
499                             startRow * tpd->cellHeight + tpd->offsetY +
500                             tpd->cellHeight - 1);
501                                                 /* Y2                   */
502                 }
503             } else {
504                 (void) _DtTermPrimRenderText(
505                     w,                          /* Widget               */
506                     enhInfo.font,               /* TermFont             */
507                     enhInfo.fg,                 /* fg Pixel             */
508                     enhInfo.bg,                 /* bg Pixel             */
509                     enhInfo.flags,              /* flags                */
510                     chunkStartColumn * tpd->cellWidth + tpd->offsetX,
511                                                 /* x                    */
512                     startRow * tpd->cellHeight + tpd->offsetY,
513                                                 /* y                    */
514                     linePtr,                    /* string               */
515                     chunkWidth);                /* width                */
516             }
517             chunkStartColumn += chunkWidth;
518             lineWidth -= chunkWidth;
519             linePtr += chunkWidth;
520         }
521
522         /* clear any extra space in the line.  chunkStartColumn now points to
523          * the end of the line, and lineWidth == 0...
524          */
525         while (endColumn - chunkStartColumn >= 0) {
526             chunkWidth = endColumn - chunkStartColumn + 1;
527             if (checkSelection && 
528                 _DtTermPrimSelectIsInSelection(w, lineNum, chunkStartColumn,
529                                          chunkWidth, &chunkWidth)) {
530                 /* use the render gc set to the fg color... */
531                 gc = tpd->renderReverseGC.gc;
532
533                 valueMask = (unsigned long) 0;
534                 if (tpd->renderReverseGC.foreground !=
535                         tw->term.reverseVideo ?
536                         tw->core.background_pixel : tw->primitive.foreground) {
537                     values.foreground =
538                             tw->term.reverseVideo ?
539                             tw->core.background_pixel :
540                             tw->primitive.foreground;
541                     tpd->renderReverseGC.foreground = values.foreground;
542                     valueMask |= GCForeground;
543                 }
544                 if (valueMask) {
545                     (void) XChangeGC(XtDisplay(w), tpd->renderReverseGC.gc,
546                             valueMask, &values);
547                 }
548 #ifndef SUN_MOTIF_PERF
549             } else {
550                 /* use the clear GC... */
551                 gc = tpd->clearGC.gc;
552
553                 valueMask = (unsigned long) 0;
554                 if (tpd->clearGC.foreground !=
555                         tw->term.reverseVideo ?
556                         tw->core.background_pixel : tw->primitive.foreground) {
557                     values.foreground =
558                             tw->term.reverseVideo ?
559                             tw->primitive.foreground :
560                             tw->core.background_pixel;
561                     tpd->clearGC.foreground = values.foreground;
562                     valueMask |= GCForeground;
563                 }
564                 if (tpd->clearGC.background !=
565                         tw->term.reverseVideo ?
566                         tw->primitive.foreground :
567                         tw->core.background_pixel) {
568                     values.background =
569                             tw->term.reverseVideo ?
570                             tw->core.background_pixel :
571                             tw->primitive.foreground;
572                     tpd->clearGC.background = values.background;
573                     valueMask |= GCBackground;
574                 }
575                 if (valueMask) {
576                     (void) XChangeGC(XtDisplay(w), tpd->clearGC.gc,
577                             valueMask, &values);
578                 }
579             }
580 #endif /* not SUN_MOTIF_PERF */
581
582             if (isDebugFSet('t', 1)) {
583 #ifdef  BBA
584 #pragma BBA_IGNORE
585 #endif  /*BBA*/
586                 /* Fill in the clear area so we can see what is going to
587                  * be displayed...
588                  */
589                 (void) XFillRectangle(XtDisplay(w),
590                         XtWindow(w),
591                         tpd->renderGC.gc,
592                         chunkStartColumn * tpd->cellWidth + tpd->offsetX,
593                         startRow * tpd->cellHeight + tpd->offsetY,
594                         chunkWidth * tpd->cellWidth,
595                         tpd->cellHeight);
596                 (void) XSync(XtDisplay(w), False);
597                 (void) shortSleep(100000);
598             }
599             (void) XFillRectangle(XtDisplay(w),
600                                                 /* Display              */
601                     XtWindow(w),                /* Drawable             */
602                     gc,                         /* GC                   */
603                     chunkStartColumn * tpd->cellWidth + tpd->offsetX,
604                                                 /* x                    */
605                     startRow * tpd->cellHeight + tpd->offsetY,
606                                                 /* y                    */
607                     chunkWidth * tpd->cellWidth,
608                                                 /* width                */
609                     tpd->cellHeight);           /* height               */
610 #ifdef SUN_MOTIF_PERF
611             }
612 #endif /* SUN_MOTIF_PERF */
613             chunkStartColumn += chunkWidth;
614         }
615     }
616     DebugF('t', 0, fprintf(stderr, ">>_DtTermPrimRefreshText() finished\n"));
617 }
618
619
620 /**************************************************************************
621  *  Function:
622  *      _DtTermPrimExposeText(): expose (refresh) the text in rectangular area
623  *  
624  *  Parameters:
625  *      Widget w: widget to expose
626  *      int x: starting x pixel in window
627  *      int y: starting y pixel in window
628  *      int width: width in pixels
629  *      int height: height in pixels
630  *      Boolean isExposeEvent: true, deal with copyarea / expose overlap
631  *  
632  *  Returns:
633  *      <nothing>
634  *  
635  *  Notes:
636  *      This function will redisplay the text in a rectangular exposure
637  *      area.  The x, y, width, and height are in absolute pixels.  The
638  *      internal x and y offsets will be accounted for, and the minimum
639  *      number of characters will be displayed.  The algorithm has been
640  *      tuned (somewhat) and no longer exposes an extra character at the
641  *      end of the refresh lines and an extra line at the bottom of the
642  *      refresh area.  This can be verified by using the 't' and 'e'
643  *      debug flags.
644  */
645
646 void
647 _DtTermPrimExposeText(Widget w, int x, int y, int width, int height,
648         Boolean isExposeEvent)
649 {
650     struct termData *tpd = ((DtTermPrimitiveWidget) w)->term.tpd;
651
652     DebugF('e', 0, fprintf(stderr, ">>exposeText() starting\n"));
653     DebugF('e', 0, fprintf(stderr,
654             ">>exposeText() startX=%d  startY=%d  width=%d  height=%d\n",
655             x, y, width, height));
656     DebugF('e', 0, fprintf(stderr,
657             ">>             offsetX=%d  offsetY=%d  cellHeight=%d  cellWidth=%d\n",
658             tpd->offsetX, tpd->offsetY, tpd->cellHeight, tpd->cellWidth));
659
660     /* The following "hack" takes care of the problem of an exposure event
661      * from the server and a copy area from the client crossing.  The
662      * combination of these two events can cause a race condition which
663      * manifests itself by leaving a hole in the terminal window.  What
664      * happens is this:
665      * 
666      * A window is partially obscured.  The terminal emulator does a copy
667      * area (scroll) which includes part of obscured area.  Before the
668      * server processes the copy area, the window is unobscured, and the
669      * server sends an exposure event back to the client.
670      * 
671      * - The window is partially obscured.
672      * 
673      * - The terminal emulator does a copy area (scroll) which includes a
674      *   portion of the obscured area.  Normally, the server will generate
675      *   a graphics exposure event for the obscured portion that it can't
676      *   copy which will allow the terminal emulator to update the area.
677      * 
678      * - Before the server receives the copy area request, the server
679      *   unobscures the window and sends an exposure event for the exposed
680      *   area.  (This is where the hack comes into play and refreshes the
681      *   scrolled portion of this area (and possibly some extra space as
682      *   well)).
683      * 
684      * - The server processes the copy area.  Since the area in question is
685      *   no longer obscured, the server will copy blank space and not
686      *   generate a graphics exposure event.
687      * 
688      * - The terminal emulator processes the exposure event and refreshes
689      *   the exposed area.  (This is the hack extends the exposure to cover
690      *   the gap).
691      * 
692      * - You now have a blank chunk of the terminal window where the
693      *   obscured area was scrolled (without the hack).
694      * 
695      * Our fix is similar to the one used in xterm.  The Motif text widget
696      * uses a more brute force method and simply extends the exposure event
697      * to the full height (or width) of the screen in the direction of the
698      * copy area.
699      */
700     if (isExposeEvent && tpd->scrollInProgress) {
701         int bothX1;
702         int bothX2;
703         int bothY1;
704         int bothY2;
705
706         bothX1 = MAX(tpd->scrollSrcX, x);
707         bothY1 = MAX(tpd->scrollSrcY, y);
708         bothX2 = MIN(tpd->scrollSrcX + tpd->scrollWidth, x + width - 1);
709         bothY2 = MIN(tpd->scrollSrcY + tpd->scrollHeight, y + height - 1);
710
711         if ((bothX2 > bothX1) && (bothY2 > bothY1)) {
712             (void) _DtTermPrimRefreshText(w,
713                     (x - tpd->offsetX + tpd->scrollDestX - tpd->scrollSrcX) /
714                     tpd->cellWidth,
715                     (y - tpd->offsetY + tpd->scrollDestY - tpd->scrollSrcY) /
716                     tpd->cellHeight,
717                     (x + width - 1 - tpd->offsetX + tpd->scrollDestX -
718                     tpd->scrollSrcX) / tpd->cellWidth,
719                     (y + height - 1 - tpd->offsetY + tpd->scrollDestY -
720                     tpd->scrollSrcY) / tpd->cellHeight);
721         }
722     }
723
724     /* render the text... */
725     DebugF('t', 0, fprintf(stderr, ">>exposeText() calling _DtTermPrimRefreshText()\n"));
726     (void) _DtTermPrimRefreshText(w, (x - tpd->offsetX) / tpd->cellWidth,
727             (y - tpd->offsetY)  / tpd->cellHeight,
728             (x + width - 1 - tpd->offsetX) / tpd->cellWidth,
729             (y + height - 1 - tpd->offsetY) / tpd->cellHeight);
730
731     DebugF('e', 0, fprintf(stderr, ">>exposeText() finished\n"));
732 }
733
734 void
735 _DtTermPrimFillScreenGap(Widget w)
736 {
737     DtTermPrimitiveWidget tw = (DtTermPrimitiveWidget) w;    
738     struct termData *tpd = tw->term.tpd;
739     TermBuffer tBuffer = tpd->termBuffer;
740     int linesNeeded;
741     int linesCopied;
742     int historyLinesNeeded;
743     int i1;
744     TermLineSelection selectionFlag;
745
746     /* before we insert any text, we need to insure that the cursor is
747      * on a valid buffer row.  If not, we will need to fill in any gap
748      * between the lastUsedRow and the current cursor postion with 0
749      * width lines, and get a buffer row for the current cursor row...
750      */
751     if (tpd->topRow + tpd->cursorRow >= tpd->lastUsedRow) {
752         /* if there are lines left in the buffer, just use them...
753          */
754         /* this does not effect the position of the selection... */
755         for (; (tpd->lastUsedRow < tpd->bufferRows) &&
756                 (tpd->topRow + tpd->cursorRow >= tpd->lastUsedRow); ) {
757             /* all we need to do is clear the line... */
758             _DtTermPrimBufferClearLine(tBuffer, tpd->lastUsedRow++, 0);
759         }
760
761         /* if we still need more lines, then we will need to steal from
762          * the top of the buffer...
763          */
764         if (tpd->topRow + tpd->cursorRow >= tpd->lastUsedRow) {
765             if (tpd->memoryLockMode == SCROLL_LOCKprotect) {
766                 if (!tpd->warningDialogMapped) {
767                     (void) _DtTermPrimWarningDialog(w,
768                             GETMESSAGE(NL_SETN_PrimRend, 1,"MEMORY FULL\nPress OK to clear"));
769                 }
770             } else {
771                 /* figure out how many lines are needed... */
772                 linesNeeded = tpd->topRow + tpd->cursorRow -
773                         (tpd->lastUsedRow - 1);
774                 linesCopied = 0;
775                 /* before we drop them off of the top, if we are
776                  * using a history buffer, we need to transfer them 
777                  * into the history buffer...
778                  */
779                 if (tpd->useHistoryBuffer &&
780                         (tpd->scrollLockTopRow == 0) &&
781                         (tpd->scrollLockBottomRow >= (tw->term.rows - 1))) {
782                     /* do we have enough lines in the history buffer, or
783                      * do we need to steal some from the top?...
784                      */
785                     historyLinesNeeded = linesNeeded -
786                             (tpd->historyBufferRows - tpd->lastUsedHistoryRow);
787                     if ((historyLinesNeeded > 0) &&
788                             (tpd->historyBufferRows > 0)) {
789                         /* take them from the top...  */
790                         /* we are effectively deleting the lines from the
791                          * top of the combined buffers...
792                          */
793                         (void) _DtTermPrimSelectDeleteLines(w, 0,
794                                 historyLinesNeeded);
795                         /* move the lines from the top to the bottom... */
796                         (void) _DtTermPrimBufferInsertLineFromTB(
797                                 tpd->historyBuffer, tpd->historyBufferRows - 1,
798                                 historyLinesNeeded, insertFromTop);
799                         /* the lines are now freed, adjust the used count... */
800                         tpd->lastUsedHistoryRow -= historyLinesNeeded;
801                         if (tpd->lastUsedHistoryRow < 0) {
802                             historyLinesNeeded += tpd->lastUsedHistoryRow;
803                             tpd->lastUsedHistoryRow = 0;
804                         }
805                     }
806
807                     /* copy the lines over... */
808                     for (i1 = 0; (i1 < linesNeeded) &&
809                             (tpd->lastUsedHistoryRow < tpd->historyBufferRows);
810                             i1++) {
811                         termChar *c1;
812                         short length;
813                         termChar *overflowChars;
814                         short overflowCount;
815
816                         /* get the line from the active buffer... */
817                         length = _DtTermPrimBufferGetLineLength(tBuffer,
818                                 i1);
819
820                         /*
821                         ** stuff it into the history buffer...
822                         ** (but only if there is something to copy)
823                         */
824                         if (length > 0) {
825                             unsigned char eIndex;
826                             short eCol;
827                             short eCount;
828                             enhValue *eValues = (enhValue *)NULL;
829
830                             overflowChars = (termChar *) XtMalloc(BUFSIZ * sizeof (termChar));
831                             c1 = _DtTermPrimBufferGetCharacterPointer(
832                                     tBuffer, i1, 0);
833
834                             /* Perpetuate the enhancements. */
835                             for (eCol = 0; eCol < length; eCol += eCount)
836                             {
837                                 if (_DtTermPrimBufferGetEnhancement(tBuffer,
838                                         i1, eCol, &eValues,
839                                         &eCount, countAll))
840                                 {
841                                     if ((eValues == (enhValue *)NULL) ||
842                                         (eCount <= 0))
843                                         break;
844
845                                     for (eIndex = 0;
846                                          eIndex < NUM_ENH_FIELDS(tBuffer);
847                                          eIndex++)
848                                     {
849                                         _DtTermPrimBufferSetEnhancement(
850                                             tpd->historyBuffer,
851                                             tpd->lastUsedHistoryRow,
852                                             eCol, eIndex, eValues[eIndex]);
853                                     }
854
855                                     (void) _DtTermPrimBufferInsert(
856                                             tpd->historyBuffer,
857                                             tpd->lastUsedHistoryRow,
858                                             eCol, c1, eCount,
859                                             False, &overflowChars,
860                                             &overflowCount);
861                                 }
862                                 else break;
863                             }
864
865                             if (eCol < length)
866                             {
867                                 /* Clear out enhancement values if necessary */
868                                 if (eValues != (enhValue *)NULL)
869                                 {
870                                     for (eIndex = 0;
871                                          eIndex < NUM_ENH_FIELDS(tBuffer);
872                                          eIndex++)
873                                     {
874                                         _DtTermPrimBufferSetEnhancement(
875                                             tpd->historyBuffer,
876                                             tpd->lastUsedHistoryRow,
877                                             eCol, eIndex, 0);
878                                     }
879                                 }
880                                 (void) _DtTermPrimBufferInsert(
881                                         tpd->historyBuffer,
882                                         tpd->lastUsedHistoryRow,
883                                         eCol, c1,
884                                         length - eCol,
885                                         False, &overflowChars,
886                                         &overflowCount);
887                             }
888
889                             (void) XtFree((char *) overflowChars); 
890                         } else {
891                             (void) _DtTermPrimBufferClearLine(
892                                     tpd->historyBuffer,
893                                     tpd->lastUsedHistoryRow, 0);
894                         }
895                         /* perpetuate the state of the wrap flag... */
896                         (void) _DtTermPrimBufferSetLineWrapFlag(
897                             tpd->historyBuffer,
898                             tpd->lastUsedHistoryRow,
899                             _DtTermPrimBufferTestLineWrapFlag(tBuffer, i1));
900                         /* perpetuate the state of the inSelection flag...
901                          */
902
903                         selectionFlag =
904                                 _DtTermPrimBufferGetInSelectionFlag(
905                                         tBuffer, i1);
906                         (void) _DtTermPrimBufferSetInSelectionFlag(
907                                 tpd->historyBuffer,
908                                 tpd->lastUsedHistoryRow,
909                                 selectionFlag);
910                 
911                         /* clear the inselection flag before we scroll
912                          * the lines off or we will end up releasing the
913                          * selection...
914                          */
915                         (void) _DtTermPrimBufferSetInSelectionFlag(
916                             tBuffer, i1, (TermLineSelection) 0);
917                         (void) tpd->lastUsedHistoryRow++;
918                         (void) linesCopied++;
919                     }
920                 }
921                     
922                 /* take them from the top.  If we are about to take lines
923                  * from the top without first copying them into the
924                  * history buffer, we need to adjust the selection
925                  * position...
926                  */
927                 if (linesNeeded > linesCopied) {
928                     (void) _DtTermPrimSelectDeleteLines(w,
929                             tpd->lastUsedHistoryRow + linesCopied,
930                             linesNeeded - linesCopied);
931                 }
932                 (void) _DtTermPrimBufferInsertLineFromTB(tBuffer,
933                         tpd->bufferRows - 1,
934                         linesNeeded, insertFromTop);
935                 /* adjust everything... */
936                 tpd->topRow -= linesNeeded;
937             }
938         }
939     }
940 }
941
942 static short
943 DoInsert(Widget w, unsigned char *buffer, int length, Boolean *wrapped)
944 {
945     DtTermPrimitiveWidget tw = (DtTermPrimitiveWidget) w;    
946     struct termData *tpd = tw->term.tpd;
947     TermBuffer tBuffer = tpd->termBuffer;
948     short newWidth;
949
950     termChar *returnChars;
951     short returnCount;
952
953     /* before we insert any text, we need to insure that the cursor is on
954      * a valid buffer row...
955      */
956     if (tpd->topRow + tpd->cursorRow >= tpd->lastUsedRow) {
957         (void) _DtTermPrimFillScreenGap(w);
958     }
959
960     /* insert the text... */
961     returnChars = (termChar *) XtMalloc(BUFSIZ * sizeof (termChar));
962     newWidth = _DtTermPrimBufferInsert(tBuffer, /* TermBuffer           */
963             tpd->topRow + tpd->cursorRow,               /* row                  */
964             tpd->cursorColumn,                  /* column               */
965             (termChar *) buffer,                /* newChars             */
966             length,                             /* numChars             */
967             tpd->insertCharMode != DtTERM_INSERT_CHAR_OFF,
968                                                 /* insert flag          */
969             &returnChars,                       /* return char ptr      */
970             &returnCount);                      /* return count ptr     */
971
972     if ((tpd->insertCharMode != DtTERM_INSERT_CHAR_ON_WRAP) || (returnCount <= 0)) {
973         (void) XtFree((char *) returnChars);
974         return(newWidth);
975     }
976
977     /* we are in insert char mode with wrap and we wrapped text off of
978      * the line...
979      */
980     *wrapped = True;
981
982     /* wrap the inserted characters into the following line... */
983     if (tpd->topRow + tpd->cursorRow + 1 >= tpd->lastUsedRow) {
984         /* fake cursorRow... */
985         (void) tpd->cursorRow++;
986         (void) _DtTermPrimFillScreenGap(w);
987         (void) tpd->cursorRow--;
988     }
989
990     /* we will allocate a temporary buffer so we don't have to worry
991      * about _DtTermPrimBufferInsert tromping over its overflow buffer...
992      */
993     length = returnCount;
994     buffer = (unsigned char *) XtMalloc(length);
995     (void) memcpy(buffer, returnChars, length);
996
997     /* insert the text into the next line... */
998     newWidth = _DtTermPrimBufferInsert(tBuffer, /* TermBuffer           */
999             tpd->topRow + tpd->cursorRow + 1,
1000                                         /* row                  */
1001             0,                          /* column               */
1002             (termChar *) buffer,        /* newChars             */
1003             length,                     /* numChars             */
1004             tpd->insertCharMode != DtTERM_INSERT_CHAR_OFF,
1005                                         /* insert flag          */
1006             &returnChars,               /* return char ptr      */
1007             &returnCount);              /* return count ptr     */
1008
1009     (void) XtFree((char *) buffer);
1010     (void) XtFree((char *) returnChars);
1011     return(newWidth);
1012 }
1013
1014 int
1015 _DtTermPrimInsertText(Widget w, unsigned char *buffer, int length)
1016 {
1017     DtTermPrimitiveWidget tw = (DtTermPrimitiveWidget) w;    
1018     struct termData *tpd = tw->term.tpd;
1019     TermBuffer tBuffer = tpd->termBuffer;
1020     int i;
1021     short renderStartX;
1022     short renderEndX;
1023     short insertStartX;
1024     short insertCharCount;
1025     short newWidth;
1026     Boolean needToRender = False;
1027     Boolean wrapped = False;
1028
1029
1030     if (tpd->mbCurMax > 1)
1031     {
1032         short    wcBufferLen;
1033         wchar_t *wcBuffer;
1034         wchar_t *pwc;
1035         int      i;
1036         int      mbLen;
1037         char    *pmb;        
1038 #ifdef    NOCODE
1039         /* 
1040         ** It would be nice if the calling function supplied us with a count
1041         ** of the number of mb characters in the buffer, then we wouldn't
1042         ** have to count them again.
1043         */
1044         /* 
1045         ** we could use this if the multi-byte buffer was null terminated
1046         */
1047         wcBufferLen = mbstowcs((wchar_t *)NULL, (char *)buffer, length);
1048 #else  /* NOCODE */
1049         i           = 0;
1050         pmb         = (char *)buffer;
1051         /* 
1052         ** we should never need more than length * sizeof(wchar_t)
1053         ** bytes to store the wide char equivalent of the incoming mb string
1054         */
1055         wcBuffer    = (wchar_t *)XtMalloc(length * sizeof(wchar_t));                                                
1056         pwc         = wcBuffer;
1057         wcBufferLen = 0;
1058         while (i < length)
1059         {
1060             switch (mbLen = mbtowc(pwc, pmb, MIN(((int)MB_CUR_MAX), length - i)))
1061             {
1062               case -1:
1063                 if ((int)MB_CUR_MAX <= length - i) {
1064                     /* we have a bogus multi-byte character.  Throw away
1065                      * the first byte and rescan (TM 12/14/93)...
1066                      */
1067                     /* 
1068                     ** in this case, we move the remaining length - i - 1 
1069                     ** bytes one byte to the left (to overwrite the bogus
1070                     ** byte)
1071                     */
1072                     memmove(pmb, pmb + 1, length - i - 1);
1073                     length--;
1074                     continue;
1075                 }
1076                 break;
1077               case  0:
1078                 /* 
1079                 ** treat null character same as any other character...
1080                 */
1081                 mbLen = 1;
1082               default:
1083                 i   += mbLen;
1084                 pmb += mbLen;
1085                 pwc++;
1086                 wcBufferLen++;
1087             }
1088         }
1089 #endif /* NOCODE */
1090         i = _DtTermPrimInsertTextWc(w, wcBuffer, wcBufferLen);
1091         /* convert back from a wide character count to a multibyte
1092          * character count...
1093          */
1094         pmb         = (char *)buffer;
1095         pwc         = wcBuffer;
1096         wcBufferLen = i;
1097         i           = 0;
1098         while (i < wcBufferLen)
1099         {
1100             switch (mbLen = mblen(pmb, MIN(((int)MB_CUR_MAX), length - i)))
1101             {
1102               case -1:
1103               case  0:
1104                 /* 
1105                 ** treat null character same as any other character...
1106                 */
1107                 mbLen = 1;
1108               default:
1109                 i   ++;
1110                 pmb += mbLen;
1111             }
1112         }
1113         XtFree((char *)wcBuffer);
1114         return(pmb - (char *) buffer);
1115     }
1116
1117     /* turn off the cursor... */
1118     if (CURSORoff != tpd->cursorState) {
1119         (void) _DtTermPrimCursorOff(w);
1120     }
1121
1122     /* we support two different types of autowrap.  The HP style one is where
1123      * you display the character and then wrap if you are at the end of the
1124      * line.  The ANSI style one is where you insert the character at the end
1125      * of the line and don't autowrap until you try to insert another
1126      * character...
1127      */
1128
1129     renderStartX = tpd->cursorColumn;
1130     renderEndX = tpd->cursorColumn;
1131     insertStartX = tpd->cursorColumn;
1132     insertCharCount = 0;
1133
1134     for (i = 0; (i < length) && tpd->ptyInputId; i++) {
1135         /* the following code performs two functions.  If we are in
1136          * autowrap, it performs a sanity check on the insert position.
1137          * if we are not in autowrap, it will insure that characters
1138          * inserted after the last position will replace the last
1139          * character...
1140          */
1141         if ((tpd->cursorColumn >= tw->term.columns) &&
1142                 !(tpd->autoWrapRight && !tpd->wrapRightAfterInsert)) {
1143             /* blow away the previous character...
1144              */
1145             tpd->cursorColumn = tw->term.columns - 1;
1146             renderStartX = MIN(renderStartX, tpd->cursorColumn);
1147             tpd->wrapState = WRAPpastRightMargin;
1148         }
1149
1150         /* for emulations that wrap after inserting the character, we
1151          * will insert the character and then check for wrap...
1152          */
1153         if (tpd->wrapRightAfterInsert) {
1154             if (insertCharCount == 0) {
1155                 insertStartX = i;
1156             }
1157             (void) insertCharCount++;
1158         }
1159
1160         if (((tpd->cursorColumn + insertCharCount) >= tw->term.columns) ||
1161                 ((tpd->cursorColumn + insertCharCount) ==
1162                 tpd->rightMargin + 1)) {
1163             if (tpd->autoWrapRight) {
1164                 /* perform an auto wrap...
1165                  */
1166                 /* we need to insert any pending characters, and
1167                  * render them...
1168                  */
1169                 if (insertCharCount) {
1170                     newWidth = DoInsert(w, &buffer[insertStartX],
1171                             insertCharCount, &wrapped);
1172                     tpd->cursorColumn += insertCharCount;
1173                     insertCharCount = 0;
1174                     if (tpd->insertCharMode != DtTERM_INSERT_CHAR_OFF) {
1175                         renderEndX = newWidth;
1176                     } else {
1177                         renderEndX = MAX(renderEndX, tpd->cursorColumn);
1178                     }
1179                     needToRender = True;
1180                 }
1181                 if (needToRender) {
1182                     DebugF('t', 0, fprintf(stderr,
1183                             ">>termInsertText() calling[2] _DtTermPrimRefreshText()\n"));
1184                     (void) _DtTermPrimRefreshText(w, renderStartX, tpd->cursorRow,
1185                             wrapped ? tw->term.columns : MAX(renderEndX, 0),
1186                             tpd->cursorRow);
1187                     if (wrapped && (tpd->cursorRow + 1 < tw->term.rows)) {
1188                         (void) _DtTermPrimRefreshText(w, 0, tpd->cursorRow + 1,
1189                                 renderEndX, tpd->cursorRow + 1);
1190                     }
1191                     wrapped = False;
1192                     needToRender = False;
1193                 }
1194                 tpd->cursorColumn = tpd->leftMargin;
1195                 tpd->wrapState = WRAPbetweenMargins;
1196                 renderEndX = 0;
1197
1198                 _DtTermPrimBufferSetLineWrapFlag(tBuffer,
1199                                                  tpd->topRow + tpd->cursorRow,
1200                                                  True);
1201
1202                 if (tpd->cursorRow == tpd->scrollLockBottomRow) {
1203                     /* scroll at the bottom of the lock area... */
1204                     (void) _DtTermPrimScrollText(w, 1);
1205                     (void) _DtTermPrimFillScreenGap(w);
1206                 } else if (++tpd->cursorRow >= tw->term.rows) {
1207                     /* we are at the bottom row of the locked region.
1208                      * Wrap to the beginning of this line...
1209                      */
1210                     tpd->cursorRow = tw->term.rows - 1;
1211                 } else {
1212                     /* we are not at the bottom row.  We already have
1213                      * wrapped down one line...
1214                      */
1215                 }
1216                     
1217                 renderStartX = tpd->cursorColumn;
1218
1219                 if (tpd->scroll.nojump.pendingScroll) {
1220                     /* If we wrapped the screen, bail out now and we
1221                      * will take care of this character when we
1222                      * finish the scroll.  If we are in wrap after,
1223                      * then we need to skip past this character so
1224                      * that it doesn't get processed twice...
1225                      */
1226                     if (tpd->wrapRightAfterInsert)
1227                         (void) i++;
1228                     break;
1229                 }
1230             } else {
1231                 /* overwrite the last character(s) on the line... */
1232                 if (insertCharCount > 0) {
1233                     newWidth = DoInsert(w, &buffer[insertStartX],
1234                             insertCharCount, &wrapped);
1235                     tpd->cursorColumn += insertCharCount;
1236                     insertCharCount = 0;
1237                     if (tpd->insertCharMode != DtTERM_INSERT_CHAR_OFF) {
1238                         renderEndX = newWidth;
1239                     } else {
1240                         renderEndX = MAX(renderEndX, tpd->cursorColumn);
1241                     }
1242                     needToRender = True;
1243                 }
1244
1245                 if (tpd->cursorColumn == tpd->rightMargin + 1)
1246                     tpd->cursorColumn = tpd->rightMargin;
1247                 else 
1248                     tpd->cursorColumn = tw->term.columns - 1;
1249             }
1250         }
1251
1252         /* for emulations that wrap before inserting the character, we
1253          * will insert the character and then check for wrap...
1254          */
1255         if (!tpd->wrapRightAfterInsert) {
1256             /* before we insert any text, we need to insure that the
1257              * cursor is on a valid buffer row...
1258              */
1259             if (insertCharCount == 0) {
1260                 insertStartX = i;
1261             }
1262             (void) insertCharCount++;
1263         }
1264     }
1265     /* insert and render any remaining text... */
1266     if (insertCharCount > 0) {
1267         newWidth = DoInsert(w, &buffer[insertStartX],
1268                 insertCharCount, &wrapped);
1269         tpd->cursorColumn += insertCharCount;
1270         insertCharCount = 0;
1271         if (tpd->insertCharMode != DtTERM_INSERT_CHAR_OFF) {
1272             renderEndX = newWidth;
1273         } else {
1274             renderEndX = MAX(renderEndX, tpd->cursorColumn);
1275         }
1276         needToRender = True;
1277     }
1278     if (needToRender) {
1279         renderEndX = MAX(renderEndX, tpd->cursorColumn);
1280         DebugF('t', 0, fprintf(stderr,
1281                 ">>termInsertText() calling _DtTermPrimRefreshText()\n"));
1282         (void) _DtTermPrimRefreshText(w, renderStartX, tpd->cursorRow,
1283                 wrapped ? tw->term.columns : MAX(renderEndX, 0),
1284                 tpd->cursorRow);
1285         if (wrapped && (tpd->cursorRow + 1 < tw->term.rows)) {
1286             (void) _DtTermPrimRefreshText(w, 0, tpd->cursorRow + 1,
1287                     renderEndX, tpd->cursorRow + 1);
1288         }
1289         wrapped = False;
1290         needToRender = False;
1291     }
1292
1293     return(i);
1294 }
1295
1296 static void
1297 buildDangleBuffer
1298 (
1299     unsigned char        *buffer,
1300     int                   bufferLen,
1301     unsigned char        *mbPartialChar,
1302     int                  *mbPartialCharLen,
1303     int                   writeLen,
1304     unsigned char       **dangleBuffer,
1305     int                  *dangleBufferLen
1306 )
1307 {
1308     /* malloc our dangle buffer... */
1309     *dangleBufferLen = bufferLen + *mbPartialCharLen - writeLen;
1310     *dangleBuffer = (unsigned char *) XtMalloc(*dangleBufferLen);
1311
1312     /* copy over the unwritten part of the orignal buffer... */
1313     (void) memmove(*dangleBuffer, buffer + writeLen, bufferLen - writeLen);
1314     if (*mbPartialCharLen) {
1315         (void) memmove(*dangleBuffer + bufferLen - writeLen, mbPartialChar,
1316                 *mbPartialCharLen);
1317         *mbPartialCharLen = 0;
1318     }
1319     return;
1320 }
1321
1322 Boolean
1323 _DtTermPrimParseInput
1324 (
1325     Widget                w,
1326     unsigned char        *buffer,
1327     int                   len,
1328     unsigned char       **dangleBuffer,
1329     int                  *dangleBufferLen
1330 )
1331 {
1332     DtTermPrimitiveWidget tw = (DtTermPrimitiveWidget) w;    
1333     struct termData *tpd = tw->term.tpd;
1334     DtTermPrimitiveClassPart     *termClassPart = &(((DtTermPrimitiveClassRec *)
1335             (tw->core.widget_class))->term_primitive_class);
1336     TermBuffer tBuffer = tpd->termBuffer;
1337     int i;
1338     short insertStart;
1339     short insertByteCount;
1340     short returnLen;
1341     Boolean turnCursorOn = False;
1342     unsigned char *tmpBuffer = (unsigned char *) 0;
1343     unsigned char mbChar[MB_LEN_MAX];
1344     int mbCharLen = 1;
1345     static Boolean *preParseTable = (Boolean *) 0;
1346
1347     *dangleBuffer = (unsigned char *) 0;
1348     insertStart = 0;
1349     insertByteCount = 0;
1350
1351     /* initialize the preParseTable if necessary... */
1352     _DtTermProcessLock();
1353     if (!preParseTable) {
1354         preParseTable = (Boolean *) XtMalloc(256 * sizeof(Boolean));
1355         (void) memset(preParseTable, '\0', 256 * sizeof(Boolean));
1356
1357         for (i = 0x00; i <= 0x1f; i++) {
1358             preParseTable[i] = True;
1359         }
1360         for (i = 0x80; i <= 0x9f; i++) {
1361             preParseTable[i] = True;
1362         }
1363     }
1364     _DtTermProcessUnlock();
1365
1366     /* check for partial multibyte character... */
1367     if (tpd->mbPartialCharLen > 0) {
1368         tmpBuffer = (unsigned char *) XtMalloc(len + tpd->mbPartialCharLen);
1369         (void) memcpy(tmpBuffer, tpd->mbPartialChar, tpd->mbPartialCharLen);
1370         (void) memcpy(tmpBuffer + tpd->mbPartialCharLen, buffer, len);
1371         buffer = tmpBuffer;
1372         len += tpd->mbPartialCharLen;
1373         tpd->mbPartialCharLen = 0;
1374     }
1375
1376     /* turn off the cursor... */
1377     if (CURSORoff != tpd->cursorState) {
1378         (void) _DtTermPrimCursorOff(w);
1379         turnCursorOn = True;
1380     }
1381
1382
1383     for (i = 0; (i < len) && tpd->ptyInputId; ) {
1384         if (tpd->mbCurMax > 1) {
1385             switch (mbCharLen = 
1386                     mblen((char *) &buffer[i], MIN(((int)MB_CUR_MAX), len - i)))
1387             {
1388               case -1:
1389                 if ((int)MB_CUR_MAX <= len - i)
1390                 {
1391                     /* we have a bogus multi-byte character.  Throw away
1392                      * the first byte and rescan (TM 12/14/93)...
1393                      */
1394                     /* dump what we know we want to insert... */
1395                     if (insertByteCount > 0) {
1396                         returnLen = (*(termClassPart->term_insert_proc))(w,
1397                                 &buffer[insertStart], insertByteCount);
1398                         if (returnLen != insertByteCount) {
1399                             (void) buildDangleBuffer(buffer, len,
1400                                         tpd->mbPartialChar,
1401                                         &tpd->mbPartialCharLen,
1402                                         insertStart + returnLen,
1403                                         dangleBuffer, dangleBufferLen);
1404
1405                             insertByteCount = 0;
1406                             break;
1407                         }
1408                         insertByteCount = 0;
1409                     }
1410                     /* skip over the bogus char's first byte... */
1411                     (void) i++;
1412                     insertStart = i;
1413                     continue;
1414                 } else {
1415                     /* we have a dangling partial multi-byte character... */
1416                     (void) memmove(tpd->mbPartialChar, &buffer[i], len - i);
1417                     tpd->mbPartialCharLen = len - i;
1418                     /* remove the partial char from the buffer and adjust
1419                      * the buffer len...
1420                      */
1421                     len = i;
1422                     continue;
1423                 }
1424                 break;
1425               case 0:
1426                 mbCharLen = 1;
1427                 /* fall through */
1428               default:
1429                 break;
1430             }
1431         }
1432
1433         if (((mbCharLen == 1) && preParseTable[buffer[i]]) ||
1434                 tpd->parserNotInStartState) {
1435             /* 
1436             ** It's either a control code or we are not in the start state.
1437             ** If we have any text to insert, insert
1438             ** the text, display any added text, then send it down through
1439             ** the parser.
1440             */
1441             if (insertByteCount > 0) {
1442                 returnLen = (*(termClassPart->term_insert_proc))(w,
1443                         &buffer[insertStart], insertByteCount);
1444                 if (returnLen != insertByteCount) {
1445                     (void) buildDangleBuffer(buffer, len,
1446                                 tpd->mbPartialChar,
1447                                 &tpd->mbPartialCharLen,
1448                                 insertStart + returnLen,
1449                                 dangleBuffer, dangleBufferLen);
1450                     insertByteCount = 0;
1451                     break;
1452                 }
1453                 insertByteCount = 0;
1454             }
1455             
1456             tpd->parserNotInStartState = _DtTermPrimParse(w, &buffer[i],
1457                     mbCharLen);
1458             i += mbCharLen;
1459             insertStart = i;
1460         } else {
1461             /* queue up text to insert into the buffer... */
1462             insertByteCount += mbCharLen;
1463             i += mbCharLen;
1464         }
1465     }
1466
1467     if (insertByteCount > 0)
1468     {
1469         returnLen = (*(termClassPart->term_insert_proc))(w,
1470                                                          &buffer[insertStart],
1471                                                          insertByteCount);
1472         if (returnLen != insertByteCount)
1473         {
1474             (void) buildDangleBuffer(buffer, len,
1475                                      tpd->mbPartialChar,
1476                                      &tpd->mbPartialCharLen,
1477                                      insertStart + returnLen,
1478                                      dangleBuffer, dangleBufferLen);
1479         }
1480     }
1481     else
1482     {
1483         /*
1484         ** insertByteCount <= 0, check to make sure we haven't
1485         ** already saved any remaining text in the dangleBuffer...
1486         */
1487         if (i < len && !*dangleBuffer)
1488         {
1489             (void) buildDangleBuffer(buffer, len,
1490                                      tpd->mbPartialChar,
1491                                      &tpd->mbPartialCharLen,
1492                                      i, dangleBuffer, dangleBufferLen);
1493         }
1494     }
1495
1496     /* if we turned the cursor off, turn the cursor back on... */
1497     if (turnCursorOn) {
1498         (void) _DtTermPrimCursorOn(w);
1499     }
1500
1501     if (tmpBuffer) {
1502         (void) XtFree((char *) tmpBuffer);
1503     }
1504     if (*dangleBuffer) {
1505         return(False);
1506     }
1507     return(True);
1508 }
1509
1510 /* 
1511 ** Pad the current line from the current end of line up to (and
1512 ** including) the current cursor column.
1513 */
1514 void
1515 _DtTermPrimRenderPadLine
1516 (
1517     Widget          w
1518 )
1519 {
1520     DtTermPrimitiveWidget          tw = (DtTermPrimitiveWidget) w;    
1521     struct termData    *tpd = tw->term.tpd;
1522     TermBuffer          tBuffer = tpd->termBuffer;
1523     short               currentWidth;
1524
1525     /* before we pad this line, we need to insure that cursor is on a
1526      * valid buffer row...
1527      */
1528     (void) _DtTermPrimFillScreenGap(w);
1529
1530     currentWidth = _DtTermPrimBufferGetLineWidth(tBuffer,
1531             tpd->topRow + tpd->cursorRow);
1532     if (tpd->cursorColumn >= currentWidth)
1533     {
1534         /* 
1535         ** Pad the line and refresh it.
1536         */
1537         if (tpd->mbCurMax > 1) {
1538             _DtTermPrimBufferPadLineWc(tBuffer, tpd->topRow + tpd->cursorRow,
1539                                      tpd->cursorColumn + 1);
1540         } else {
1541             _DtTermPrimBufferPadLine(tBuffer, tpd->topRow + tpd->cursorRow,
1542                                      tpd->cursorColumn + 1);
1543         }
1544         _DtTermPrimRefreshText(w, currentWidth, tpd->cursorRow, tpd->cursorColumn,
1545                    tpd->cursorRow);
1546     }
1547 }
1548
1549 void
1550 _DtTermPrimDestroyFont(
1551     Widget                w,
1552     TermFont              font
1553 )
1554 {
1555     if (font) (void) (*(font->destroyFunction))(w, font);
1556     return;
1557 }
1558
1559 void
1560 _DtTermPrimRenderText(
1561     Widget                w,
1562     TermFont              font,
1563     Pixel                 fg,
1564     Pixel                 bg,
1565     unsigned long         flags,
1566     int                   x,
1567     int                   y,
1568     unsigned char        *string,
1569     int                   len
1570 )
1571 {
1572     (void) (*(font->renderFunction))(w, font, fg, bg, flags, x, y, string, len);
1573     return;
1574 }