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