FreeBSD 10 clang port
[oweals/cde.git] / cde / lib / DtTerm / TermPrim / TermPrimRenderMb.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 #ifndef lint
24 #ifdef  VERBOSE_REV_INFO
25 static char rcs_id[] = "$XConsortium: TermPrimRenderMb.c /main/1 1996/04/21 19:19:05 drk $";
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 /* 
40 ** This file contains the multi-byte character versions of the render routines.
41 */
42
43 #include "TermHeader.h"
44 #include "TermPrimDebug.h"
45 #include "TermPrimP.h"
46 #include "TermPrimData.h"
47 #include "TermPrimLineDraw.h"
48 #include "TermPrimOSDepI.h"
49 #include "TermPrimBufferP.h"
50 #include "TermPrimRenderP.h"
51 #include "TermPrimSelect.h"
52 #include "TermPrimSelectP.h"
53 #include <limits.h>
54 #include <wchar.h>
55
56 void
57 _DtTermPrimRefreshTextWc(Widget w, short startColumn, short startRow,
58         short endColumn, short endRow)
59 {
60     DtTermPrimitiveWidget tw = (DtTermPrimitiveWidget) w;
61     struct termData *tpd = tw->term.tpd;
62     TermBuffer tBuffer = tpd->termBuffer;
63     short lineWidth;
64     short chunkLineWidth;
65     wchar_t *linePtr;
66     wchar_t *wc;
67     TermFont termFont;
68     int currentColorPair = 0;
69     int currentVideo = 0;
70     short chunkStartColumn;
71     short chunkWidth;
72     short chunkLength;
73     short thisStartColumn;
74     short thisEndColumn;
75     enhValues enhancements;
76     int i1;
77     int lineNum;
78     unsigned long valueMask;
79     GC gc;
80     XGCValues values;
81     TermEnhInfoRec enhInfo;
82     Boolean checkSelection = False;
83     Boolean inSelection;
84     int selectionEnd;
85     Pixel tmpPixel;
86     XmTextPosition  begin, end;
87     TermCharInfoRec startCharInfo;
88     
89     DebugF('t', 0, fprintf(stderr, ">>_DtTermPrimRefreshTextWc() starting\n"));
90     DebugF('t', 0, fprintf(stderr,
91             ">>_DtTermPrimRefreshTextWc() startCol=%hd  startRow=%hd  endCol=%hd  endRow=%hd\n",
92             startColumn, startRow, endColumn, endRow));
93
94     /* clip start/end x/y... */
95     if (startColumn <= 0)
96         startColumn = 0;
97     if (startRow <= 0)
98         startRow = 0;
99     if (endColumn >= tw->term.columns)
100         endColumn = tw->term.columns - 1;
101     if (endRow >= tw->term.rows)
102         endRow = tw->term.rows - 1;
103
104     /*
105     ** don't display if we are in jump scroll and in the process
106     ** of scrolling and inside the scroll region...
107     */
108     if (tw->term.jumpScroll && tpd->scroll.jump.scrolled) {
109         /* set all the scrollRefreshRows flags... */
110         for (; startRow <= endRow; startRow++) {
111             tpd->scrollRefreshRows[startRow] = True;
112         }
113
114         DebugF('t', 0, 
115                 fprintf(stderr,
116                 ">>_DtTermPrimRefreshTextWc() jump scroll in progress, no render\n"));
117         return;
118     }
119
120     if (!tpd->renderGC.gc) {
121         /* get a drawImageString GC... */
122         int i;
123         XGCValues values;
124
125         /***********************************************************
126          * renderGC...
127          */
128         /* set the GC fg and bg... */
129         values.foreground = tw->primitive.foreground;
130         values.background = tw->core.background_pixel;
131
132         tpd->renderGC.gc = XCreateGC(XtDisplay(w), XtWindow(w),
133                 GCForeground | GCBackground, &values);
134
135         /* set the GC cache values... */
136         tpd->renderGC.foreground = values.foreground;
137         tpd->renderGC.background = values.background;
138         tpd->renderGC.fid = (Font) 0;
139
140         /***********************************************************
141          * renderReverseGC...
142          */
143         values.foreground = tw->core.background_pixel;
144         values.background = tw->primitive.foreground;
145
146         tpd->renderReverseGC.gc = XCreateGC(XtDisplay(w), XtWindow(w),
147                 GCForeground | GCBackground, &values);
148
149         /* set the GC cache values... */
150         tpd->renderReverseGC.foreground = values.foreground;
151         tpd->renderReverseGC.background = values.background;
152         tpd->renderReverseGC.fid = (Font) 0;
153
154         /***********************************************************
155          * clearGC...
156          */
157         values.foreground = tw->core.background_pixel;
158         values.background = tw->primitive.foreground;
159         tpd->clearGC.gc = XCreateGC(XtDisplay(w), XtWindow(w),
160                 GCForeground | GCBackground, &values);
161
162         /* set the GC cache values... */
163         tpd->clearGC.foreground = values.foreground;
164         tpd->clearGC.background = values.background;
165         tpd->clearGC.fid = (Font) 0;
166     }
167
168     /* clip start/end x/y... */
169     if (endColumn >= tw->term.columns)
170         endColumn = tw->term.columns - 1;
171     if (endRow >= tw->term.rows)
172         endRow = tw->term.rows - 1;
173
174     for (; startRow <= endRow; startRow++) {
175         /* if we are refreshing a full line, then we can clear the
176          * scrollRefreshRows flag for this line...
177          */
178         if ((startColumn == 0) && (endColumn >= tw->term.columns - 1)) {
179             tpd->scrollRefreshRows[startRow] = False;
180         }
181
182         lineNum = startRow + tpd->topRow;
183
184         /* are we in the selected area?... */
185         if (tpd->useHistoryBuffer)
186         {
187             if (_DtTermPrimSelectGetSelection(w, &begin, &end) &&
188                 (begin < end) &&
189                 (lineNum >= (begin / (tpd->selectInfo->columns + 1)) - 
190                             tpd->lastUsedHistoryRow) &&
191                 (lineNum <= (end   / (tpd->selectInfo->columns + 1)) - 
192                             tpd->lastUsedHistoryRow)) {
193                 checkSelection = True;
194             } else {
195                 checkSelection = False;
196             }
197         }
198         else
199         {
200             if (_DtTermPrimSelectGetSelection(w, &begin, &end) &&
201                 (begin < end) &&
202                 (lineNum >= (begin / (tpd->selectInfo->columns + 1))) &&
203                 (lineNum <= (end   / (tpd->selectInfo->columns + 1))))  {
204                 checkSelection = True;
205             } else {
206                 checkSelection = False;
207             }
208         }
209
210         if (startColumn > endColumn) {
211             /* nothing to render on this line... */
212             continue;
213         }
214
215         if (lineNum >= tpd->lastUsedRow) {
216             /* we are pointing to empty screen space below the last used
217              * line of the display...
218              */
219             lineWidth       = 0;
220             linePtr         = (wchar_t) 0;
221             thisStartColumn = startColumn;
222             thisEndColumn   = endColumn;
223         } else if (lineNum < 0) {
224             if ((tpd->useHistoryBuffer) &&
225                     (-lineNum <= tpd->lastUsedHistoryRow)) {
226                 /* get a line out of the history buffer... */
227                 lineWidth = _DtTermPrimBufferGetLineWidth(tpd->historyBuffer,
228                         tpd->lastUsedHistoryRow + lineNum);
229                 if ((startColumn < lineWidth) &&
230                         _DtTermPrimGetCharacterInfo(tpd->historyBuffer,
231                         tpd->lastUsedHistoryRow + lineNum,
232                         startColumn, &startCharInfo)) {
233                     thisStartColumn = startCharInfo.startCol;
234                 } else {
235                     /* past the last character in this line... */
236                     thisStartColumn = startColumn;
237                 }
238
239                 /* get the line width and a pointer to the data... */
240                 thisEndColumn = endColumn;
241                 lineWidth = MAX(0, MIN(thisEndColumn - thisStartColumn + 1,
242                         lineWidth - startColumn));
243                 linePtr = startCharInfo.u.pwc;
244             } else {
245                 /* we are above the history buffer.  Should not happen, but...
246                  */
247                 lineWidth = 0;
248                 linePtr = (wchar_t) 0;
249             }
250         } else {
251             /* adjust startColumn to point to the first column for the
252              * first character...
253              */
254             lineWidth = _DtTermPrimBufferGetLineWidth(tBuffer, lineNum); 
255             if ((startColumn < lineWidth) &&
256                     _DtTermPrimGetCharacterInfo(tBuffer, lineNum, startColumn,
257                     &startCharInfo)) {
258                 thisStartColumn = startCharInfo.startCol;
259             } else {
260                 /* past the last character in this line... */
261                 thisStartColumn = startColumn;
262             }
263
264             /* get the line width and a pointer to the data... */
265             thisEndColumn = endColumn;
266             lineWidth = MAX(0, MIN(thisEndColumn - thisStartColumn + 1,
267                     lineWidth - thisStartColumn));
268             linePtr = startCharInfo.u.pwc;
269         }
270
271         chunkStartColumn = thisStartColumn;
272         while (lineWidth > 0) {
273             /* get the enhancement values for the first chunk of the
274              * string...
275              */
276             if (lineNum >= 0) {
277                 (void) _DtTermPrimBufferGetEnhancement(tBuffer,
278                                                 /* TermBuffer           */
279                         lineNum,                /* row                  */
280                         chunkStartColumn,       /* col                  */
281                         &enhancements,          /* enhancements         */
282                         &chunkWidth,            /* width                */
283                         countAll);              /* countWhich           */
284             } else {
285                 /* get it from the history buffer... */
286                 (void) _DtTermPrimBufferGetEnhancement(tpd->historyBuffer,
287                                                 /* TermBuffer           */
288                         tpd->lastUsedHistoryRow + lineNum,
289                                                 /* row                  */
290                         chunkStartColumn,       /* col                  */
291                         &enhancements,          /* enhancements         */
292                         &chunkWidth,            /* width                */
293                         countAll);              /* countWhich           */
294             }
295
296             /* clip chunkWidth... */
297             if (chunkWidth > lineWidth)
298                 chunkWidth = lineWidth;
299
300             /* Are we in the selection area?
301              * _DtTermPrimSelectIsInSelection clips the chunkWidth to
302              * the end of the selection if we are...
303              */
304             if (checkSelection && 
305                 _DtTermPrimSelectIsInSelection(w, lineNum, chunkStartColumn,
306                                          chunkWidth, &chunkWidth)) {
307                 inSelection = True;
308             } else {
309                 inSelection = False;
310             }
311
312             /* walk through the linePtr and figure out how many
313              * wide characters we need to render to fill out this
314              * chunk, and if the end of this chunk is in the middle
315              * of a wide character, bump the chunkWidth one column.
316              */
317             for (chunkLineWidth = 0, wc = linePtr;
318                     chunkLineWidth < chunkWidth;
319                     chunkLineWidth += i1, wc++) {
320                 if ((i1 = wcwidth(*wc)) <= 0) {
321                     /* take care of null characters... */
322                     i1 = 1;
323                 }
324             }
325             /* adjust for a multi-column character that spans the line
326              * boundary...
327              */
328             if (chunkLineWidth > chunkWidth) {
329                 chunkWidth = chunkLineWidth;
330             }
331             /* set reasonable defaults for our render info... */
332             enhInfo.fg = tw->primitive.foreground;
333             enhInfo.bg = tw->core.background_pixel;
334             enhInfo.font = tpd->defaultTermFont;
335             enhInfo.flags = (unsigned long) 0;
336
337             /* set our font and color from the enhancements... */
338             if (ENH_PROC(tBuffer)) {
339                 (void) (*(ENH_PROC(tBuffer)))(w, enhancements, &enhInfo);
340             }
341
342             /* are we in the selection area?... */
343             if (inSelection) {
344                 /* flip fg and bg... */
345                 tmpPixel = enhInfo.fg;
346                 enhInfo.fg = enhInfo.bg;
347                 enhInfo.bg = tmpPixel;
348             }
349                         
350             /* if secure, we will use a XFillRectangle, and we need
351              * foreground set to the background...
352              */
353
354             /* set the renderGC... */
355             valueMask = (unsigned long) 0;
356             if (TermIS_SECURE(enhInfo.flags)) {
357                 /* render secure video locally...
358                  */
359                 if (tpd->renderReverseGC.foreground != enhInfo.bg) {
360                     tpd->renderReverseGC.foreground = enhInfo.bg;
361                     values.foreground = enhInfo.bg;
362                     valueMask |= GCForeground;
363                 }
364                 if (valueMask) {
365                     (void) XChangeGC(XtDisplay(w), tpd->renderReverseGC.gc,
366                             valueMask, &values);
367                 }
368                 (void) XFillRectangle(XtDisplay(w),
369                         XtWindow(w),
370                         tpd->renderReverseGC.gc,
371                         chunkStartColumn * tpd->cellWidth + tpd->offsetX,
372                         startRow * tpd->cellHeight + tpd->offsetY,
373                         tpd->cellWidth *  chunkWidth,
374                         tpd->cellHeight);
375
376                 /* underline as well... */
377                 if (TermIS_UNDERLINE(enhInfo.flags)) {
378                     valueMask = (unsigned long) 0;
379                     if (tpd->renderGC.foreground != enhInfo.fg) {
380                         tpd->renderGC.foreground = enhInfo.fg;
381                         values.foreground = enhInfo.fg;
382                         valueMask |= GCForeground;
383                     }
384                     if (valueMask) {
385                         (void) XChangeGC(XtDisplay(w), tpd->renderGC.gc,
386                                 valueMask, &values);
387                     }
388                     (void) XDrawLine(XtDisplay(w),
389                                                 /* Display              */
390                             XtWindow(w),        /* Drawable             */
391                             tpd->renderGC.gc,   /* GC                   */
392                             chunkStartColumn * tpd->cellWidth + tpd->offsetX,
393                                                 /* X1                   */
394                             startRow * tpd->cellHeight + tpd->offsetY +
395                             tpd->cellHeight - 1,
396                                                 /* Y1                   */
397                             (chunkStartColumn + chunkWidth) * tpd->cellWidth +
398                             tpd->offsetX,       /* X2                   */
399                             startRow * tpd->cellHeight + tpd->offsetY +
400                             tpd->cellHeight - 1);
401                                                 /* Y2                   */
402                 }
403             } else {
404                 (void) _DtTermPrimRenderText(
405                     w,                          /* Widget               */
406                     enhInfo.font,               /* TermFont             */
407                     enhInfo.fg,                 /* fg Pixel             */
408                     enhInfo.bg,                 /* bg Pixel             */
409                     enhInfo.flags,              /* flags                */
410                     chunkStartColumn * tpd->cellWidth + tpd->offsetX,
411                                                 /* x                    */
412                     startRow * tpd->cellHeight + tpd->offsetY,
413                                                 /* y                    */
414                     /*DKS: this probably should be changed... */
415                     (termChar *) linePtr,       /* string               */
416                     wc - linePtr);              /* width                */
417             }
418             chunkStartColumn += chunkWidth;
419             lineWidth -= chunkWidth;
420             linePtr = wc;
421         }
422
423         /* clear any extra space in the line.  chunkStartColumn now points to
424          * the end of the line, and lineWidth == 0...
425          */
426         while (thisEndColumn - chunkStartColumn >= 0) {
427             chunkWidth = thisEndColumn - chunkStartColumn + 1;
428             if (checkSelection && 
429                 _DtTermPrimSelectIsInSelection(w, lineNum, chunkStartColumn,
430                                          chunkWidth, &chunkWidth)) {
431                 /* use the render gc set to the fg color... */
432                 gc = tpd->renderReverseGC.gc;
433
434                 valueMask = (unsigned long) 0;
435                 if (tpd->renderReverseGC.foreground !=
436                         tw->primitive.foreground) {
437                     tpd->renderReverseGC.foreground = tw->primitive.foreground;
438                     values.foreground = tw->primitive.foreground;
439                     valueMask |= GCForeground;
440                 }
441                 if (valueMask) {
442                     (void) XChangeGC(XtDisplay(w), tpd->renderReverseGC.gc,
443                             valueMask, &values);
444                 }
445             } else {
446                 /* use the clear GC... */
447                 gc = tpd->clearGC.gc;
448             }
449
450             if (isDebugFSet('t', 1)) {
451 #ifdef  BBA
452 #pragma BBA_IGNORE
453 #endif  /*BBA*/
454                 /* Fill in the clear area so we can see what is going to
455                  * be displayed...
456                  */
457                 (void) XFillRectangle(XtDisplay(w),
458                         XtWindow(w),
459                         tpd->renderGC.gc,
460                         chunkStartColumn * tpd->cellWidth + tpd->offsetX,
461                         startRow * tpd->cellHeight + tpd->offsetY,
462                         chunkWidth * tpd->cellWidth,
463                         tpd->cellHeight);
464                 (void) XSync(XtDisplay(w), False);
465                 (void) shortSleep(100000);
466             }
467             (void) XFillRectangle(XtDisplay(w),
468                                                 /* Display              */
469                     XtWindow(w),                /* Drawable             */
470                     gc,                         /* GC                   */
471                     chunkStartColumn * tpd->cellWidth + tpd->offsetX,
472                                                 /* x                    */
473                     startRow * tpd->cellHeight + tpd->offsetY,
474                                                 /* y                    */
475                     chunkWidth * tpd->cellWidth,
476                                                 /* width                */
477                     tpd->cellHeight);           /* height               */
478             chunkStartColumn += chunkWidth;
479         }
480     }
481     DebugF('t', 0, fprintf(stderr, ">>_DtTermPrimRefreshTextWc() finished\n"));
482 }
483
484
485 /**************************************************************************
486  *  Function:
487  *      _DtTermPrimExposeText(): expose (refresh) the text in rectangular area
488  *  
489  *  Parameters:
490  *      Widget w: widget to expose
491  *      int x: starting x pixel in window
492  *      int y: starting y pixel in window
493  *      int width: width in pixels
494  *      int height: height in pixels
495  *      Boolean isExposeEvent: true, deal with copyarea / expose overlap
496  *  
497  *  Returns:
498  *      <nothing>
499  *  
500  *  Notes:
501  *      This function will redisplay the text in a rectangular exposure
502  *      area.  The x, y, width, and height are in absolute pixels.  The
503  *      internal x and y offsets will be accounted for, and the minimum
504  *      number of characters will be displayed.  The algorithm has been
505  *      tuned (somewhat) and no longer exposes an extra character at the
506  *      end of the refresh lines and an extra line at the bottom of the
507  *      refresh area.  This can be verified by using the 't' and 'e'
508  *      debug flags.
509  */
510
511 void
512 _DtTermPrimExposeTextMb(Widget w, int x, int y, int width, int height,
513         Boolean isExposeEvent)
514 {
515     struct termData *tpd = ((DtTermPrimitiveWidget) w)->term.tpd;
516
517     DebugF('e', 0, fprintf(stderr, ">>exposeText() starting\n"));
518     DebugF('e', 0, fprintf(stderr,
519             ">>exposeText() startX=%d  startY=%d  width=%d  height=%d\n",
520             x, y, width, height));
521     DebugF('e', 0, fprintf(stderr,
522             ">>             offsetX=%d  offsetY=%d  cellHeight=%d  cellWidth=%d\n",
523             tpd->offsetX, tpd->offsetY, tpd->cellHeight, tpd->cellWidth));
524
525     /* The following "hack" takes care of the problem of an exposure event
526      * from the server and a copy area from the client crossing.  The
527      * combination of these two events can cause a race condition which
528      * manifests itself by leaving a hole in the terminal window.  What
529      * happens is this:
530      * 
531      * A window is partially obscured.  The terminal emulator does a copy
532      * area (scroll) which includes part of obscured area.  Before the
533      * server processes the copy area, the window is unobscured, and the
534      * server sends an exposure event back to the client.
535      * 
536      * - The window is partially obscured.
537      * 
538      * - The terminal emulator does a copy area (scroll) which includes a
539      *   portion of the obscured area.  Normally, the server will generate
540      *   a graphics exposure event for the obscured portion that it can't
541      *   copy which will allow the terminal emulator to update the area.
542      * 
543      * - Before the server receives the copy area request, the server
544      *   unobscures the window and sends an exposure event for the exposed
545      *   area.  (This is where the hack comes into play and refreshes the
546      *   scrolled portion of this area (and possibly some extra space as
547      *   well)).
548      * 
549      * - The server processes the copy area.  Since the area in question is
550      *   no longer obscured, the server will copy blank space and not
551      *   generate a graphics exposure event.
552      * 
553      * - The terminal emulator processes the exposure event and refreshes
554      *   the exposed area.  (This is the hack extends the exposure to cover
555      *   the gap).
556      * 
557      * - You now have a blank chunk of the terminal window where the
558      *   obscured area was scrolled (without the hack).
559      * 
560      * Our fix is similar to the one used in xterm.  The Motif text widget
561      * uses a more brute force method and simply extends the exposure event
562      * to the full height (or width) of the screen in the direction of the
563      * copy area.
564      */
565     if (isExposeEvent && tpd->scrollInProgress) {
566         int bothX1;
567         int bothX2;
568         int bothY1;
569         int bothY2;
570
571         bothX1 = MAX(tpd->scrollSrcX, x);
572         bothY1 = MAX(tpd->scrollSrcY, y);
573         bothX2 = MIN(tpd->scrollSrcX + tpd->scrollWidth, x + width - 1);
574         bothY2 = MIN(tpd->scrollSrcY + tpd->scrollHeight, y + height - 1);
575
576         if ((bothX2 > bothX1) && (bothY2 > bothY1)) {
577             (void) _DtTermPrimRefreshTextWc(w,
578                     (x - tpd->offsetX + tpd->scrollDestX - tpd->scrollSrcX) /
579                     tpd->cellWidth,
580                     (y - tpd->offsetY + tpd->scrollDestY - tpd->scrollSrcY) /
581                     tpd->cellHeight,
582                     (x + width - 1 - tpd->offsetX + tpd->scrollDestX -
583                     tpd->scrollSrcX) / tpd->cellWidth,
584                     (y + height - 1 - tpd->offsetY + tpd->scrollDestY -
585                     tpd->scrollSrcY) / tpd->cellHeight);
586         }
587     }
588
589     /* render the text... */
590     DebugF('t', 0, fprintf(stderr, ">>exposeText() calling _DtTermPrimRefreshTextWc()\n"));
591     (void) _DtTermPrimRefreshTextWc(w, (x - tpd->offsetX) / tpd->cellWidth,
592             (y - tpd->offsetY)  / tpd->cellHeight,
593             (x + width - 1 - tpd->offsetX) / tpd->cellWidth,
594             (y + height - 1 - tpd->offsetY) / tpd->cellHeight);
595
596     DebugF('e', 0, fprintf(stderr, ">>exposeText() finished\n"));
597 }
598
599 static short
600 DoInsertWc(Widget w, wchar_t *wcBuffer, int wcBufferLen, Boolean *wrapped)
601 {
602     DtTermPrimitiveWidget   tw = (DtTermPrimitiveWidget) w;    
603     struct termData        *tpd = tw->term.tpd;
604     TermBuffer              tBuffer = tpd->termBuffer;
605     short                   newWidth;
606     wchar_t                *returnChars;
607     short                   returnCount;
608     
609     /*
610     ** before we insert any text, we need to insure that the cursor is on
611     ** a valid buffer row...
612     */
613     if (tpd->topRow + tpd->cursorRow >= tpd->lastUsedRow)
614     {
615         (void) _DtTermPrimFillScreenGap(w);
616     }
617
618     /* insert the text... */
619     returnChars = (wchar_t *) XtMalloc(BUFSIZ * sizeof (wchar_t));
620     newWidth = _DtTermPrimBufferInsertWc(tBuffer,       /* TermBuffer       */
621             tpd->topRow + tpd->cursorRow,               /* row              */
622             tpd->cursorColumn,                          /* column           */
623             wcBuffer,                                   /* newChars         */
624             wcBufferLen,                                /* numChars         */
625             tpd->insertCharMode != DtTERM_INSERT_CHAR_OFF,  /* insert flag          */
626             &returnChars,                               /* return char ptr  */
627             &returnCount);                              /* return count ptr */
628
629     if ((tpd->insertCharMode != DtTERM_INSERT_CHAR_ON_WRAP) || (returnCount <= 0)) {
630         (void) XtFree((char *) returnChars);
631         return(newWidth);
632     }
633
634     /* we are in insert char mode with wrap and we wrapped text off of
635      * the line...
636      */
637     *wrapped = True;
638
639     /* wrap the inserted characters into the following line... */
640     if (tpd->topRow + tpd->cursorRow + 1 >= tpd->lastUsedRow) {
641         /* fake cursorRow... */
642         (void) tpd->cursorRow++;
643         (void) _DtTermPrimFillScreenGap(w);
644         (void) tpd->cursorRow--;
645     }
646
647     /*
648     ** Copy the returned characters to a temporary buffer so we don't have to
649     ** worry about _DtTermPrimBufferInsertWc tromping over its overflow buffer...
650     */
651     wcBufferLen = returnCount;
652     wcBuffer    = (wchar_t *)XtMalloc(wcBufferLen);
653     (void) memcpy(wcBuffer, returnChars, wcBufferLen * sizeof(wchar_t));
654
655     /*
656     ** insert the text into the next line...
657     */
658     newWidth = _DtTermPrimBufferInsertWc(tBuffer,       /* TermBuffer       */
659             tpd->topRow + tpd->cursorRow + 1,           /* row              */
660             0,                                          /* column           */
661             wcBuffer,                                   /* newChars         */
662             wcBufferLen,                                /* numChars         */
663             tpd->insertCharMode != DtTERM_INSERT_CHAR_OFF,  /* insert flag          */
664             &returnChars,                               /* return char ptr  */
665             &returnCount);                              /* return count ptr */
666     (void) XtFree((char *) wcBuffer);
667     (void) XtFree((char *) returnChars);
668     return(newWidth);
669 }
670
671 /* 
672 ** Insert the supplied text into the TermBuffer, and return a count of the
673 ** number of characters actually inserted.
674 */
675 int
676 _DtTermPrimInsertTextWc
677 (
678     Widget      w, 
679     wchar_t    *wcBuffer,   /* buffer of wide chars                    */
680     int         wcBufLen    /* # of wide chars in the buffer           */
681 )
682 {
683     DtTermPrimitiveWidget   tw      = (DtTermPrimitiveWidget) w;    
684     struct termData        *tpd     = tw->term.tpd;
685     TermBuffer              tBuffer = tpd->termBuffer;
686
687     int         i;
688     short       renderStartX;
689     short       renderEndX;
690     short       insertStartX;
691     short       insertCharCount;
692     short       insertCharWidth; /* column width of characters to insert */
693     short       thisCharWidth;
694     short       newWidth;
695     Boolean     needToRender = False;
696     Boolean     wrapped = False;
697
698     /* turn off the cursor... */
699     if (CURSORoff != tpd->cursorState) {
700         (void) _DtTermPrimCursorOff(w);
701     }
702
703     /* we support two different types of autowrap.  The HP style one is where
704      * you display the character and then wrap if you are at the end of the
705      * line.  The ANSI style one is where you insert the character at the end
706      * of the line and don't autowrap until you try to insert another
707      * character...
708      */
709
710     renderStartX    = tpd->cursorColumn;
711     renderEndX      = tpd->cursorColumn;
712     insertStartX    = tpd->cursorColumn;
713     insertCharCount = 0;
714     insertCharWidth = 0;
715
716     for (i = 0; (i < wcBufLen) && tpd->ptyInputId; i++) {
717         thisCharWidth = MAX(1, wcwidth(wcBuffer[i]));
718
719         /* the following code performs two functions.  If we are in
720          * autowrap, it performs a sanity check on the insert position.
721          * if we are not in autowrap, it will insure that characters
722          * inserted after the last position will replace the last
723          * character...
724          */
725         if (((tpd->cursorColumn + thisCharWidth) > tw->term.columns) &&
726                 !(tpd->autoWrapRight && !tpd->wrapRightAfterInsert)) {
727             /* blow away the previous character...
728              */
729             tpd->cursorColumn = tw->term.columns - thisCharWidth;
730             renderStartX = MIN(renderStartX, tpd->cursorColumn);
731             tpd->wrapState = WRAPpastRightMargin;
732         }
733
734         /* for emulations that wrap after inserting the character, we
735          * will insert the character and then check for wrap...
736          */
737         if (tpd->wrapRightAfterInsert) {
738             if (insertCharCount == 0) {
739                 insertStartX    = i;
740                 insertCharWidth = 0;
741             }
742             (void) insertCharCount++;
743             insertCharWidth += thisCharWidth;
744         }
745
746         if (((tpd->cursorColumn + insertCharWidth +
747                 (tpd->wrapRightAfterInsert ? 0: thisCharWidth)) >
748                 tw->term.columns) ||
749                 ((tpd->cursorColumn + insertCharWidth +
750                 (tpd->wrapRightAfterInsert ? 0 : (thisCharWidth - 1))) ==
751                 tpd->rightMargin + 1)) {
752             if (tpd->autoWrapRight) {
753                 /* perform an auto wrap...
754                  */
755                 /* we need to insert any pending characters, and
756                  * render them...
757                  */
758                 if (insertCharCount) {
759                     newWidth  = DoInsertWc(w, &wcBuffer[insertStartX],
760                                            insertCharCount, &wrapped);
761                     tpd->cursorColumn += insertCharWidth;
762                     insertCharCount = 0;
763                     if (tpd->insertCharMode == DtTERM_INSERT_CHAR_OFF) {
764                         renderEndX = MAX(renderEndX, tpd->cursorColumn);
765                     } else {
766                         renderEndX = newWidth;
767                     }
768                     needToRender = True;
769                 }
770                 if (needToRender) {
771                     DebugF('t', 0, fprintf(stderr,
772                             ">>termInsertText() calling[2] _DtTermPrimRefreshTextWc()\n"));
773                     (void) _DtTermPrimRefreshTextWc(w, renderStartX, tpd->cursorRow,
774                             wrapped ? tw->term.columns : MAX(renderEndX, 0),
775                             tpd->cursorRow);
776                     if (wrapped && (tpd->cursorRow + 1 < tw->term.rows)) {
777                         (void) _DtTermPrimRefreshTextWc(w, 0, tpd->cursorRow + 1,
778                                 renderEndX, tpd->cursorRow + 1);
779                     }
780                     wrapped = False;
781                     needToRender = False;
782                 }
783                 tpd->cursorColumn = tpd->leftMargin;
784                 tpd->wrapState = WRAPbetweenMargins;
785                 renderEndX = 0;
786
787                 _DtTermPrimBufferSetLineWrapFlag(tBuffer,
788                                                  tpd->topRow + tpd->cursorRow,
789                                                  True);
790
791                 if (tpd->cursorRow == tpd->scrollLockBottomRow) {
792                     /* scroll at the bottom of the lock area... */
793                     (void) _DtTermPrimScrollText(w, 1);
794                     (void) _DtTermPrimFillScreenGap(w);
795                 } else if (++tpd->cursorRow >= tw->term.rows) {
796                     /* we are at the bottom row of the locked region.
797                      * Wrap to the beginning of this line...
798                      */
799                     tpd->cursorRow = tw->term.rows - 1;
800                 } else {
801                     /* we are not at the bottom row.  We already have
802                      * wrapped down one line...
803                      */
804                 }
805                     
806                 renderStartX = tpd->cursorColumn;
807
808                 if (tpd->scroll.nojump.pendingScroll) {
809                     /* If we wrapped the screen, bail out now and we
810                      * will take care of this character when we
811                      * finish the scroll.  If we are in wrap after,
812                      * then we need to skip past this character so
813                      * that it doesn't get processed twice...
814                      */
815                     if (tpd->wrapRightAfterInsert)
816                         (void) i++;
817                     break;
818                 }
819             } else {
820                 /* overwrite the last character(s) on the line... */
821                 if (insertCharCount > 0) {
822                     newWidth  = DoInsertWc(w, &wcBuffer[insertStartX],
823                                            insertCharCount, &wrapped);
824                     tpd->cursorColumn += insertCharWidth;
825                     insertCharCount = 0;
826                     if (tpd->insertCharMode == DtTERM_INSERT_CHAR_OFF) {
827                         renderEndX = MAX(renderEndX, tpd->cursorColumn);
828                     } else {
829                         renderEndX = newWidth;
830                     }
831                     needToRender = True;
832                 }
833
834                 if (tpd->cursorColumn == tpd->rightMargin + 1)
835                     tpd->cursorColumn = tpd->rightMargin;
836                 else 
837                     tpd->cursorColumn = tw->term.columns - 1;
838             }
839         }
840
841         /* for emulations that wrap before inserting the character, we
842          * will insert the character and then check for wrap...
843          */
844         if (!tpd->wrapRightAfterInsert) {
845             /* before we insert any text, we need to insure that the
846              * cursor is on a valid buffer row...
847              */
848             if (insertCharCount == 0) {
849                 insertStartX    = i;
850                 insertCharWidth = 0;
851             }
852             (void) insertCharCount++;
853             insertCharWidth += thisCharWidth;
854         }
855     }
856     /* insert and render any remaining text... */
857     if (insertCharCount > 0) {
858         /*
859         ** The following code performs two functions.  If we are in
860         ** autowrap, it performs a sanity check on the insert position.
861         ** if we are not in autowrap, it will insure that characters
862         ** inserted after the last position will replace the last
863         ** character...
864         **
865         ** NOTE: 
866         **    This is only required in the case that we are trying to
867         **    overwrite the last character on the line with a two column
868         **    character...
869         */
870         if (insertCharCount == 1 &&
871             thisCharWidth   == 2 && 
872             ((tpd->cursorColumn + thisCharWidth) > tw->term.columns) &&
873             !(tpd->autoWrapRight && !tpd->wrapRightAfterInsert))
874         {
875             /*
876             ** blow away the previous character...
877             */
878             tpd->cursorColumn = tw->term.columns - thisCharWidth;
879             renderStartX = MIN(renderStartX, tpd->cursorColumn);
880             tpd->wrapState = WRAPpastRightMargin;
881         }
882         newWidth  = DoInsertWc(w, &wcBuffer[insertStartX],
883                                insertCharCount, &wrapped);
884         tpd->cursorColumn += insertCharWidth;
885         insertCharCount = 0;
886         if (tpd->insertCharMode == DtTERM_INSERT_CHAR_OFF) {
887             renderEndX = MAX(renderEndX, tpd->cursorColumn);
888         } else {
889             renderEndX = newWidth;
890         }
891         needToRender = True;
892     }
893     if (needToRender) {
894         renderEndX = MAX(renderEndX, tpd->cursorColumn);
895         DebugF('t', 0, fprintf(stderr,
896                 ">>termInsertText() calling _DtTermPrimRefreshTextWc()\n"));
897         (void) _DtTermPrimRefreshTextWc(w, renderStartX - 1, tpd->cursorRow,
898                 wrapped ? tw->term.columns : MAX(renderEndX + 1, 0),
899                 tpd->cursorRow);
900         if (wrapped && (tpd->cursorRow + 1 < tw->term.rows)) {
901             (void) _DtTermPrimRefreshTextWc(w, 0, tpd->cursorRow + 1,
902                     renderEndX + 1, tpd->cursorRow + 1);
903         }
904         wrapped = False;
905         needToRender = False;
906     }
907     return(i);
908 }