1 /* $XTermId: util.c,v 1.889 2021/11/08 23:56:52 tom Exp $ */
2
3 /*
4 * Copyright 1999-2020,2021 by Thomas E. Dickey
5 *
6 * All Rights Reserved
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the
10 * "Software"), to deal in the Software without restriction, including
11 * without limitation the rights to use, copy, modify, merge, publish,
12 * distribute, sublicense, and/or sell copies of the Software, and to
13 * permit persons to whom the Software is furnished to do so, subject to
14 * the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included
17 * in all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22 * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
23 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 *
27 * Except as contained in this notice, the name(s) of the above copyright
28 * holders shall not be used in advertising or otherwise to promote the
29 * sale, use or other dealings in this Software without prior written
30 * authorization.
31 *
32 *
33 * Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
34 *
35 * All Rights Reserved
36 *
37 * Permission to use, copy, modify, and distribute this software and its
38 * documentation for any purpose and without fee is hereby granted,
39 * provided that the above copyright notice appear in all copies and that
40 * both that copyright notice and this permission notice appear in
41 * supporting documentation, and that the name of Digital Equipment
42 * Corporation not be used in advertising or publicity pertaining to
43 * distribution of the software without specific, written prior permission.
44 *
45 *
46 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
47 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
48 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
49 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
50 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
51 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
52 * SOFTWARE.
53 */
54
55 /* util.c */
56
57 #include <xterm.h>
58
59 #include <data.h>
60 #include <error.h>
61 #include <menu.h>
62 #include <fontutils.h>
63 #include <xstrings.h>
64
65 #include <stdio.h>
66 #include <string.h>
67 #include <ctype.h>
68 #include <assert.h>
69
70 #if OPT_WIDE_CHARS
71 #if defined(HAVE_WCHAR_H) && defined(HAVE_WCWIDTH)
72 #include <wchar.h>
73 #endif
74 #include <wcwidth.h>
75 #endif
76
77 #ifdef HAVE_X11_EXTENSIONS_XINERAMA_H
78 #include <X11/extensions/Xinerama.h>
79 #endif /* HAVE_X11_EXTENSIONS_XINERAMA_H */
80
81 #include <graphics.h>
82
83 #define IncrementSavedLines(amount) \
84 if (screen->savedlines < screen->savelines) { \
85 if ((screen->savedlines += amount) > screen->savelines) \
86 screen->savedlines = screen->savelines; \
87 ScrollBarDrawThumb(xw, 1); \
88 }
89
90 static int handle_translated_exposure(XtermWidget xw,
91 int rect_x,
92 int rect_y,
93 int rect_width,
94 int rect_height);
95 static void ClearLeft(XtermWidget xw);
96 static void CopyWait(XtermWidget xw);
97 static void horizontal_copy_area(XtermWidget xw,
98 int firstchar,
99 int nchars,
100 int amount);
101 static void vertical_copy_area(XtermWidget xw,
102 int firstline,
103 int nlines,
104 int amount,
105 int left,
106 int right);
107
108 #if OPT_WIDE_CHARS
109 unsigned first_widechar;
110 int (*my_wcwidth) (wchar_t);
111 #endif
112
113 #if OPT_WIDE_CHARS
114 /*
115 * We will modify the 'n' cells beginning at the current position.
116 * Some of those cells may be part of multi-column characters, including
117 * carryover from the left. Find the limits of the multi-column characters
118 * that we should fill with blanks, return true if filling is needed.
119 */
120 int
DamagedCells(TScreen * screen,unsigned n,int * klp,int * krp,int row,int col)121 DamagedCells(TScreen *screen, unsigned n, int *klp, int *krp, int row, int col)
122 {
123 CLineData *ld = getLineData(screen, row);
124 int result = False;
125
126 assert(ld);
127 if (col < (int) ld->lineSize) {
128 int nn = (int) n;
129 int kl = col;
130 int kr = col + nn;
131
132 if (kr >= (int) ld->lineSize) {
133 nn = (ld->lineSize - col - 1);
134 kr = col + nn;
135 }
136
137 if (nn > 0) {
138 assert(kl < (int) ld->lineSize);
139 if (ld->charData[kl] == HIDDEN_CHAR) {
140 while (kl > 0) {
141 if (ld->charData[--kl] != HIDDEN_CHAR) {
142 break;
143 }
144 }
145 } else {
146 kl = col + 1;
147 }
148
149 assert(kr < (int) ld->lineSize);
150 if (ld->charData[kr] == HIDDEN_CHAR) {
151 while (kr < screen->max_col) {
152 assert((kr + 1) < (int) ld->lineSize);
153 if (ld->charData[++kr] != HIDDEN_CHAR) {
154 --kr;
155 break;
156 }
157 }
158 } else {
159 kr = col - 1;
160 }
161
162 if (klp)
163 *klp = kl;
164 if (krp)
165 *krp = kr;
166 result = (kr >= kl);
167 }
168 }
169
170 return result;
171 }
172
173 int
DamagedCurCells(TScreen * screen,unsigned n,int * klp,int * krp)174 DamagedCurCells(TScreen *screen, unsigned n, int *klp, int *krp)
175 {
176 return DamagedCells(screen, n, klp, krp, screen->cur_row, screen->cur_col);
177 }
178 #endif /* OPT_WIDE_CHARS */
179
180 /*
181 * These routines are used for the jump scroll feature
182 */
183 void
FlushScroll(XtermWidget xw)184 FlushScroll(XtermWidget xw)
185 {
186 TScreen *screen = TScreenOf(xw);
187 int i;
188 int shift = INX2ROW(screen, 0);
189 int bot = screen->max_row - shift;
190 int refreshtop;
191 int refreshheight;
192 int scrolltop;
193 int scrollheight;
194 int left = ScrnLeftMargin(xw);
195 int right = ScrnRightMargin(xw);
196 Boolean full_lines = (Boolean) ((left == 0) && (right == screen->max_col));
197
198 if (screen->cursor_state)
199 HideCursor(xw);
200
201 TRACE(("FlushScroll %s-lines scroll:%d refresh %d\n",
202 full_lines ? "full" : "partial",
203 screen->scroll_amt,
204 screen->refresh_amt));
205
206 if (screen->scroll_amt > 0) {
207 /*
208 * Lines will be scrolled "up".
209 */
210 refreshheight = screen->refresh_amt;
211 scrollheight = screen->bot_marg - screen->top_marg - refreshheight + 1;
212 refreshtop = screen->bot_marg - refreshheight + 1 + shift;
213 i = screen->max_row - screen->scroll_amt + 1;
214 if (refreshtop > i) {
215 refreshtop = i;
216 }
217
218 /*
219 * If this is the normal (not alternate) screen, and the top margin is
220 * at the top of the screen, then we will shift full lines scrolled out
221 * of the scrolling region into the saved-lines.
222 */
223 if (screen->scrollWidget
224 && !screen->whichBuf
225 && full_lines
226 && screen->top_marg == 0) {
227 scrolltop = 0;
228 scrollheight += shift;
229 if (scrollheight > i)
230 scrollheight = i;
231 i = screen->bot_marg - bot;
232 if (i > 0) {
233 refreshheight -= i;
234 if (refreshheight < screen->scroll_amt) {
235 refreshheight = screen->scroll_amt;
236 }
237 }
238 IncrementSavedLines(screen->scroll_amt);
239 } else {
240 scrolltop = screen->top_marg + shift;
241 i = bot - (screen->bot_marg - screen->refresh_amt + screen->scroll_amt);
242 if (i > 0) {
243 if (bot < screen->bot_marg) {
244 refreshheight = screen->scroll_amt + i;
245 }
246 } else {
247 scrollheight += i;
248 refreshheight = screen->scroll_amt;
249 i = screen->top_marg + screen->scroll_amt - 1 - bot;
250 if (i > 0) {
251 refreshtop += i;
252 refreshheight -= i;
253 }
254 }
255 }
256 } else {
257 /*
258 * Lines will be scrolled "down".
259 */
260 refreshheight = -screen->refresh_amt;
261 scrollheight = screen->bot_marg - screen->top_marg - refreshheight + 1;
262 refreshtop = screen->top_marg + shift;
263 scrolltop = refreshtop + refreshheight;
264 i = screen->bot_marg - bot;
265 if (i > 0) {
266 scrollheight -= i;
267 }
268 i = screen->top_marg + refreshheight - 1 - bot;
269 if (i > 0) {
270 refreshheight -= i;
271 }
272 }
273
274 vertical_copy_area(xw,
275 scrolltop + screen->scroll_amt,
276 scrollheight,
277 screen->scroll_amt,
278 left,
279 right);
280 ScrollSelection(screen, -(screen->scroll_amt), False);
281 screen->scroll_amt = 0;
282 screen->refresh_amt = 0;
283
284 if (refreshheight > 0) {
285 ClearCurBackground(xw,
286 refreshtop,
287 left,
288 (unsigned) refreshheight,
289 (unsigned) (right + 1 - left),
290 (unsigned) FontWidth(screen));
291 ScrnRefresh(xw,
292 refreshtop,
293 0,
294 refreshheight,
295 MaxCols(screen),
296 False);
297 }
298 xtermTimedDbe(xw);
299 return;
300 }
301
302 /*
303 * Returns true if there are lines off-screen due to scrolling which should
304 * include the current line. If false, the line is visible and we should
305 * paint it now rather than waiting for the line to become visible.
306 */
307 static Bool
AddToRefresh(XtermWidget xw)308 AddToRefresh(XtermWidget xw)
309 {
310 TScreen *screen = TScreenOf(xw);
311 int amount = screen->refresh_amt;
312 int row = screen->cur_row;
313 Bool result;
314
315 if (amount == 0) {
316 result = False;
317 } else if (amount > 0) {
318 int bottom;
319
320 if (row == (bottom = screen->bot_marg) - amount) {
321 screen->refresh_amt++;
322 result = True;
323 } else {
324 result = (row >= bottom - amount + 1 && row <= bottom);
325 }
326 } else {
327 int top;
328
329 amount = -amount;
330 if (row == (top = screen->top_marg) + amount) {
331 screen->refresh_amt--;
332 result = True;
333 } else {
334 result = (row <= top + amount - 1 && row >= top);
335 }
336 }
337
338 /*
339 * If this line is visible, and there are scrolled-off lines, flush out
340 * those which are now visible.
341 */
342 if (!result && screen->scroll_amt)
343 FlushScroll(xw);
344
345 return result;
346 }
347
348 /*
349 * Returns true if the current row is in the visible area (it should be for
350 * screen operations) and incidentally flush the scrolled-in lines which
351 * have newly become visible.
352 */
353 static Bool
AddToVisible(XtermWidget xw)354 AddToVisible(XtermWidget xw)
355 {
356 TScreen *screen = TScreenOf(xw);
357 Bool result = False;
358
359 if (INX2ROW(screen, screen->cur_row) <= screen->max_row) {
360 if (!AddToRefresh(xw)) {
361 result = True;
362 }
363 }
364 return result;
365 }
366
367 /*
368 * If we're scrolling, leave the selection intact if possible.
369 * If it will bump into one of the extremes of the saved-lines, truncate that.
370 * If the selection is not entirely contained within the margins and not
371 * entirely outside the margins, clear it.
372 */
373 static void
adjustHiliteOnFwdScroll(XtermWidget xw,int amount,Bool all_lines)374 adjustHiliteOnFwdScroll(XtermWidget xw, int amount, Bool all_lines)
375 {
376 TScreen *screen = TScreenOf(xw);
377 int lo_row = (all_lines
378 ? (screen->bot_marg - screen->savelines)
379 : screen->top_marg);
380 int hi_row = screen->bot_marg;
381 int left = ScrnLeftMargin(xw);
382 int right = ScrnRightMargin(xw);
383
384 TRACE2(("adjustSelection FWD %s by %d (%s)\n",
385 screen->whichBuf ? "alternate" : "normal",
386 amount,
387 all_lines ? "all" : "visible"));
388 TRACE2((" before highlite %d.%d .. %d.%d\n",
389 screen->startH.row,
390 screen->startH.col,
391 screen->endH.row,
392 screen->endH.col));
393 TRACE2((" margins %d..%d\n", screen->top_marg, screen->bot_marg));
394 TRACE2((" limits %d..%d\n", lo_row, hi_row));
395
396 if ((left > 0 || right < screen->max_col) &&
397 ((screen->startH.row >= lo_row &&
398 screen->startH.row - amount <= hi_row) ||
399 (screen->endH.row >= lo_row &&
400 screen->endH.row - amount <= hi_row))) {
401 /*
402 * This could be improved slightly by excluding the special case where
403 * the selection is on a single line outside left/right margins.
404 */
405 TRACE2(("deselect because selection overlaps with scrolled partial-line\n"));
406 ScrnDisownSelection(xw);
407 } else if (screen->startH.row >= lo_row
408 && screen->startH.row - amount < lo_row) {
409 /* truncate the selection because its start would move out of region */
410 if (lo_row + amount <= screen->endH.row) {
411 TRACE2(("truncate selection by changing start %d.%d to %d.%d\n",
412 screen->startH.row,
413 screen->startH.col,
414 lo_row + amount,
415 0));
416 screen->startH.row = lo_row + amount;
417 screen->startH.col = 0;
418 } else {
419 TRACE2(("deselect because %d.%d .. %d.%d shifted %d is outside margins %d..%d\n",
420 screen->startH.row,
421 screen->startH.col,
422 screen->endH.row,
423 screen->endH.col,
424 -amount,
425 lo_row,
426 hi_row));
427 ScrnDisownSelection(xw);
428 }
429 } else if (screen->startH.row <= hi_row && screen->endH.row > hi_row) {
430 TRACE2(("deselect because selection straddles top-margin\n"));
431 ScrnDisownSelection(xw);
432 } else if (screen->startH.row < lo_row && screen->endH.row > lo_row) {
433 TRACE2(("deselect because selection straddles bottom-margin\n"));
434 ScrnDisownSelection(xw);
435 }
436
437 TRACE2((" after highlite %d.%d .. %d.%d\n",
438 screen->startH.row,
439 screen->startH.col,
440 screen->endH.row,
441 screen->endH.col));
442 }
443
444 /*
445 * This is the same as adjustHiliteOnFwdScroll(), but reversed. In this case,
446 * only the visible lines are affected.
447 */
448 static void
adjustHiliteOnBakScroll(XtermWidget xw,int amount)449 adjustHiliteOnBakScroll(XtermWidget xw, int amount)
450 {
451 TScreen *screen = TScreenOf(xw);
452 int lo_row = screen->top_marg;
453 int hi_row = screen->bot_marg;
454
455 TRACE2(("adjustSelection BAK %s by %d (%s)\n",
456 screen->whichBuf ? "alternate" : "normal",
457 amount,
458 "visible"));
459 TRACE2((" before highlite %d.%d .. %d.%d\n",
460 screen->startH.row,
461 screen->startH.col,
462 screen->endH.row,
463 screen->endH.col));
464 TRACE2((" margins %d..%d\n", screen->top_marg, screen->bot_marg));
465
466 if (screen->endH.row >= hi_row
467 && screen->endH.row + amount > hi_row) {
468 /* truncate the selection because its start would move out of region */
469 if (hi_row - amount >= screen->startH.row) {
470 TRACE2(("truncate selection by changing start %d.%d to %d.%d\n",
471 screen->startH.row,
472 screen->startH.col,
473 hi_row - amount,
474 0));
475 screen->endH.row = hi_row - amount;
476 screen->endH.col = 0;
477 } else {
478 TRACE2(("deselect because %d.%d .. %d.%d shifted %d is outside margins %d..%d\n",
479 screen->startH.row,
480 screen->startH.col,
481 screen->endH.row,
482 screen->endH.col,
483 amount,
484 lo_row,
485 hi_row));
486 ScrnDisownSelection(xw);
487 }
488 } else if (screen->endH.row >= lo_row && screen->startH.row < lo_row) {
489 ScrnDisownSelection(xw);
490 } else if (screen->endH.row > hi_row && screen->startH.row > hi_row) {
491 ScrnDisownSelection(xw);
492 }
493
494 TRACE2((" after highlite %d.%d .. %d.%d\n",
495 screen->startH.row,
496 screen->startH.col,
497 screen->endH.row,
498 screen->endH.col));
499 }
500
501 /*
502 * Move cells in LineData's on the current screen to simulate scrolling by the
503 * given amount of lines.
504 */
505 static void
scrollInMargins(XtermWidget xw,int amount,int top)506 scrollInMargins(XtermWidget xw, int amount, int top)
507 {
508 TScreen *screen = TScreenOf(xw);
509 LineData *src;
510 LineData *dst;
511 int row;
512 int left = ScrnLeftMargin(xw);
513 int right = ScrnRightMargin(xw);
514 int length = right + 1 - left;
515
516 if_OPT_WIDE_CHARS(screen, {
517 if (amount != 0) {
518 for (row = top; row <= screen->bot_marg; ++row) {
519 LineData *ld;
520 if ((ld = getLineData(screen, row + amount)) != 0) {
521 if (left > 0) {
522 if (ld->charData[left] == HIDDEN_CHAR) {
523 Clear1Cell(ld, left - 1);
524 Clear1Cell(ld, left);
525 }
526 }
527 if (right + 1 < (int) ld->lineSize) {
528 if (ld->charData[right + 1] == HIDDEN_CHAR) {
529 Clear1Cell(ld, right);
530 Clear1Cell(ld, right + 1);
531 }
532 }
533 }
534 }
535 }
536 });
537
538 if (amount > 0) {
539 for (row = top; row <= screen->bot_marg - amount; ++row) {
540 if ((src = getLineData(screen, row + amount)) != 0
541 && (dst = getLineData(screen, row)) != 0) {
542 CopyCells(screen, src, dst, left, length, False);
543 }
544 }
545 while (row <= screen->bot_marg) {
546 ClearCells(xw, 0, (unsigned) length, row, left);
547 ++row;
548 }
549 } else if (amount < 0) {
550 for (row = screen->bot_marg; row >= top - amount; --row) {
551 if ((src = getLineData(screen, row + amount)) != 0
552 && (dst = getLineData(screen, row)) != 0) {
553 CopyCells(screen, src, dst, left, length, True);
554 }
555 }
556 while (row >= top) {
557 ClearCells(xw, 0, (unsigned) length, row, left);
558 --row;
559 }
560 }
561 }
562
563 #if OPT_WIDE_CHARS
564 /*
565 * If we're repainting a section of wide-characters that, e.g., ClearCells has
566 * repaired when finding double-cell characters, then we should account for
567 * that in the repaint.
568 */
569 static void
ScrnUpdate2(XtermWidget xw,int toprow,int leftcol,int nrows,int ncols,Bool force)570 ScrnUpdate2(XtermWidget xw,
571 int toprow,
572 int leftcol,
573 int nrows,
574 int ncols,
575 Bool force)
576 {
577 if_OPT_WIDE_CHARS(TScreenOf(xw), {
578 if (leftcol + ncols <= TScreenOf(xw)->max_col)
579 ncols++;
580 if (leftcol > 0) {
581 leftcol--;
582 ncols++;
583 }
584 });
585 ScrnUpdate(xw, toprow, leftcol, nrows, ncols, force);
586 }
587 #else
588 #define ScrnUpdate2(xw, toprow, leftcol, nrows, ncols, force) \
589 ScrnUpdate(xw, toprow, leftcol, nrows, ncols, force)
590 #endif
591
592 /*
593 * scrolls the screen by amount lines, erases bottom, doesn't alter
594 * cursor position (i.e. cursor moves down amount relative to text).
595 * All done within the scrolling region, of course.
596 * requires: amount > 0
597 */
598 void
xtermScroll(XtermWidget xw,int amount)599 xtermScroll(XtermWidget xw, int amount)
600 {
601 TScreen *screen = TScreenOf(xw);
602 int i;
603 int refreshtop = 0;
604 int refreshheight;
605 Boolean save_wrap = screen->do_wrap;
606 int left = ScrnLeftMargin(xw);
607 int right = ScrnRightMargin(xw);
608 Boolean scroll_all_lines = (Boolean) (screen->scrollWidget
609 && !screen->whichBuf
610 && screen->top_marg == 0);
611 Boolean scroll_full_line = ((left == 0) && (right == screen->max_col));
612
613 TRACE(("xtermScroll count=%d (top %d, saved %d)\n", amount,
614 screen->topline, screen->savelines));
615
616 screen->cursor_busy += 1;
617 screen->cursor_moved = True;
618
619 if (screen->cursor_state)
620 HideCursor(xw);
621
622 i = screen->bot_marg - screen->top_marg + 1;
623 if (amount > i)
624 amount = i;
625
626 if (!scroll_full_line) {
627 refreshheight = 0;
628 } else
629 #if OPT_SCROLL_LOCK
630 if ((screen->allowScrollLock && screen->scroll_lock)
631 || (screen->autoScrollLock && screen->topline < 0)) {
632 refreshheight = 0;
633 screen->scroll_amt = 0;
634 screen->refresh_amt = 0;
635 if (--(screen->topline) < -screen->savelines) {
636 screen->topline = -screen->savelines;
637 screen->scroll_dirty = True;
638 }
639 if (++(screen->savedlines) > screen->savelines) {
640 screen->savedlines = screen->savelines;
641 }
642 } else
643 #endif
644 {
645 if (ScrnHaveSelection(screen))
646 adjustHiliteOnFwdScroll(xw, amount, scroll_all_lines);
647
648 if (screen->jumpscroll) {
649 if (screen->scroll_amt > 0) {
650 if (!screen->fastscroll) {
651 if (screen->refresh_amt + amount > i)
652 FlushScroll(xw);
653 }
654 screen->scroll_amt += amount;
655 screen->refresh_amt += amount;
656 } else {
657 if (!screen->fastscroll) {
658 if (screen->scroll_amt < 0)
659 FlushScroll(xw);
660 }
661 screen->scroll_amt = amount;
662 screen->refresh_amt = amount;
663 }
664 refreshheight = 0;
665 } else {
666 int scrolltop;
667 int scrollheight;
668 int shift;
669 int bot;
670
671 ScrollSelection(screen, -(amount), False);
672 if (amount == i) {
673 ClearScreen(xw);
674 goto done;
675 }
676
677 shift = INX2ROW(screen, 0);
678 bot = screen->max_row - shift;
679 scrollheight = i - amount;
680 refreshheight = amount;
681
682 if ((refreshtop = screen->bot_marg - refreshheight + 1 + shift) >
683 (i = screen->max_row - refreshheight + 1))
684 refreshtop = i;
685
686 if (scroll_all_lines) {
687 scrolltop = 0;
688 if ((scrollheight += shift) > i)
689 scrollheight = i;
690 IncrementSavedLines(amount);
691 } else {
692 scrolltop = screen->top_marg + shift;
693 if ((i = screen->bot_marg - bot) > 0) {
694 scrollheight -= i;
695 if ((i = screen->top_marg + amount - 1 - bot) >= 0) {
696 refreshtop += i;
697 refreshheight -= i;
698 }
699 }
700 }
701
702 if (screen->multiscroll && amount == 1 &&
703 screen->topline == 0 && screen->top_marg == 0 &&
704 screen->bot_marg == screen->max_row) {
705 if (screen->incopy < 0 && screen->scrolls == 0)
706 CopyWait(xw);
707 screen->scrolls++;
708 }
709
710 vertical_copy_area(xw,
711 scrolltop + amount,
712 scrollheight,
713 amount,
714 left,
715 right);
716
717 if (refreshheight > 0) {
718 ClearCurBackground(xw,
719 refreshtop,
720 left,
721 (unsigned) refreshheight,
722 (unsigned) (right + 1 - left),
723 (unsigned) FontWidth(screen));
724 if (refreshheight > shift)
725 refreshheight = shift;
726 }
727 }
728 }
729
730 if (amount > 0) {
731 if (left > 0 || right < screen->max_col) {
732 scrollInMargins(xw, amount, screen->top_marg);
733 ScrnUpdate2(xw,
734 screen->top_marg,
735 left,
736 screen->bot_marg + 1 - screen->top_marg,
737 right + 1 - left,
738 True);
739 } else if (scroll_all_lines) {
740 ScrnDeleteLine(xw,
741 screen->saveBuf_index,
742 screen->bot_marg + screen->savelines,
743 0,
744 (unsigned) amount);
745 } else {
746 ScrnDeleteLine(xw,
747 screen->visbuf,
748 screen->bot_marg,
749 screen->top_marg,
750 (unsigned) amount);
751 }
752 }
753
754 scroll_displayed_graphics(xw, amount);
755
756 if (refreshheight > 0) {
757 ScrnRefresh(xw,
758 refreshtop,
759 left,
760 refreshheight,
761 right + 1 - left,
762 False);
763 }
764
765 done:
766 screen->do_wrap = save_wrap;
767 screen->cursor_busy -= 1;
768 TRACE(("...xtermScroll count=%d (top %d, saved %d)\n", amount,
769 screen->topline, screen->savelines));
770 return;
771 }
772
773 /*
774 * This is from ISO 6429, not found in any of DEC's terminals.
775 */
776 void
xtermScrollLR(XtermWidget xw,int amount,Bool toLeft)777 xtermScrollLR(XtermWidget xw, int amount, Bool toLeft)
778 {
779 if (amount > 0) {
780 xtermColScroll(xw, amount, toLeft, ScrnLeftMargin(xw));
781 }
782 }
783
784 /*
785 * Implement DECBI/DECFI (back/forward column index)
786 */
787 void
xtermColIndex(XtermWidget xw,Bool toLeft)788 xtermColIndex(XtermWidget xw, Bool toLeft)
789 {
790 TScreen *screen = TScreenOf(xw);
791
792 if (toLeft) {
793 if (ScrnIsColInMargins(screen, screen->cur_col)) {
794 if (screen->cur_col == ScrnLeftMargin(xw)) {
795 xtermColScroll(xw, 1, False, screen->cur_col);
796 } else {
797 CursorBack(xw, 1);
798 }
799 } else {
800 CursorBack(xw, 1);
801 }
802 } else {
803 if (ScrnIsColInMargins(screen, screen->cur_col)) {
804 if (screen->cur_col == ScrnRightMargin(xw)) {
805 xtermColScroll(xw, 1, True, ScrnLeftMargin(xw));
806 } else {
807 CursorForward(xw, 1);
808 }
809 } else {
810 CursorForward(xw, 1);
811 }
812 }
813 }
814
815 /*
816 * Implement DECDC/DECIC (delete/insert column)
817 */
818 void
xtermColScroll(XtermWidget xw,int amount,Bool toLeft,int at_col)819 xtermColScroll(XtermWidget xw, int amount, Bool toLeft, int at_col)
820 {
821 TScreen *screen = TScreenOf(xw);
822
823 if (amount > 0) {
824 int min_row;
825 int max_row;
826
827 if (ScrnHaveRowMargins(screen)) {
828 min_row = screen->top_marg;
829 max_row = screen->bot_marg;
830 } else {
831 min_row = 0;
832 max_row = screen->max_row;
833 }
834
835 if (screen->cur_row >= min_row
836 && screen->cur_row <= max_row
837 && screen->cur_col >= screen->lft_marg
838 && screen->cur_col <= screen->rgt_marg) {
839 int save_row = screen->cur_row;
840 int save_col = screen->cur_col;
841 int row;
842
843 screen->cur_col = at_col;
844 if (toLeft) {
845 for (row = min_row; row <= max_row; row++) {
846 screen->cur_row = row;
847 ScrnDeleteChar(xw, (unsigned) amount);
848 }
849 } else {
850 for (row = min_row; row <= max_row; row++) {
851 screen->cur_row = row;
852 ScrnInsertChar(xw, (unsigned) amount);
853 }
854 }
855 screen->cur_row = save_row;
856 screen->cur_col = save_col;
857 xtermRepaint(xw);
858 }
859 }
860 }
861
862 /*
863 * Reverse scrolls the screen by amount lines, erases top, doesn't alter
864 * cursor position (i.e. cursor moves up amount relative to text).
865 * All done within the scrolling region, of course.
866 * Requires: amount > 0
867 */
868 void
RevScroll(XtermWidget xw,int amount)869 RevScroll(XtermWidget xw, int amount)
870 {
871 TScreen *screen = TScreenOf(xw);
872 int i = screen->bot_marg - screen->top_marg + 1;
873 int left = ScrnLeftMargin(xw);
874 int right = ScrnRightMargin(xw);
875 Boolean scroll_full_line = ((left == 0) && (right == screen->max_col));
876
877 TRACE(("RevScroll count=%d\n", amount));
878
879 screen->cursor_busy += 1;
880 screen->cursor_moved = True;
881
882 if (screen->cursor_state)
883 HideCursor(xw);
884
885 if (amount > i)
886 amount = i;
887
888 if (ScrnHaveSelection(screen))
889 adjustHiliteOnBakScroll(xw, amount);
890
891 if (!scroll_full_line) {
892 ;
893 } else if (screen->jumpscroll) {
894 if (screen->scroll_amt < 0) {
895 if (-screen->refresh_amt + amount > i)
896 FlushScroll(xw);
897 screen->scroll_amt -= amount;
898 screen->refresh_amt -= amount;
899 } else {
900 if (screen->scroll_amt > 0)
901 FlushScroll(xw);
902 screen->scroll_amt = -amount;
903 screen->refresh_amt = -amount;
904 }
905 } else {
906 int shift = INX2ROW(screen, 0);
907 int bot = screen->max_row - shift;
908 int refreshheight = amount;
909 int refreshtop = screen->top_marg + shift;
910 int scrollheight = (screen->bot_marg
911 - screen->top_marg - refreshheight + 1);
912 int scrolltop = refreshtop + refreshheight;
913
914 if ((i = screen->bot_marg - bot) > 0)
915 scrollheight -= i;
916 if ((i = screen->top_marg + refreshheight - 1 - bot) > 0)
917 refreshheight -= i;
918
919 if (screen->multiscroll && amount == 1 &&
920 screen->topline == 0 && screen->top_marg == 0 &&
921 screen->bot_marg == screen->max_row) {
922 if (screen->incopy < 0 && screen->scrolls == 0)
923 CopyWait(xw);
924 screen->scrolls++;
925 }
926
927 vertical_copy_area(xw,
928 scrolltop - amount,
929 scrollheight,
930 -amount,
931 left,
932 right);
933
934 if (refreshheight > 0) {
935 ClearCurBackground(xw,
936 refreshtop,
937 left,
938 (unsigned) refreshheight,
939 (unsigned) (right + 1 - left),
940 (unsigned) FontWidth(screen));
941 }
942 }
943 if (amount > 0) {
944 if (left > 0 || right < screen->max_col) {
945 scrollInMargins(xw, -amount, screen->top_marg);
946 ScrnUpdate2(xw,
947 screen->top_marg,
948 left,
949 screen->bot_marg + 1 - screen->top_marg,
950 right + 1 - left,
951 True);
952 } else {
953 ScrnInsertLine(xw,
954 screen->visbuf,
955 screen->bot_marg,
956 screen->top_marg,
957 (unsigned) amount);
958 }
959 }
960 screen->cursor_busy -= 1;
961 return;
962 }
963
964 #if OPT_ZICONBEEP
965 void
initZIconBeep(void)966 initZIconBeep(void)
967 {
968 if (resource.zIconBeep > 100 || resource.zIconBeep < -100) {
969 resource.zIconBeep = 0; /* was 100, but I prefer to defaulting off. */
970 xtermWarning("a number between -100 and 100 is required for zIconBeep. 0 used by default\n");
971 }
972 }
973
974 static char *
getIconName(void)975 getIconName(void)
976 {
977 static char *icon_name;
978 static Arg args[] =
979 {
980 {XtNiconName, (XtArgVal) & icon_name}
981 };
982
983 icon_name = NULL;
984 XtGetValues(toplevel, args, XtNumber(args));
985 return icon_name;
986 }
987
988 static void
setZIconBeep(XtermWidget xw)989 setZIconBeep(XtermWidget xw)
990 {
991 TScreen *screen = TScreenOf(xw);
992
993 /* Flag icon name with "***" on window output when iconified.
994 */
995 if (resource.zIconBeep && mapstate == IsUnmapped && !screen->zIconBeep_flagged) {
996 char *icon_name = getIconName();
997 if (icon_name != NULL) {
998 screen->zIconBeep_flagged = True;
999 ChangeIconName(xw, icon_name);
1000 }
1001 xtermBell(xw, XkbBI_Info, 0);
1002 }
1003 mapstate = -1;
1004 }
1005
1006 /*
1007 * If warning should be given then give it
1008 */
1009 Boolean
showZIconBeep(XtermWidget xw,char * name)1010 showZIconBeep(XtermWidget xw, char *name)
1011 {
1012 Boolean code = False;
1013
1014 if (resource.zIconBeep && TScreenOf(xw)->zIconBeep_flagged) {
1015 char *format = resource.zIconFormat;
1016 char *newname = malloc(strlen(name) + strlen(format) + 2);
1017 if (!newname) {
1018 xtermWarning("malloc failed in showZIconBeep\n");
1019 } else {
1020 char *marker = strstr(format, "%s");
1021 char *result = newname;
1022 if (marker != 0) {
1023 size_t skip = (size_t) (marker - format);
1024 if (skip) {
1025 strncpy(result, format, skip);
1026 result += skip;
1027 }
1028 strcpy(result, name);
1029 strcat(result, marker + 2);
1030 } else {
1031 strcpy(result, format);
1032 strcat(result, name);
1033 }
1034 ChangeGroup(xw, XtNiconName, newname);
1035 free(newname);
1036 }
1037 code = True;
1038 }
1039 return code;
1040 }
1041
1042 /*
1043 * Restore the icon name, resetting the state for zIconBeep.
1044 */
1045 void
resetZIconBeep(XtermWidget xw)1046 resetZIconBeep(XtermWidget xw)
1047 {
1048 TScreen *screen = TScreenOf(xw);
1049
1050 if (screen->zIconBeep_flagged) {
1051 char *icon_name = getIconName();
1052 screen->zIconBeep_flagged = False;
1053 if (icon_name != NULL) {
1054 char *buf = malloc(strlen(icon_name) + 1);
1055 if (buf == NULL) {
1056 screen->zIconBeep_flagged = True;
1057 } else {
1058 char *format = resource.zIconFormat;
1059 char *marker = strstr(format, "%s");
1060 Boolean found = False;
1061
1062 if (marker != 0) {
1063 if (marker == format
1064 || !strncmp(icon_name, format, (size_t) (marker - format))) {
1065 found = True;
1066 strcpy(buf, icon_name + (marker - format));
1067 marker += 2;
1068 if (*marker != '\0') {
1069 size_t len_m = strlen(marker);
1070 size_t len_b = strlen(buf);
1071 if (len_m < len_b
1072 && !strcmp(buf + len_b - len_m, marker)) {
1073 buf[len_b - len_m] = '\0';
1074 }
1075 }
1076 }
1077 } else if (!strncmp(icon_name, format, strlen(format))) {
1078 strcpy(buf, icon_name + strlen(format));
1079 found = True;
1080 }
1081 if (found)
1082 ChangeIconName(xw, buf);
1083 free(buf);
1084 }
1085 }
1086 }
1087 }
1088 #else
1089 #define setZIconBeep(xw) /* nothing */
1090 #endif /* OPT_ZICONBEEP */
1091
1092 /*
1093 * write a string str of length len onto the screen at
1094 * the current cursor position. update cursor position.
1095 */
1096 void
WriteText(XtermWidget xw,IChar * str,Cardinal len)1097 WriteText(XtermWidget xw, IChar *str, Cardinal len)
1098 {
1099 TScreen *screen = TScreenOf(xw);
1100 XTermDraw params;
1101 CLineData *ld = 0;
1102 unsigned attr_flags = xw->flags;
1103 CellColor fg_bg = xtermColorPair(xw);
1104 unsigned cells = visual_width(str, len);
1105 GC currentGC;
1106
1107 TRACE(("WriteText %d (%2d,%2d) %3d:%s\n",
1108 screen->topline,
1109 screen->cur_row,
1110 screen->cur_col,
1111 len, visibleIChars(str, len)));
1112
1113 if (cells + (unsigned) screen->cur_col > (unsigned) MaxCols(screen)) {
1114 cells = (unsigned) (MaxCols(screen) - screen->cur_col);
1115 }
1116
1117 if (ScrnHaveSelection(screen)
1118 && ScrnIsRowInSelection(screen, INX2ROW(screen, screen->cur_row))) {
1119 ScrnDisownSelection(xw);
1120 }
1121 #if OPT_ISO_COLORS
1122 /* if colorBDMode is set, and enabled */
1123 if (screen->colorBDMode &&
1124 screen->boldColors &&
1125 !hasDirectFG(attr_flags) &&
1126 /* and bold foreground color on bold background color */
1127 GetCellColorFG(fg_bg) > COLOR_7 &&
1128 GetCellColorFG(fg_bg) < MIN_ANSI_COLORS &&
1129 /* and both colors are the same */
1130 GetCellColorFG(fg_bg) == GetCellColorBG(fg_bg))
1131 /* clear BOLD flag, else it will be colorBD on bold background color */
1132 UIntClr(attr_flags, BOLD);
1133 #endif
1134
1135 /* if we are in insert-mode, reserve space for the new cells */
1136 if (attr_flags & INSERT) {
1137 InsertChar(xw, cells);
1138 }
1139
1140 if (AddToVisible(xw)
1141 && ((ld = getLineData(screen, screen->cur_row))) != 0) {
1142 unsigned test;
1143
1144 if (screen->cursor_state)
1145 HideCursor(xw);
1146
1147 /*
1148 * If we overwrite part of a multi-column character, fill the rest
1149 * of it with blanks.
1150 */
1151 if_OPT_WIDE_CHARS(screen, {
1152 int kl;
1153 int kr;
1154 if (DamagedCurCells(screen, cells, &kl, &kr))
1155 ClearInLine(xw, screen->cur_row, kl, (unsigned) (kr - kl + 1));
1156 });
1157
1158 if (attr_flags & INVISIBLE) {
1159 Cardinal n;
1160 for (n = 0; n < cells; ++n)
1161 str[n] = ' ';
1162 }
1163
1164 TRACE(("WriteText calling drawXtermText (%d) (%d,%d)\n",
1165 LineCharSet(screen, ld),
1166 screen->cur_row,
1167 screen->cur_col));
1168
1169 test = attr_flags;
1170 #if OPT_ISO_COLORS
1171 {
1172 int fg;
1173 if (screen->colorAttrMode) {
1174 fg = MapToColorMode(xw->cur_foreground, screen, attr_flags);
1175 } else {
1176 fg = xw->cur_foreground;
1177 }
1178 checkVeryBoldColors(test, fg);
1179 }
1180 #endif
1181
1182 /* make sure that the correct GC is current */
1183 currentGC = updatedXtermGC(xw, attr_flags, fg_bg, False);
1184
1185 /* *INDENT-EQLS* */
1186 params.xw = xw;
1187 params.attr_flags = (test & DRAWX_MASK);
1188 params.draw_flags = 0;
1189 params.this_chrset = LineCharSet(screen, ld);
1190 params.real_chrset = CSET_SWL;
1191 params.on_wide = 0;
1192
1193 drawXtermText(¶ms,
1194 currentGC,
1195 LineCursorX(screen, ld, screen->cur_col),
1196 CursorY(screen, screen->cur_row),
1197 str, len);
1198
1199 resetXtermGC(xw, attr_flags, False);
1200 }
1201
1202 ScrnWriteText(xw, str, attr_flags, fg_bg, len);
1203 CursorForward(xw, (int) cells);
1204 setZIconBeep(xw);
1205 return;
1206 }
1207
1208 /*
1209 * If cursor not in scrolling region, returns. Else,
1210 * inserts n blank lines at the cursor's position. Lines above the
1211 * bottom margin are lost.
1212 */
1213 void
InsertLine(XtermWidget xw,int n)1214 InsertLine(XtermWidget xw, int n)
1215 {
1216 TScreen *screen = TScreenOf(xw);
1217 int i;
1218 int left = ScrnLeftMargin(xw);
1219 int right = ScrnRightMargin(xw);
1220 Boolean scroll_full_line = ((left == 0) && (right == screen->max_col));
1221
1222 if (!ScrnIsRowInMargins(screen, screen->cur_row)
1223 || screen->cur_col < left
1224 || screen->cur_col > right)
1225 return;
1226
1227 TRACE(("InsertLine count=%d\n", n));
1228
1229 set_cur_col(screen, ScrnLeftMargin(xw));
1230 if (screen->cursor_state)
1231 HideCursor(xw);
1232
1233 if (ScrnHaveSelection(screen)
1234 && ScrnAreRowsInSelection(screen,
1235 INX2ROW(screen, screen->top_marg),
1236 INX2ROW(screen, screen->cur_row - 1))
1237 && ScrnAreRowsInSelection(screen,
1238 INX2ROW(screen, screen->cur_row),
1239 INX2ROW(screen, screen->bot_marg))) {
1240 ScrnDisownSelection(xw);
1241 }
1242
1243 ResetWrap(screen);
1244 if (n > (i = screen->bot_marg - screen->cur_row + 1))
1245 n = i;
1246 if (screen->jumpscroll && scroll_full_line) {
1247 if (screen->scroll_amt <= 0 &&
1248 screen->cur_row <= -screen->refresh_amt) {
1249 if (-screen->refresh_amt + n > MaxRows(screen))
1250 FlushScroll(xw);
1251 screen->scroll_amt -= n;
1252 screen->refresh_amt -= n;
1253 } else {
1254 if (screen->scroll_amt)
1255 FlushScroll(xw);
1256 }
1257 }
1258 if (!screen->scroll_amt && scroll_full_line) {
1259 int shift = INX2ROW(screen, 0);
1260 int bot = screen->max_row - shift;
1261 int refreshheight = n;
1262 int refreshtop = screen->cur_row + shift;
1263 int scrolltop = refreshtop + refreshheight;
1264 int scrollheight = (screen->bot_marg
1265 - screen->cur_row - refreshheight + 1);
1266
1267 if ((i = screen->bot_marg - bot) > 0)
1268 scrollheight -= i;
1269 if ((i = screen->cur_row + refreshheight - 1 - bot) > 0)
1270 refreshheight -= i;
1271 vertical_copy_area(xw, scrolltop - n, scrollheight, -n, left, right);
1272 if (refreshheight > 0) {
1273 ClearCurBackground(xw,
1274 refreshtop,
1275 left,
1276 (unsigned) refreshheight,
1277 (unsigned) (right + 1 - left),
1278 (unsigned) FontWidth(screen));
1279 }
1280 }
1281 if (n > 0) {
1282 if (scroll_full_line) {
1283 ScrnInsertLine(xw,
1284 screen->visbuf,
1285 screen->bot_marg,
1286 screen->cur_row,
1287 (unsigned) n);
1288 } else {
1289 scrollInMargins(xw, -n, screen->cur_row);
1290 ScrnUpdate2(xw,
1291 screen->cur_row,
1292 left,
1293 screen->bot_marg + 1 - screen->cur_row,
1294 right + 1 - left,
1295 True);
1296 }
1297 }
1298 }
1299
1300 /*
1301 * If cursor not in scrolling region, returns. Else, deletes n lines
1302 * at the cursor's position, lines added at bottom margin are blank.
1303 */
1304 void
DeleteLine(XtermWidget xw,int n,Bool canSave)1305 DeleteLine(XtermWidget xw, int n, Bool canSave)
1306 {
1307 TScreen *screen = TScreenOf(xw);
1308 int i;
1309 int left = ScrnLeftMargin(xw);
1310 int right = ScrnRightMargin(xw);
1311 Boolean scroll_all_lines = (Boolean) (screen->scrollWidget
1312 && !screen->whichBuf
1313 && screen->cur_row == 0);
1314 Boolean scroll_full_line = ((left == 0) && (right == screen->max_col));
1315
1316 if (!ScrnIsRowInMargins(screen, screen->cur_row) ||
1317 !ScrnIsColInMargins(screen, screen->cur_col))
1318 return;
1319
1320 TRACE(("DeleteLine count=%d\n", n));
1321
1322 set_cur_col(screen, ScrnLeftMargin(xw));
1323 if (screen->cursor_state)
1324 HideCursor(xw);
1325
1326 if (n > (i = screen->bot_marg - screen->cur_row + 1)) {
1327 n = i;
1328 }
1329 if (ScrnHaveSelection(screen)
1330 && ScrnAreRowsInSelection(screen,
1331 INX2ROW(screen, screen->cur_row),
1332 INX2ROW(screen, screen->cur_row + n - 1))) {
1333 ScrnDisownSelection(xw);
1334 }
1335
1336 ResetWrap(screen);
1337 if (screen->jumpscroll && scroll_full_line) {
1338 if (screen->scroll_amt >= 0 && screen->cur_row == screen->top_marg) {
1339 if (screen->refresh_amt + n > MaxRows(screen))
1340 FlushScroll(xw);
1341 if (canSave) {
1342 screen->scroll_amt += n;
1343 screen->refresh_amt += n;
1344 }
1345 } else {
1346 if (screen->scroll_amt)
1347 FlushScroll(xw);
1348 }
1349 }
1350
1351 /* adjust screen->buf */
1352 if (n > 0) {
1353 if (left > 0 || right < screen->max_col) {
1354 scrollInMargins(xw, n, screen->cur_row);
1355 } else if (canSave && scroll_all_lines) {
1356 ScrnDeleteLine(xw,
1357 screen->saveBuf_index,
1358 screen->bot_marg + screen->savelines,
1359 0,
1360 (unsigned) n);
1361 } else {
1362 ScrnDeleteLine(xw,
1363 screen->visbuf,
1364 screen->bot_marg,
1365 screen->cur_row,
1366 (unsigned) n);
1367 }
1368 }
1369
1370 /* repaint the screen, as needed */
1371 if (!scroll_full_line) {
1372 ScrnUpdate2(xw,
1373 screen->cur_row,
1374 left,
1375 screen->bot_marg + 1 - screen->cur_row,
1376 right + 1 - left,
1377 True);
1378 } else if (!screen->scroll_amt) {
1379 int shift = INX2ROW(screen, 0);
1380 int bot = screen->max_row - shift;
1381 int refreshtop;
1382 int refreshheight = n;
1383 int scrolltop;
1384 int scrollheight = i - n;
1385
1386 if ((refreshtop = screen->bot_marg - refreshheight + 1 + shift) >
1387 (i = screen->max_row - refreshheight + 1))
1388 refreshtop = i;
1389 if (canSave && scroll_all_lines) {
1390 scrolltop = 0;
1391 if ((scrollheight += shift) > i)
1392 scrollheight = i;
1393 IncrementSavedLines(n);
1394 } else {
1395 scrolltop = screen->cur_row + shift;
1396 if ((i = screen->bot_marg - bot) > 0) {
1397 scrollheight -= i;
1398 if ((i = screen->cur_row + n - 1 - bot) >= 0) {
1399 refreshheight -= i;
1400 }
1401 }
1402 }
1403 vertical_copy_area(xw, scrolltop + n, scrollheight, n, left, right);
1404 if (shift > 0 && refreshheight > 0) {
1405 int rows = refreshheight;
1406 if (rows > shift)
1407 rows = shift;
1408 ScrnUpdate(xw, refreshtop, 0, rows, MaxCols(screen), True);
1409 refreshtop += shift;
1410 refreshheight -= shift;
1411 }
1412 if (refreshheight > 0) {
1413 ClearCurBackground(xw,
1414 refreshtop,
1415 left,
1416 (unsigned) refreshheight,
1417 (unsigned) (right + 1 - left),
1418 (unsigned) FontWidth(screen));
1419 }
1420 }
1421 }
1422
1423 /*
1424 * Insert n blanks at the cursor's position, no wraparound
1425 */
1426 void
InsertChar(XtermWidget xw,unsigned n)1427 InsertChar(XtermWidget xw, unsigned n)
1428 {
1429 TScreen *screen = TScreenOf(xw);
1430 CLineData *ld;
1431 unsigned limit;
1432 int row = INX2ROW(screen, screen->cur_row);
1433 int left = ScrnLeftMargin(xw);
1434 int right = ScrnRightMargin(xw);
1435
1436 if (screen->cursor_state)
1437 HideCursor(xw);
1438
1439 TRACE(("InsertChar count=%d\n", n));
1440
1441 if (ScrnHaveSelection(screen)
1442 && ScrnIsRowInSelection(screen, row)) {
1443 ScrnDisownSelection(xw);
1444 }
1445 ResetWrap(screen);
1446
1447 limit = (unsigned) (right + 1 - screen->cur_col);
1448
1449 if (n > limit)
1450 n = limit;
1451
1452 if (screen->cur_col < left || screen->cur_col > right) {
1453 n = 0;
1454 } else if (AddToVisible(xw)
1455 && (ld = getLineData(screen, screen->cur_row)) != 0) {
1456 int col = right + 1 - (int) n;
1457
1458 /*
1459 * If we shift part of a multi-column character, fill the rest
1460 * of it with blanks. Do similar repair for the text which will
1461 * be shifted into the right-margin.
1462 */
1463 if_OPT_WIDE_CHARS(screen, {
1464 int kl;
1465 int kr = screen->cur_col;
1466 if (DamagedCurCells(screen, n, &kl, (int *) 0) && kr > kl) {
1467 ClearInLine(xw, screen->cur_row, kl, (unsigned) (kr - kl + 1));
1468 }
1469 kr = screen->max_col - (int) n + 1;
1470 if (DamagedCells(screen, n, &kl, (int *) 0,
1471 screen->cur_row,
1472 kr) && kr > kl) {
1473 ClearInLine(xw, screen->cur_row, kl, (unsigned) (kr - kl + 1));
1474 }
1475 });
1476
1477 #if OPT_DEC_CHRSET
1478 if (CSET_DOUBLE(GetLineDblCS(ld))) {
1479 col = MaxCols(screen) / 2 - (int) n;
1480 }
1481 #endif
1482 /*
1483 * prevent InsertChar from shifting the end of a line over
1484 * if it is being appended to
1485 */
1486 if (non_blank_line(screen, screen->cur_row,
1487 screen->cur_col, MaxCols(screen))) {
1488 horizontal_copy_area(xw, screen->cur_col,
1489 col - screen->cur_col,
1490 (int) n);
1491 }
1492
1493 ClearCurBackground(xw,
1494 INX2ROW(screen, screen->cur_row),
1495 screen->cur_col,
1496 1U,
1497 n,
1498 (unsigned) LineFontWidth(screen, ld));
1499 }
1500 if (n != 0) {
1501 /* adjust screen->buf */
1502 ScrnInsertChar(xw, n);
1503 }
1504 }
1505
1506 /*
1507 * Deletes n chars at the cursor's position, no wraparound.
1508 */
1509 void
DeleteChar(XtermWidget xw,unsigned n)1510 DeleteChar(XtermWidget xw, unsigned n)
1511 {
1512 TScreen *screen = TScreenOf(xw);
1513 CLineData *ld;
1514 unsigned limit;
1515 int row = INX2ROW(screen, screen->cur_row);
1516 int right = ScrnRightMargin(xw);
1517
1518 if (screen->cursor_state)
1519 HideCursor(xw);
1520
1521 if (!ScrnIsColInMargins(screen, screen->cur_col))
1522 return;
1523
1524 TRACE(("DeleteChar count=%d\n", n));
1525
1526 if (ScrnHaveSelection(screen)
1527 && ScrnIsRowInSelection(screen, row)) {
1528 ScrnDisownSelection(xw);
1529 }
1530 ResetWrap(screen);
1531
1532 limit = (unsigned) (right + 1 - screen->cur_col);
1533
1534 if (n > limit)
1535 n = limit;
1536
1537 if (AddToVisible(xw)
1538 && (ld = getLineData(screen, screen->cur_row)) != 0) {
1539 int col = right + 1 - (int) n;
1540
1541 /*
1542 * If we delete part of a multi-column character, fill the rest
1543 * of it with blanks.
1544 */
1545 if_OPT_WIDE_CHARS(screen, {
1546 int kl;
1547 int kr;
1548 if (DamagedCurCells(screen, n, &kl, &kr))
1549 ClearInLine(xw, screen->cur_row, kl, (unsigned) (kr - kl + 1));
1550 });
1551
1552 #if OPT_DEC_CHRSET
1553 if (CSET_DOUBLE(GetLineDblCS(ld))) {
1554 col = MaxCols(screen) / 2 - (int) n;
1555 }
1556 #endif
1557 horizontal_copy_area(xw,
1558 (screen->cur_col + (int) n),
1559 col - screen->cur_col,
1560 -((int) n));
1561
1562 ClearCurBackground(xw,
1563 INX2ROW(screen, screen->cur_row),
1564 col,
1565 1U,
1566 n,
1567 (unsigned) LineFontWidth(screen, ld));
1568 }
1569 if (n != 0) {
1570 /* adjust screen->buf */
1571 ScrnDeleteChar(xw, n);
1572 }
1573 }
1574
1575 /*
1576 * Clear from cursor position to beginning of display, inclusive.
1577 */
1578 static void
ClearAbove(XtermWidget xw)1579 ClearAbove(XtermWidget xw)
1580 {
1581 TScreen *screen = TScreenOf(xw);
1582
1583 if (screen->protected_mode != OFF_PROTECT) {
1584 int row;
1585 unsigned len = (unsigned) MaxCols(screen);
1586
1587 assert(screen->max_col >= 0);
1588 for (row = 0; row < screen->cur_row; row++)
1589 ClearInLine(xw, row, 0, len);
1590 ClearInLine(xw, screen->cur_row, 0, (unsigned) screen->cur_col);
1591 } else {
1592 int top;
1593
1594 if (screen->cursor_state)
1595 HideCursor(xw);
1596 if ((top = INX2ROW(screen, 0)) <= screen->max_row) {
1597 int height;
1598
1599 if (screen->scroll_amt)
1600 FlushScroll(xw);
1601 if ((height = screen->cur_row + top) > screen->max_row)
1602 height = screen->max_row + 1;
1603 if ((height -= top) > 0) {
1604 chararea_clear_displayed_graphics(screen,
1605 0,
1606 top,
1607 MaxCols(screen),
1608 height);
1609
1610 ClearCurBackground(xw,
1611 top,
1612 0,
1613 (unsigned) height,
1614 (unsigned) MaxCols(screen),
1615 (unsigned) FontWidth(screen));
1616 }
1617 }
1618 ClearBufRows(xw, 0, screen->cur_row - 1);
1619 }
1620
1621 ClearLeft(xw);
1622 }
1623
1624 /*
1625 * Clear from cursor position to end of display, inclusive.
1626 */
1627 static void
ClearBelow(XtermWidget xw)1628 ClearBelow(XtermWidget xw)
1629 {
1630 TScreen *screen = TScreenOf(xw);
1631
1632 ClearRight(xw, -1);
1633
1634 if (screen->protected_mode != OFF_PROTECT) {
1635 int row;
1636 unsigned len = (unsigned) MaxCols(screen);
1637
1638 assert(screen->max_col >= 0);
1639 for (row = screen->cur_row + 1; row <= screen->max_row; row++)
1640 ClearInLine(xw, row, 0, len);
1641 } else {
1642 int top;
1643
1644 if ((top = INX2ROW(screen, screen->cur_row)) <= screen->max_row) {
1645 if (screen->scroll_amt)
1646 FlushScroll(xw);
1647 if (++top <= screen->max_row) {
1648 chararea_clear_displayed_graphics(screen,
1649 0,
1650 top,
1651 MaxCols(screen),
1652 (screen->max_row - top + 1));
1653 ClearCurBackground(xw,
1654 top,
1655 0,
1656 (unsigned) (screen->max_row - top + 1),
1657 (unsigned) MaxCols(screen),
1658 (unsigned) FontWidth(screen));
1659 }
1660 }
1661 ClearBufRows(xw, screen->cur_row + 1, screen->max_row);
1662 }
1663 }
1664
1665 /*
1666 * Clear the given row, for the given range of columns, returning 1 if no
1667 * protected characters were found, 0 otherwise.
1668 */
1669 static int
ClearInLine2(XtermWidget xw,int flags,int row,int col,unsigned len)1670 ClearInLine2(XtermWidget xw, int flags, int row, int col, unsigned len)
1671 {
1672 TScreen *screen = TScreenOf(xw);
1673 CLineData *ld;
1674 int rc = 1;
1675
1676 TRACE(("ClearInLine(row=%d, col=%d, len=%d) vs %d..%d\n",
1677 row, col, len,
1678 screen->startH.row,
1679 screen->startH.col));
1680
1681 if (ScrnHaveSelection(screen)
1682 && ScrnIsRowInSelection(screen, row)) {
1683 ScrnDisownSelection(xw);
1684 }
1685
1686 if (col + (int) len >= MaxCols(screen)) {
1687 len = (unsigned) (MaxCols(screen) - col);
1688 }
1689
1690 /* If we've marked protected text on the screen, we'll have to
1691 * check each time we do an erase.
1692 */
1693 if (screen->protected_mode != OFF_PROTECT) {
1694 unsigned n;
1695 IAttr *attrs = getLineData(screen, row)->attribs + col;
1696 int saved_mode = screen->protected_mode;
1697 Bool done;
1698
1699 /* disable this branch during recursion */
1700 screen->protected_mode = OFF_PROTECT;
1701
1702 do {
1703 done = True;
1704 for (n = 0; n < len; n++) {
1705 if (attrs[n] & PROTECTED) {
1706 rc = 0; /* found a protected segment */
1707 if (n != 0) {
1708 ClearInLine(xw, row, col, n);
1709 }
1710 while ((n < len)
1711 && (attrs[n] & PROTECTED)) {
1712 n++;
1713 }
1714 done = False;
1715 break;
1716 }
1717 }
1718 /* setup for another segment, past the protected text */
1719 if (!done) {
1720 attrs += n;
1721 col += (int) n;
1722 len -= n;
1723 }
1724 } while (!done);
1725
1726 screen->protected_mode = saved_mode;
1727 if ((int) len <= 0) {
1728 return 0;
1729 }
1730 }
1731 /* fall through to the final non-protected segment */
1732
1733 if (screen->cursor_state)
1734 HideCursor(xw);
1735 ResetWrap(screen);
1736
1737 if (AddToVisible(xw)
1738 && (ld = getLineData(screen, row)) != 0) {
1739
1740 ClearCurBackground(xw,
1741 INX2ROW(screen, row),
1742 col,
1743 1U,
1744 len,
1745 (unsigned) LineFontWidth(screen, ld));
1746 }
1747
1748 if (len != 0) {
1749 ClearCells(xw, flags, len, row, col);
1750 }
1751
1752 return rc;
1753 }
1754
1755 int
ClearInLine(XtermWidget xw,int row,int col,unsigned len)1756 ClearInLine(XtermWidget xw, int row, int col, unsigned len)
1757 {
1758 TScreen *screen = TScreenOf(xw);
1759 int flags = 0;
1760
1761 /*
1762 * If we're clearing to the end of the line, we won't count this as
1763 * "drawn" characters. We'll only do cut/paste on "drawn" characters,
1764 * so this has the effect of suppressing trailing blanks from a
1765 * selection.
1766 */
1767 if (col + (int) len < MaxCols(screen)) {
1768 flags |= CHARDRAWN;
1769 }
1770 return ClearInLine2(xw, flags, row, col, len);
1771 }
1772
1773 /*
1774 * Clear the next n characters on the cursor's line, including the cursor's
1775 * position.
1776 */
1777 void
ClearRight(XtermWidget xw,int n)1778 ClearRight(XtermWidget xw, int n)
1779 {
1780 TScreen *screen = TScreenOf(xw);
1781 LineData *ld;
1782 unsigned len = (unsigned) (MaxCols(screen) - screen->cur_col);
1783
1784 assert(screen->max_col >= 0);
1785 assert(screen->max_col >= screen->cur_col);
1786
1787 if (n < 0) /* the remainder of the line */
1788 n = MaxCols(screen);
1789 if (n == 0) /* default for 'ECH' */
1790 n = 1;
1791
1792 if (len > (unsigned) n)
1793 len = (unsigned) n;
1794
1795 ld = getLineData(screen, screen->cur_row);
1796 if (AddToVisible(xw)) {
1797 if_OPT_WIDE_CHARS(screen, {
1798 int col = screen->cur_col;
1799 int row = screen->cur_row;
1800 int kl;
1801 int kr;
1802 if (DamagedCurCells(screen, len, &kl, &kr) && kr >= kl) {
1803 int xx = col;
1804 if (kl < xx) {
1805 ClearInLine2(xw, 0, row, kl, (unsigned) (xx - kl));
1806 }
1807 xx = col + (int) len - 1;
1808 if (kr > xx) {
1809 ClearInLine2(xw, 0, row, xx + 1, (unsigned) (kr - xx));
1810 }
1811 }
1812 });
1813 (void) ClearInLine(xw, screen->cur_row, screen->cur_col, len);
1814 } else {
1815 ScrnClearCells(xw, screen->cur_row, screen->cur_col, len);
1816 }
1817
1818 /* with the right part cleared, we can't be wrapping */
1819 LineClrWrapped(ld);
1820 ShowWrapMarks(xw, screen->cur_row, ld);
1821 ResetWrap(screen);
1822 }
1823
1824 /*
1825 * Clear first part of cursor's line, inclusive.
1826 */
1827 static void
ClearLeft(XtermWidget xw)1828 ClearLeft(XtermWidget xw)
1829 {
1830 TScreen *screen = TScreenOf(xw);
1831 unsigned len = (unsigned) screen->cur_col + 1;
1832
1833 assert(screen->cur_col >= 0);
1834 if (AddToVisible(xw)) {
1835 if_OPT_WIDE_CHARS(screen, {
1836 int row = screen->cur_row;
1837 int kl;
1838 int kr;
1839 if (DamagedCurCells(screen, 1, &kl, &kr) && kr >= kl) {
1840 ClearInLine2(xw, 0, row, kl, (unsigned) (kr - kl + 1));
1841 }
1842 });
1843 (void) ClearInLine(xw, screen->cur_row, 0, len);
1844 } else {
1845 ScrnClearCells(xw, screen->cur_row, 0, len);
1846 }
1847 }
1848
1849 /*
1850 * Erase the cursor's line.
1851 */
1852 static void
ClearLine(XtermWidget xw)1853 ClearLine(XtermWidget xw)
1854 {
1855 TScreen *screen = TScreenOf(xw);
1856 unsigned len = (unsigned) MaxCols(screen);
1857
1858 assert(screen->max_col >= 0);
1859 (void) ClearInLine(xw, screen->cur_row, 0, len);
1860 }
1861
1862 void
ClearScreen(XtermWidget xw)1863 ClearScreen(XtermWidget xw)
1864 {
1865 TScreen *screen = TScreenOf(xw);
1866 int top;
1867
1868 TRACE(("ClearScreen\n"));
1869
1870 if (screen->cursor_state)
1871 HideCursor(xw);
1872
1873 ScrnDisownSelection(xw);
1874 ResetWrap(screen);
1875 if ((top = INX2ROW(screen, 0)) <= screen->max_row) {
1876 if (screen->scroll_amt)
1877 FlushScroll(xw);
1878 chararea_clear_displayed_graphics(screen,
1879 0,
1880 top,
1881 MaxCols(screen),
1882 (screen->max_row - top + 1));
1883 ClearCurBackground(xw,
1884 top,
1885 0,
1886 (unsigned) (screen->max_row - top + 1),
1887 (unsigned) MaxCols(screen),
1888 (unsigned) FontWidth(screen));
1889 }
1890 ClearBufRows(xw, 0, screen->max_row);
1891 }
1892
1893 /*
1894 * If we've written protected text DEC-style, and are issuing a non-DEC
1895 * erase, temporarily reset the protected_mode flag so that the erase will
1896 * ignore the protected flags.
1897 */
1898 void
do_erase_char(XtermWidget xw,int param,int mode)1899 do_erase_char(XtermWidget xw, int param, int mode)
1900 {
1901 TScreen *screen = TScreenOf(xw);
1902 int saved_mode = screen->protected_mode;
1903
1904 if (saved_mode == DEC_PROTECT
1905 && saved_mode != mode) {
1906 screen->protected_mode = OFF_PROTECT;
1907 }
1908
1909 ClearRight(xw, param);
1910 screen->protected_mode = saved_mode;
1911 }
1912
1913 void
do_erase_line(XtermWidget xw,int param,int mode)1914 do_erase_line(XtermWidget xw, int param, int mode)
1915 {
1916 TScreen *screen = TScreenOf(xw);
1917 int saved_mode = screen->protected_mode;
1918
1919 if (saved_mode == DEC_PROTECT
1920 && saved_mode != mode) {
1921 screen->protected_mode = OFF_PROTECT;
1922 }
1923
1924 switch (param) {
1925 case -1: /* DEFAULT */
1926 case 0:
1927 ClearRight(xw, -1);
1928 break;
1929 case 1:
1930 ClearLeft(xw);
1931 break;
1932 case 2:
1933 ClearLine(xw);
1934 break;
1935 }
1936 screen->protected_mode = saved_mode;
1937 }
1938
1939 /*
1940 * Just like 'do_erase_line()', except that this intercepts ED controls. If we
1941 * clear the whole screen, we'll get the return-value from ClearInLine, and
1942 * find if there were any protected characters left. If not, reset the
1943 * protected mode flag in the screen data (it's slower).
1944 */
1945 void
do_erase_display(XtermWidget xw,int param,int mode)1946 do_erase_display(XtermWidget xw, int param, int mode)
1947 {
1948 TScreen *screen = TScreenOf(xw);
1949 int saved_mode = screen->protected_mode;
1950
1951 if (saved_mode == DEC_PROTECT
1952 && saved_mode != mode)
1953 screen->protected_mode = OFF_PROTECT;
1954
1955 switch (param) {
1956 case -1: /* DEFAULT */
1957 case 0:
1958 if (screen->cur_row == 0
1959 && screen->cur_col == 0) {
1960 screen->protected_mode = saved_mode;
1961 do_erase_display(xw, 2, mode);
1962 saved_mode = screen->protected_mode;
1963 } else
1964 ClearBelow(xw);
1965 break;
1966
1967 case 1:
1968 if (screen->cur_row == screen->max_row
1969 && screen->cur_col == screen->max_col) {
1970 screen->protected_mode = saved_mode;
1971 do_erase_display(xw, 2, mode);
1972 saved_mode = screen->protected_mode;
1973 } else
1974 ClearAbove(xw);
1975 break;
1976
1977 case 2:
1978 /*
1979 * We use 'ClearScreen()' throughout the remainder of the
1980 * program for places where we don't care if the characters are
1981 * protected or not. So we modify the logic around this call
1982 * on 'ClearScreen()' to handle protected characters.
1983 */
1984 if (screen->protected_mode != OFF_PROTECT) {
1985 int row;
1986 int rc = 1;
1987 unsigned len = (unsigned) MaxCols(screen);
1988
1989 assert(screen->max_col >= 0);
1990 for (row = 0; row <= screen->max_row; row++)
1991 rc &= ClearInLine(xw, row, 0, len);
1992 if (rc != 0)
1993 saved_mode = OFF_PROTECT;
1994 } else {
1995 ClearScreen(xw);
1996 }
1997 break;
1998
1999 case 3:
2000 /* xterm addition - erase saved lines. */
2001 if (screen->eraseSavedLines) {
2002 screen->savedlines = 0;
2003 ScrollBarDrawThumb(xw, 1);
2004 }
2005 break;
2006 }
2007 screen->protected_mode = saved_mode;
2008 }
2009
2010 static Boolean
row_has_data(TScreen * screen,int row)2011 row_has_data(TScreen *screen, int row)
2012 {
2013 Boolean result = False;
2014 CLineData *ld;
2015
2016 if ((ld = getLineData(screen, row)) != 0) {
2017 int col;
2018
2019 for (col = 0; col < screen->max_col; ++col) {
2020 if (ld->attribs[col] & CHARDRAWN && ld->charData[col] != ' ') {
2021 result = True;
2022 break;
2023 }
2024 }
2025 }
2026 return result;
2027 }
2028
2029 static Boolean
screen_has_data(XtermWidget xw)2030 screen_has_data(XtermWidget xw)
2031 {
2032 TScreen *screen = TScreenOf(xw);
2033 Boolean result = False;
2034 int row;
2035
2036 for (row = 0; row < screen->max_row; ++row) {
2037 if (row_has_data(screen, row)) {
2038 result = True;
2039 break;
2040 }
2041 }
2042 return result;
2043 }
2044
2045 static void
do_extra_scroll(XtermWidget xw,Bool trimmed)2046 do_extra_scroll(XtermWidget xw, Bool trimmed)
2047 {
2048 TScreen *screen = TScreenOf(xw);
2049 int row;
2050
2051 if (screen_has_data(xw)) {
2052 TRACE(("do_extra_scroll buffer=%d, trimmed=%s\n", screen->whichBuf,
2053 BtoS(trimmed)));
2054 if (trimmed) {
2055 Boolean hadData = (Boolean) ((screen->saved_fifo > 0)
2056 ? row_has_data(screen, -1)
2057 : False);
2058
2059 for (row = 0; row < screen->max_row; ++row) {
2060 Boolean hasData = row_has_data(screen, row);
2061 if (hasData || hadData) {
2062 LineData *dst = addScrollback(screen);
2063 LineData *src = getLineData(screen, row);
2064 copyLineData(dst, src);
2065 IncrementSavedLines(1);
2066 }
2067 hadData = hasData;
2068 }
2069 } else {
2070 xtermScroll(xw, screen->max_row);
2071 FlushScroll(xw);
2072 }
2073 xtermRepaint(xw);
2074 }
2075 }
2076
2077 /*
2078 * Like tiXtraScroll, perform a scroll up of the page contents.
2079 *
2080 * In this case, it happens for the special case when erasing the whole
2081 * display, e.g., an erase-below starting from the upper-left corner of the
2082 * screen, or if the erasure applies to the whole screen.
2083 */
2084 void
do_cd_xtra_scroll(XtermWidget xw,int param)2085 do_cd_xtra_scroll(XtermWidget xw, int param)
2086 {
2087 TScreen *screen = TScreenOf(xw);
2088
2089 TRACE(("do_cd_xtra_scroll param %d, @%d,%d vs %d,%d\n", param,
2090 screen->cur_row,
2091 screen->cur_col,
2092 ScrnTopMargin(xw),
2093 ScrnLeftMargin(xw)));
2094 if (xw->misc.cdXtraScroll
2095 && (param == 2 ||
2096 (param == 0
2097 && screen->cur_col <= ScrnLeftMargin(xw)
2098 && screen->cur_row <= ScrnTopMargin(xw)))) {
2099 do_extra_scroll(xw, (xw->misc.cdXtraScroll == edTrim));
2100 }
2101 }
2102
2103 /*
2104 * Scroll the page up (saving it). This is called when doing terminal
2105 * initialization (ti) or exiting from that (te).
2106 */
2107 void
do_ti_xtra_scroll(XtermWidget xw)2108 do_ti_xtra_scroll(XtermWidget xw)
2109 {
2110 if (xw->misc.tiXtraScroll) {
2111 do_extra_scroll(xw, False);
2112 }
2113 }
2114
2115 static void
CopyWait(XtermWidget xw)2116 CopyWait(XtermWidget xw)
2117 {
2118 TScreen *screen = TScreenOf(xw);
2119 XEvent reply;
2120 XEvent *rep = &reply;
2121 #ifndef NO_ACTIVE_ICON
2122 int retries = 0;
2123 #endif
2124
2125 #if USE_DOUBLE_BUFFER
2126 if (resource.buffered)
2127 return;
2128 #endif
2129
2130 for (;;) {
2131 #ifndef NO_ACTIVE_ICON
2132 if (xw->work.active_icon != eiFalse) {
2133 /*
2134 * The XWindowEvent call blocks until an event is available. That
2135 * can hang when using active-icon and iconifying/deiconifying
2136 * while the terminal is receiving lots of output. Checking with
2137 * this call on the other hand may lose exposure events which
2138 * arrive too late. As a compromise, try several times with a
2139 * time-delay before assuming no more events are available.
2140 */
2141 if (XCheckWindowEvent(screen->display,
2142 VWindow(screen),
2143 ExposureMask,
2144 &reply)) {
2145 retries = 0;
2146 } else {
2147 if (++retries >= 1000)
2148 return;
2149 usleep(100U); /* wait 0.1msec */
2150 continue;
2151 }
2152 } else
2153 #endif
2154 XWindowEvent(screen->display, VWindow(screen), ExposureMask, &reply);
2155 switch (reply.type) {
2156 case Expose:
2157 HandleExposure(xw, &reply);
2158 break;
2159 case NoExpose:
2160 case GraphicsExpose:
2161 if (screen->incopy <= 0) {
2162 screen->incopy = 1;
2163 if (screen->scrolls > 0)
2164 screen->scrolls--;
2165 }
2166 if (reply.type == GraphicsExpose)
2167 HandleExposure(xw, &reply);
2168
2169 if ((reply.type == NoExpose) ||
2170 ((XExposeEvent *) rep)->count == 0) {
2171 if (screen->incopy <= 0 && screen->scrolls > 0)
2172 screen->scrolls--;
2173 if (screen->scrolls == 0) {
2174 screen->incopy = 0;
2175 return;
2176 }
2177 screen->incopy = -1;
2178 }
2179 break;
2180 }
2181 }
2182 }
2183
2184 /*
2185 * used by vertical_copy_area and and horizontal_copy_area
2186 */
2187 static void
copy_area(XtermWidget xw,int src_x,int src_y,unsigned width,unsigned height,int dest_x,int dest_y)2188 copy_area(XtermWidget xw,
2189 int src_x,
2190 int src_y,
2191 unsigned width,
2192 unsigned height,
2193 int dest_x,
2194 int dest_y)
2195 {
2196 TScreen *screen = TScreenOf(xw);
2197
2198 if (width != 0 && height != 0) {
2199 /* wait for previous CopyArea to complete unless
2200 multiscroll is enabled and active */
2201 if (screen->incopy && screen->scrolls == 0)
2202 CopyWait(xw);
2203 screen->incopy = -1;
2204
2205 /* save for translating Expose events */
2206 screen->copy_src_x = src_x;
2207 screen->copy_src_y = src_y;
2208 screen->copy_width = width;
2209 screen->copy_height = height;
2210 screen->copy_dest_x = dest_x;
2211 screen->copy_dest_y = dest_y;
2212
2213 XCopyArea(screen->display,
2214 VDrawable(screen), VDrawable(screen),
2215 NormalGC(xw, screen),
2216 src_x, src_y, width, height, dest_x, dest_y);
2217 }
2218 }
2219
2220 /*
2221 * use when inserting or deleting characters on the current line
2222 */
2223 static void
horizontal_copy_area(XtermWidget xw,int firstchar,int nchars,int amount)2224 horizontal_copy_area(XtermWidget xw,
2225 int firstchar, /* char pos on screen to start copying at */
2226 int nchars,
2227 int amount) /* number of characters to move right */
2228 {
2229 TScreen *screen = TScreenOf(xw);
2230 CLineData *ld;
2231
2232 if ((ld = getLineData(screen, screen->cur_row)) != 0) {
2233 int src_x = LineCursorX(screen, ld, firstchar);
2234 int src_y = CursorY(screen, screen->cur_row);
2235
2236 copy_area(xw, src_x, src_y,
2237 (unsigned) (nchars * LineFontWidth(screen, ld)),
2238 (unsigned) FontHeight(screen),
2239 src_x + amount * LineFontWidth(screen, ld), src_y);
2240 }
2241 }
2242
2243 /*
2244 * use when inserting or deleting lines from the screen
2245 */
2246 static void
vertical_copy_area(XtermWidget xw,int firstline,int nlines,int amount,int left,int right)2247 vertical_copy_area(XtermWidget xw,
2248 int firstline, /* line on screen to start copying at */
2249 int nlines,
2250 int amount, /* number of lines to move up (neg=down) */
2251 int left,
2252 int right)
2253 {
2254 TScreen *screen = TScreenOf(xw);
2255
2256 TRACE(("vertical_copy_area - firstline=%d nlines=%d left=%d right=%d amount=%d\n",
2257 firstline, nlines, left, right, amount));
2258
2259 if (nlines > 0) {
2260 int src_x = CursorX(screen, left);
2261 int src_y = firstline * FontHeight(screen) + screen->border;
2262 unsigned int w = (unsigned) ((right + 1 - left) * FontWidth(screen));
2263 unsigned int h = (unsigned) (nlines * FontHeight(screen));
2264 int dst_x = src_x;
2265 int dst_y = src_y - amount * FontHeight(screen);
2266
2267 copy_area(xw, src_x, src_y, w, h, dst_x, dst_y);
2268
2269 if (screen->show_wrap_marks) {
2270 int row;
2271 int first = firstline - amount;
2272 int last = firstline + nlines + amount;
2273
2274 for (row = first; row < last; ++row) {
2275 CLineData *ld;
2276 int mapped = amount + row + screen->topline;
2277
2278 if ((ld = getLineData(screen, mapped)) != 0) {
2279 ShowWrapMarks(xw, row, ld);
2280 }
2281 }
2282 }
2283 }
2284 }
2285
2286 /*
2287 * use when scrolling the entire screen
2288 */
2289 void
scrolling_copy_area(XtermWidget xw,int firstline,int nlines,int amount)2290 scrolling_copy_area(XtermWidget xw,
2291 int firstline, /* line on screen to start copying at */
2292 int nlines,
2293 int amount) /* number of lines to move up (neg=down) */
2294 {
2295
2296 if (nlines > 0) {
2297 vertical_copy_area(xw, firstline, nlines, amount, 0, TScreenOf(xw)->max_col);
2298 }
2299 }
2300
2301 /*
2302 * Handler for Expose events on the VT widget.
2303 * Returns 1 iff the area where the cursor was got refreshed.
2304 */
2305 int
HandleExposure(XtermWidget xw,XEvent * event)2306 HandleExposure(XtermWidget xw, XEvent *event)
2307 {
2308 TScreen *screen = TScreenOf(xw);
2309 XExposeEvent *reply = (XExposeEvent *) event;
2310
2311 #ifndef NO_ACTIVE_ICON
2312 if (reply->window == screen->iconVwin.window) {
2313 WhichVWin(screen) = &screen->iconVwin;
2314 TRACE(("HandleExposure - icon\n"));
2315 } else {
2316 WhichVWin(screen) = &screen->fullVwin;
2317 TRACE(("HandleExposure - normal\n"));
2318 }
2319 TRACE((" event %d,%d %dx%d\n",
2320 reply->y,
2321 reply->x,
2322 reply->height,
2323 reply->width));
2324 #endif /* NO_ACTIVE_ICON */
2325
2326 /* if not doing CopyArea or if this is a GraphicsExpose, don't translate */
2327 if (!screen->incopy || event->type != Expose) {
2328 return handle_translated_exposure(xw, reply->x, reply->y,
2329 reply->width,
2330 reply->height);
2331 } else {
2332 /* compute intersection of area being copied with
2333 area being exposed. */
2334 int both_x1 = Max(screen->copy_src_x, reply->x);
2335 int both_y1 = Max(screen->copy_src_y, reply->y);
2336 int both_x2 = Min(screen->copy_src_x + (int) screen->copy_width,
2337 (reply->x + (int) reply->width));
2338 int both_y2 = Min(screen->copy_src_y + (int) screen->copy_height,
2339 (reply->y + (int) reply->height));
2340 int value = 0;
2341
2342 /* was anything copied affected? */
2343 if (both_x2 > both_x1 && both_y2 > both_y1) {
2344 /* do the copied area */
2345 value = handle_translated_exposure
2346 (xw, reply->x + screen->copy_dest_x - screen->copy_src_x,
2347 reply->y + screen->copy_dest_y - screen->copy_src_y,
2348 reply->width, reply->height);
2349 }
2350 /* was anything not copied affected? */
2351 if (reply->x < both_x1 || reply->y < both_y1
2352 || reply->x + reply->width > both_x2
2353 || reply->y + reply->height > both_y2)
2354 value = handle_translated_exposure(xw, reply->x, reply->y,
2355 reply->width, reply->height);
2356
2357 return value;
2358 }
2359 }
2360
2361 static void
set_background(XtermWidget xw,int color)2362 set_background(XtermWidget xw, int color)
2363 {
2364 TScreen *screen = TScreenOf(xw);
2365 Pixel c = getXtermBG(xw, xw->flags, color);
2366
2367 #if OPT_WIDE_ATTRS
2368 TRACE(("set_background(%d) %#lx %s\n", color, c,
2369 ((xw->flags & ATR_DIRECT_BG)
2370 ? "direct"
2371 : "indexed")));
2372 #else
2373 TRACE(("set_background(%d) %#lx\n", color, c));
2374 #endif
2375 XSetWindowBackground(screen->display, VShellWindow(xw), c);
2376 XSetWindowBackground(screen->display, VWindow(screen), c);
2377 initBorderGC(xw, WhichVWin(screen));
2378 }
2379
2380 void
xtermClear2(XtermWidget xw,int x,int y,unsigned width,unsigned height)2381 xtermClear2(XtermWidget xw, int x, int y, unsigned width, unsigned height)
2382 {
2383 TScreen *screen = TScreenOf(xw);
2384 VTwin *vwin = WhichVWin(screen);
2385 Drawable draw = VDrawable(screen);
2386 GC gc;
2387
2388 if ((gc = vwin->border_gc) != 0) {
2389 int vmark1 = screen->border;
2390 int vmark2 = vwin->height + vmark1;
2391 int hmark1 = OriginX(screen);
2392 int hmark2 = vwin->width + hmark1;
2393 if (y < vmark1) {
2394 int yy = y + (int) height;
2395 int h1 = (yy <= vmark1) ? (yy - y) : (vmark1 - y);
2396 XFillRectangle(screen->display, draw, gc,
2397 x, y, width, (unsigned) h1);
2398 if (yy > vmark1) {
2399 xtermClear2(xw, x, vmark1, width, (unsigned) (yy - vmark1));
2400 }
2401 } else if (y < vmark2) {
2402 int yy = y + (int) height;
2403 int h2 = (yy <= vmark2) ? (yy - y) : (vmark2 - y);
2404 int xb = x;
2405 int xx = x + (int) width;
2406 int ww = (int) width;
2407 if (x < hmark1) {
2408 int w1 = (xx <= hmark1) ? (xx - x) : (hmark1 - x);
2409 XFillRectangle(screen->display, draw, gc,
2410 x, y, (unsigned) w1, (unsigned) h2);
2411 x += w1;
2412 ww -= w1;
2413 }
2414 if ((ww > 0) && (x < hmark2)) {
2415 int w2 = (xx <= hmark2) ? (xx - x) : (hmark2 - x);
2416 #if USE_DOUBLE_BUFFER
2417 if (resource.buffered) {
2418 XFillRectangle(screen->display, draw,
2419 FillerGC(xw, screen),
2420 x, y, (unsigned) w2, (unsigned) h2);
2421 } else
2422 #endif
2423 XClearArea(screen->display, VWindow(screen),
2424 x, y, (unsigned) w2, (unsigned) h2, False);
2425 x += w2;
2426 ww -= w2;
2427 }
2428 if (ww > 0) {
2429 XFillRectangle(screen->display, draw, gc,
2430 x, y, (unsigned) ww, (unsigned) h2);
2431 }
2432 if (yy > vmark2) {
2433 xtermClear2(xw, xb, vmark2, width, (unsigned) (yy - vmark2));
2434 }
2435 } else {
2436 XFillRectangle(screen->display, draw, gc, x, y, width, height);
2437 }
2438 } else {
2439 #if USE_DOUBLE_BUFFER
2440 if (resource.buffered) {
2441 gc = FillerGC(xw, screen);
2442 XFillRectangle(screen->display, draw, gc,
2443 x, y, width, height);
2444 } else
2445 #endif
2446 XClearArea(screen->display,
2447 VWindow(screen),
2448 x, y, width, height, False);
2449 }
2450 }
2451
2452 /*
2453 * Called by the ExposeHandler to do the actual repaint after the coordinates
2454 * have been translated to allow for any CopyArea in progress.
2455 * The rectangle passed in is pixel coordinates.
2456 */
2457 static int
handle_translated_exposure(XtermWidget xw,int rect_x,int rect_y,int rect_width,int rect_height)2458 handle_translated_exposure(XtermWidget xw,
2459 int rect_x,
2460 int rect_y,
2461 int rect_width,
2462 int rect_height)
2463 {
2464 TScreen *screen = TScreenOf(xw);
2465 int toprow, leftcol, nrows, ncols;
2466 int x0, x1;
2467 int y0, y1;
2468 int result = 0;
2469
2470 TRACE(("handle_translated_exposure at %d,%d size %dx%d\n",
2471 rect_y, rect_x, rect_height, rect_width));
2472
2473 x0 = (rect_x - OriginX(screen));
2474 x1 = (x0 + rect_width);
2475
2476 y0 = (rect_y - OriginY(screen));
2477 y1 = (y0 + rect_height);
2478
2479 if ((x0 < 0 ||
2480 y0 < 0 ||
2481 x1 > Width(screen) ||
2482 y1 > Height(screen))) {
2483 set_background(xw, -1);
2484 xtermClear2(xw,
2485 rect_x,
2486 rect_y,
2487 (unsigned) rect_width,
2488 (unsigned) rect_height);
2489 }
2490 toprow = y0 / FontHeight(screen);
2491 if (toprow < 0)
2492 toprow = 0;
2493
2494 leftcol = x0 / FontWidth(screen);
2495 if (leftcol < 0)
2496 leftcol = 0;
2497
2498 nrows = (y1 - 1) / FontHeight(screen) - toprow + 1;
2499 ncols = (x1 - 1) / FontWidth(screen) - leftcol + 1;
2500 toprow -= screen->scrolls;
2501 if (toprow < 0) {
2502 nrows += toprow;
2503 toprow = 0;
2504 }
2505 if (toprow + nrows > MaxRows(screen))
2506 nrows = MaxRows(screen) - toprow;
2507 if (leftcol + ncols > MaxCols(screen))
2508 ncols = MaxCols(screen) - leftcol;
2509
2510 if (nrows > 0 && ncols > 0) {
2511 ScrnRefresh(xw, toprow, leftcol, nrows, ncols, True);
2512 first_map_occurred();
2513 if (screen->cur_row >= toprow &&
2514 screen->cur_row < toprow + nrows &&
2515 screen->cur_col >= leftcol &&
2516 screen->cur_col < leftcol + ncols) {
2517 result = 1;
2518 }
2519
2520 }
2521 TRACE(("...handle_translated_exposure %d\n", result));
2522 return (result);
2523 }
2524
2525 /***====================================================================***/
2526
2527 void
GetColors(XtermWidget xw,ScrnColors * pColors)2528 GetColors(XtermWidget xw, ScrnColors * pColors)
2529 {
2530 TScreen *screen = TScreenOf(xw);
2531 int n;
2532
2533 pColors->which = 0;
2534 for (n = 0; n < NCOLORS; ++n) {
2535 SET_COLOR_VALUE(pColors, n, T_COLOR(screen, n));
2536 }
2537 }
2538
2539 void
ChangeColors(XtermWidget xw,ScrnColors * pNew)2540 ChangeColors(XtermWidget xw, ScrnColors * pNew)
2541 {
2542 Bool repaint = False;
2543 TScreen *screen = TScreenOf(xw);
2544 VTwin *win = WhichVWin(screen);
2545
2546 TRACE(("ChangeColors\n"));
2547
2548 if (COLOR_DEFINED(pNew, TEXT_CURSOR)) {
2549 T_COLOR(screen, TEXT_CURSOR) = COLOR_VALUE(pNew, TEXT_CURSOR);
2550 TRACE(("... TEXT_CURSOR: %#lx\n", T_COLOR(screen, TEXT_CURSOR)));
2551 FreeMarkGCs(xw);
2552 /* no repaint needed */
2553 } else if ((T_COLOR(screen, TEXT_CURSOR) == T_COLOR(screen, TEXT_FG)) &&
2554 (COLOR_DEFINED(pNew, TEXT_FG))) {
2555 if (T_COLOR(screen, TEXT_CURSOR) != COLOR_VALUE(pNew, TEXT_FG)) {
2556 T_COLOR(screen, TEXT_CURSOR) = COLOR_VALUE(pNew, TEXT_FG);
2557 TRACE(("... TEXT_CURSOR: %#lx\n", T_COLOR(screen, TEXT_CURSOR)));
2558 if (screen->Vshow)
2559 repaint = True;
2560 }
2561 FreeMarkGCs(xw);
2562 }
2563
2564 if (COLOR_DEFINED(pNew, TEXT_FG)) {
2565 Pixel fg = COLOR_VALUE(pNew, TEXT_FG);
2566 T_COLOR(screen, TEXT_FG) = fg;
2567 TRACE(("... TEXT_FG: %#lx\n", T_COLOR(screen, TEXT_FG)));
2568 if (screen->Vshow) {
2569 setCgsFore(xw, win, gcNorm, fg);
2570 setCgsBack(xw, win, gcNormReverse, fg);
2571 setCgsFore(xw, win, gcBold, fg);
2572 setCgsBack(xw, win, gcBoldReverse, fg);
2573 repaint = True;
2574 }
2575 FreeMarkGCs(xw);
2576 }
2577
2578 if (COLOR_DEFINED(pNew, TEXT_BG)) {
2579 Pixel bg = COLOR_VALUE(pNew, TEXT_BG);
2580 T_COLOR(screen, TEXT_BG) = bg;
2581 TRACE(("... TEXT_BG: %#lx\n", T_COLOR(screen, TEXT_BG)));
2582 if (screen->Vshow) {
2583 setCgsBack(xw, win, gcNorm, bg);
2584 setCgsFore(xw, win, gcNormReverse, bg);
2585 setCgsBack(xw, win, gcBold, bg);
2586 setCgsFore(xw, win, gcBoldReverse, bg);
2587 set_background(xw, -1);
2588 repaint = True;
2589 }
2590 }
2591 #if OPT_HIGHLIGHT_COLOR
2592 if (COLOR_DEFINED(pNew, HIGHLIGHT_BG)) {
2593 if (T_COLOR(screen, HIGHLIGHT_BG) != COLOR_VALUE(pNew, HIGHLIGHT_BG)) {
2594 T_COLOR(screen, HIGHLIGHT_BG) = COLOR_VALUE(pNew, HIGHLIGHT_BG);
2595 TRACE(("... HIGHLIGHT_BG: %#lx\n", T_COLOR(screen, HIGHLIGHT_BG)));
2596 if (screen->Vshow)
2597 repaint = True;
2598 }
2599 }
2600 if (COLOR_DEFINED(pNew, HIGHLIGHT_FG)) {
2601 if (T_COLOR(screen, HIGHLIGHT_FG) != COLOR_VALUE(pNew, HIGHLIGHT_FG)) {
2602 T_COLOR(screen, HIGHLIGHT_FG) = COLOR_VALUE(pNew, HIGHLIGHT_FG);
2603 TRACE(("... HIGHLIGHT_FG: %#lx\n", T_COLOR(screen, HIGHLIGHT_FG)));
2604 if (screen->Vshow)
2605 repaint = True;
2606 }
2607 }
2608 #endif
2609
2610 if (COLOR_DEFINED(pNew, MOUSE_FG) || (COLOR_DEFINED(pNew, MOUSE_BG))) {
2611 if (COLOR_DEFINED(pNew, MOUSE_FG)) {
2612 T_COLOR(screen, MOUSE_FG) = COLOR_VALUE(pNew, MOUSE_FG);
2613 TRACE(("... MOUSE_FG: %#lx\n", T_COLOR(screen, MOUSE_FG)));
2614 }
2615 if (COLOR_DEFINED(pNew, MOUSE_BG)) {
2616 T_COLOR(screen, MOUSE_BG) = COLOR_VALUE(pNew, MOUSE_BG);
2617 TRACE(("... MOUSE_BG: %#lx\n", T_COLOR(screen, MOUSE_BG)));
2618 }
2619
2620 if (screen->Vshow) {
2621 recolor_cursor(screen,
2622 screen->pointer_cursor,
2623 T_COLOR(screen, MOUSE_FG),
2624 T_COLOR(screen, MOUSE_BG));
2625 XDefineCursor(screen->display, VWindow(screen),
2626 screen->pointer_cursor);
2627 }
2628 #if OPT_TEK4014
2629 if (TEK4014_SHOWN(xw)) {
2630 TekScreen *tekscr = TekScreenOf(tekWidget);
2631 Window tekwin = TWindow(tekscr);
2632 if (tekwin) {
2633 recolor_cursor(screen,
2634 tekscr->arrow,
2635 T_COLOR(screen, MOUSE_FG),
2636 T_COLOR(screen, MOUSE_BG));
2637 XDefineCursor(screen->display, tekwin, tekscr->arrow);
2638 }
2639 }
2640 #endif
2641 /* no repaint needed */
2642 }
2643
2644 if (COLOR_DEFINED(pNew, TEXT_FG) ||
2645 COLOR_DEFINED(pNew, TEXT_BG) ||
2646 COLOR_DEFINED(pNew, TEXT_CURSOR)) {
2647 if (set_cursor_gcs(xw) && screen->Vshow) {
2648 repaint = True;
2649 }
2650 }
2651 #if OPT_TEK4014
2652 if (COLOR_DEFINED(pNew, TEK_FG) ||
2653 COLOR_DEFINED(pNew, TEK_BG)) {
2654 ChangeTekColors(tekWidget, screen, pNew);
2655 if (TEK4014_SHOWN(xw)) {
2656 TekRepaint(tekWidget);
2657 }
2658 } else if (COLOR_DEFINED(pNew, TEK_CURSOR)) {
2659 ChangeTekColors(tekWidget, screen, pNew);
2660 }
2661 #endif
2662 if (repaint)
2663 xtermRepaint(xw);
2664 }
2665
2666 void
xtermClear(XtermWidget xw)2667 xtermClear(XtermWidget xw)
2668 {
2669 TScreen *screen = TScreenOf(xw);
2670
2671 TRACE(("xtermClear\n"));
2672 xtermClear2(xw, 0, 0, FullWidth(screen), FullHeight(screen));
2673 }
2674
2675 void
xtermRepaint(XtermWidget xw)2676 xtermRepaint(XtermWidget xw)
2677 {
2678 TScreen *screen = TScreenOf(xw);
2679
2680 TRACE(("xtermRepaint\n"));
2681 xtermClear(xw);
2682 ScrnRefresh(xw, 0, 0, MaxRows(screen), MaxCols(screen), True);
2683 }
2684
2685 /***====================================================================***/
2686
2687 Boolean
isDefaultForeground(const char * name)2688 isDefaultForeground(const char *name)
2689 {
2690 return (Boolean) !x_strcasecmp(name, XtDefaultForeground);
2691 }
2692
2693 Boolean
isDefaultBackground(const char * name)2694 isDefaultBackground(const char *name)
2695 {
2696 return (Boolean) !x_strcasecmp(name, XtDefaultBackground);
2697 }
2698
2699 #if OPT_WIDE_CHARS
2700 /*
2701 * Check for Unicode BIDI control characters, which may be miscategorized via
2702 * wcwidth() and iswprint() as zero-width printable characters.
2703 */
2704 Boolean
isWideControl(unsigned ch)2705 isWideControl(unsigned ch)
2706 {
2707 Boolean result;
2708
2709 switch (ch) {
2710 case 0x200E:
2711 case 0x200F:
2712 case 0x202A:
2713 case 0x202B:
2714 case 0x202C:
2715 case 0x202D:
2716 case 0x202E:
2717 result = True;
2718 break;
2719 default:
2720 result = False;
2721 break;
2722 }
2723 return result;
2724 }
2725 #endif
2726
2727 /***====================================================================***/
2728
2729 typedef struct {
2730 Pixel fg;
2731 Pixel bg;
2732 } ToSwap;
2733
2734 #if OPT_HIGHLIGHT_COLOR
2735 #define hc_param ,Bool hilite_color
2736 #define hc_value ,screen->hilite_color
2737 #else
2738 #define hc_param /* nothing */
2739 #define hc_value /* nothing */
2740 #endif
2741
2742 /*
2743 * Use this to swap the foreground/background color values in the resource
2744 * data, and to build up a list of the pairs which must be swapped in the
2745 * GC cache.
2746 */
2747 static void
swapLocally(ToSwap * list,int * count,ColorRes * fg,ColorRes * bg hc_param)2748 swapLocally(ToSwap * list, int *count, ColorRes * fg, ColorRes * bg hc_param)
2749 {
2750 ColorRes tmp;
2751 Boolean found = False;
2752
2753 Pixel fg_color = fg->value;
2754 Pixel bg_color = bg->value;
2755
2756 #if OPT_HIGHLIGHT_COLOR
2757 if ((fg_color != bg_color) || !hilite_color)
2758 #endif
2759 {
2760 int n;
2761
2762 EXCHANGE(*fg, *bg, tmp);
2763 for (n = 0; n < *count; ++n) {
2764 if ((list[n].fg == fg_color && list[n].bg == bg_color)
2765 || (list[n].fg == bg_color && list[n].bg == fg_color)) {
2766 found = True;
2767 break;
2768 }
2769 }
2770 if (!found) {
2771 list[*count].fg = fg_color;
2772 list[*count].bg = bg_color;
2773 *count = *count + 1;
2774 TRACE(("swapLocally fg %#lx, bg %#lx ->%d\n",
2775 fg_color, bg_color, *count));
2776 }
2777 }
2778 }
2779
2780 static void
reallySwapColors(XtermWidget xw,ToSwap * list,int count)2781 reallySwapColors(XtermWidget xw, ToSwap * list, int count)
2782 {
2783 int j, k;
2784
2785 TRACE(("reallySwapColors\n"));
2786 for (j = 0; j < count; ++j) {
2787 for_each_text_gc(k) {
2788 redoCgs(xw, list[j].fg, list[j].bg, (CgsEnum) k);
2789 }
2790 }
2791 FreeMarkGCs(xw);
2792 }
2793
2794 static void
swapVTwinGCs(XtermWidget xw,VTwin * win)2795 swapVTwinGCs(XtermWidget xw, VTwin *win)
2796 {
2797 swapCgs(xw, win, gcNorm, gcNormReverse);
2798 swapCgs(xw, win, gcBold, gcBoldReverse);
2799 }
2800
2801 void
ReverseVideo(XtermWidget xw)2802 ReverseVideo(XtermWidget xw)
2803 {
2804 TScreen *screen = TScreenOf(xw);
2805 ToSwap listToSwap[5];
2806 int numToSwap = 0;
2807
2808 TRACE(("ReverseVideo now %s\n", BtoS(xw->misc.re_verse)));
2809
2810 /*
2811 * Swap SGR foreground and background colors. By convention, these are
2812 * the colors assigned to "black" (SGR #0) and "white" (SGR #7). Also,
2813 * SGR #8 and SGR #15 are the bold (or bright) versions of SGR #0 and
2814 * #7, respectively.
2815 *
2816 * We don't swap colors that happen to match the screen's foreground
2817 * and background because that tends to produce bizarre effects.
2818 */
2819 #define swapAnyColor(name,a,b) swapLocally(listToSwap, &numToSwap, &(screen->name[a]), &(screen->name[b]) hc_value)
2820 #define swapAColor(a,b) swapAnyColor(Acolors, a, b)
2821 if_OPT_ISO_COLORS(screen, {
2822 swapAColor(0, 7);
2823 swapAColor(8, 15);
2824 });
2825
2826 if (T_COLOR(screen, TEXT_CURSOR) == T_COLOR(screen, TEXT_FG)) {
2827 T_COLOR(screen, TEXT_CURSOR) = T_COLOR(screen, TEXT_BG);
2828 }
2829 #define swapTColor(a,b) swapAnyColor(Tcolors, a, b)
2830 swapTColor(TEXT_FG, TEXT_BG);
2831 swapTColor(MOUSE_FG, MOUSE_BG);
2832
2833 reallySwapColors(xw, listToSwap, numToSwap);
2834
2835 swapVTwinGCs(xw, &(screen->fullVwin));
2836 #ifndef NO_ACTIVE_ICON
2837 swapVTwinGCs(xw, &(screen->iconVwin));
2838 #endif /* NO_ACTIVE_ICON */
2839
2840 xw->misc.re_verse = (Boolean) !xw->misc.re_verse;
2841 TRACE(("...swapping done, set ReverseVideo %s\n", BtoS(xw->misc.re_verse)));
2842
2843 if (XtIsRealized((Widget) xw)) {
2844 xtermDisplayPointer(xw);
2845 }
2846 #if OPT_TEK4014
2847 if (TEK4014_SHOWN(xw)) {
2848 TekScreen *tekscr = TekScreenOf(tekWidget);
2849 Window tekwin = TWindow(tekscr);
2850 recolor_cursor(screen,
2851 tekscr->arrow,
2852 T_COLOR(screen, MOUSE_FG),
2853 T_COLOR(screen, MOUSE_BG));
2854 XDefineCursor(screen->display, tekwin, tekscr->arrow);
2855 }
2856 #endif
2857
2858 if (screen->scrollWidget)
2859 ScrollBarReverseVideo(screen->scrollWidget);
2860
2861 if (XtIsRealized((Widget) xw)) {
2862 set_background(xw, -1);
2863 }
2864 #if OPT_TEK4014
2865 TekReverseVideo(xw, tekWidget);
2866 #endif
2867 if (XtIsRealized((Widget) xw)) {
2868 xtermRepaint(xw);
2869 }
2870 #if OPT_TEK4014
2871 if (TEK4014_SHOWN(xw)) {
2872 TekRepaint(tekWidget);
2873 }
2874 #endif
2875 ReverseOldColors(xw);
2876 set_cursor_gcs(xw);
2877 update_reversevideo();
2878 TRACE(("...ReverseVideo now %s\n", BtoS(xw->misc.re_verse)));
2879 }
2880
2881 void
recolor_cursor(TScreen * screen,Cursor cursor,unsigned long fg,unsigned long bg)2882 recolor_cursor(TScreen *screen,
2883 Cursor cursor, /* X cursor ID to set */
2884 unsigned long fg, /* pixel indexes to look up */
2885 unsigned long bg) /* pixel indexes to look up */
2886 {
2887 Display *dpy = screen->display;
2888 XColor colordefs[2]; /* 0 is foreground, 1 is background */
2889
2890 colordefs[0].pixel = fg;
2891 colordefs[1].pixel = bg;
2892 XQueryColors(dpy, DefaultColormap(dpy, DefaultScreen(dpy)),
2893 colordefs, 2);
2894 XRecolorCursor(dpy, cursor, colordefs, colordefs + 1);
2895 cleanup_colored_cursor();
2896 return;
2897 }
2898
2899 #if OPT_RENDERFONT
2900 #define XFT_CACHE_LIMIT ((unsigned)(~0) >> 1)
2901 #define XFT_CACHE_SIZE 16
2902 typedef struct {
2903 XftColor color;
2904 unsigned use;
2905 } XftColorCache;
2906
2907 static int
compare_xft_color_cache(const void * a,const void * b)2908 compare_xft_color_cache(const void *a, const void *b)
2909 {
2910 return (int) (((const XftColorCache *) a)->use -
2911 ((const XftColorCache *) b)->use);
2912 }
2913
2914 static XftColor *
getXftColor(XtermWidget xw,Pixel pixel)2915 getXftColor(XtermWidget xw, Pixel pixel)
2916 {
2917 static XftColorCache cache[XFT_CACHE_SIZE + 1];
2918 static unsigned latest_use;
2919 int i;
2920 int oldest;
2921 unsigned oldest_use;
2922 XColor color;
2923 Boolean found = False;
2924
2925 oldest_use = XFT_CACHE_LIMIT;
2926 oldest = 0;
2927 if (latest_use == XFT_CACHE_LIMIT) {
2928 latest_use = 0;
2929 qsort(cache, (size_t) XFT_CACHE_SIZE, sizeof(XftColorCache), compare_xft_color_cache);
2930 for (i = 0; i < XFT_CACHE_SIZE; i++) {
2931 if (cache[i].use) {
2932 cache[i].use = ++latest_use;
2933 }
2934 }
2935 }
2936 for (i = 0; i < XFT_CACHE_SIZE; i++) {
2937 if (cache[i].use) {
2938 if (cache[i].color.pixel == pixel) {
2939 found = True;
2940 break;
2941 }
2942 }
2943 if (cache[i].use < oldest_use) {
2944 oldest_use = cache[i].use;
2945 oldest = i;
2946 }
2947 }
2948 if (!found) {
2949 i = oldest;
2950 color.pixel = pixel;
2951 (void) QueryOneColor(xw, &color);
2952 cache[i].color.color.red = color.red;
2953 cache[i].color.color.green = color.green;
2954 cache[i].color.color.blue = color.blue;
2955 cache[i].color.color.alpha = 0xffff;
2956 cache[i].color.pixel = pixel;
2957 }
2958 cache[i].use = ++latest_use;
2959 return &cache[i].color;
2960 }
2961
2962 /*
2963 * The cell-width is related to, but not the same as the wide-character width.
2964 * We will only get useful values from wcwidth() for codes above 255.
2965 * Otherwise, interpret according to internal data.
2966 */
2967 #if OPT_RENDERWIDE
2968
2969 #if OPT_C1_PRINT
2970 #define XtermCellWidth(xw, ch) \
2971 (((ch) == 0 || (ch) == 127) \
2972 ? 0 \
2973 : (((ch) < 256) \
2974 ? (((ch) >= 128 && (ch) < 160) \
2975 ? (TScreenOf(xw)->c1_printable ? 1 : 0) \
2976 : 1) \
2977 : CharWidth(TScreenOf(xw), ch)))
2978 #else
2979 #define XtermCellWidth(xw, ch) \
2980 (((ch) == 0 || (ch) == 127) \
2981 ? 0 \
2982 : (((ch) < 256) \
2983 ? 1 \
2984 : CharWidth(TScreenOf(xw), ch)))
2985 #endif
2986
2987 #endif /* OPT_RENDERWIDE */
2988
2989 #define XFT_FONT(which) getXftFont(params->xw, which, fontnum)
2990
2991 #if OPT_ISO_COLORS
2992 #define UseBoldFont(screen) (!(screen)->colorBDMode || ((screen)->veryBoldColors & BOLD))
2993 #else
2994 #define UseBoldFont(screen) 1
2995 #endif
2996
2997 #if OPT_RENDERWIDE
2998 /*
2999 * Find Xft (truetype) double-width font for the given normal/bold attributes.
3000 */
3001 static XftFont *
getWideXftFont(XTermDraw * params,unsigned attr_flags)3002 getWideXftFont(XTermDraw * params,
3003 unsigned attr_flags)
3004 {
3005 TScreen *screen = TScreenOf(params->xw);
3006 int fontnum = screen->menu_font_number;
3007 XftFont *wfont = 0;
3008
3009 #if OPT_WIDE_ATTRS
3010 if ((attr_flags & ATR_ITALIC)
3011 #if OPT_ISO_COLORS
3012 && !screen->colorITMode
3013 #endif
3014 ) {
3015 if ((attr_flags & BOLDATTR(screen))
3016 && UseBoldFont(screen)
3017 && XFT_FONT(fWBtal)) {
3018 wfont = XFT_FONT(fWBtal);
3019 } else if (XFT_FONT(fWItal)) {
3020 wfont = XFT_FONT(fWItal);
3021 }
3022 }
3023 if (wfont != 0) {
3024 ; /* skip the other tests */
3025 } else
3026 #endif
3027 #if OPT_ISO_COLORS
3028 if ((attr_flags & UNDERLINE)
3029 && !screen->colorULMode
3030 && screen->italicULMode
3031 && XFT_FONT(fWItal)) {
3032 wfont = XFT_FONT(fWItal);
3033 } else
3034 #endif
3035 if ((attr_flags & BOLDATTR(screen))
3036 && UseBoldFont(screen)
3037 && XFT_FONT(fWBold)) {
3038 wfont = XFT_FONT(fWBold);
3039 } else {
3040 wfont = XFT_FONT(fWide);
3041 }
3042 return wfont;
3043 }
3044 #endif /* OPT_RENDERWIDE */
3045
3046 /*
3047 * Find Xft (truetype) single-width font for the given normal/bold attributes.
3048 */
3049 static XftFont *
getNormXftFont(XTermDraw * params,unsigned attr_flags,Bool * did_ul)3050 getNormXftFont(XTermDraw * params,
3051 unsigned attr_flags,
3052 Bool *did_ul)
3053 {
3054 TScreen *screen = TScreenOf(params->xw);
3055 int fontnum = screen->menu_font_number;
3056 XftFont *font = 0;
3057
3058 (void) did_ul;
3059 #if OPT_DEC_CHRSET
3060 if (CSET_DOUBLE(params->real_chrset)) {
3061 font = xterm_DoubleFT(params, params->real_chrset, attr_flags);
3062 }
3063 if (font != 0) {
3064 ; /* found a usable double-sized font */
3065 } else
3066 #endif
3067 #if OPT_WIDE_ATTRS
3068 if ((attr_flags & ATR_ITALIC)
3069 #if OPT_ISO_COLORS
3070 && !screen->colorITMode
3071 #endif
3072 ) {
3073 if ((attr_flags & BOLDATTR(screen))
3074 && UseBoldFont(screen)
3075 && XFT_FONT(fBtal)) {
3076 font = XFT_FONT(fBtal);
3077 } else if (XFT_FONT(fItal)) {
3078 font = XFT_FONT(fItal);
3079 }
3080 }
3081 if (font != 0) {
3082 ; /* skip the other tests */
3083 } else
3084 #endif
3085 #if OPT_ISO_COLORS
3086 if ((attr_flags & UNDERLINE)
3087 && !screen->colorULMode
3088 && screen->italicULMode
3089 && XFT_FONT(fItal)) {
3090 font = XFT_FONT(fItal);
3091 *did_ul = True;
3092 } else
3093 #endif
3094 if ((attr_flags & BOLDATTR(screen))
3095 && UseBoldFont(screen)
3096 && XFT_FONT(fBold)) {
3097 font = XFT_FONT(fBold);
3098 } else {
3099 font = XFT_FONT(fNorm);
3100 }
3101 return font;
3102 }
3103
3104 #if OPT_RENDERWIDE
3105 #define pickXftFont(width, nf, wf) ((width == 2 && wf != 0) ? wf : nf)
3106 #else
3107 #define pickXftFont(width, nf, wf) (nf)
3108 #endif
3109
3110 /*
3111 * fontconfig/Xft combination prior to 2.2 has a problem with
3112 * CJK truetype 'double-width' (bi-width/monospace) fonts leading
3113 * to the 's p a c e d o u t' rendering. Consequently, we can't
3114 * rely on XftDrawString8/16 when one of those fonts is used.
3115 * Instead, we need to roll out our own using XftDrawCharSpec.
3116 * A patch in the same spirit (but in a rather different form)
3117 * was applied to gnome vte and gtk2 port of vim.
3118 * See http://bugzilla.mozilla.org/show_bug.cgi?id=196312
3119 */
3120 static int
xtermXftDrawString(XTermDraw * params,unsigned attr_flags,XftColor * color,XftFont * font,int x,int y,const IChar * text,Cardinal len,Bool really)3121 xtermXftDrawString(XTermDraw * params,
3122 unsigned attr_flags,
3123 XftColor *color,
3124 XftFont *font,
3125 int x,
3126 int y,
3127 const IChar *text,
3128 Cardinal len,
3129 Bool really)
3130 {
3131 TScreen *screen = TScreenOf(params->xw);
3132 int ncells = 0;
3133
3134 (void) attr_flags;
3135 if (len != 0) {
3136 #if OPT_RENDERWIDE
3137 XftCharSpec *sbuf;
3138 XftFont *wfont = getWideXftFont(params, attr_flags);
3139 Cardinal src, dst;
3140 XftFont *lastFont = 0;
3141 XftFont *currFont = 0;
3142 Cardinal start = 0;
3143 int charWidth;
3144 int fwidth = FontWidth(screen);
3145 #if OPT_DEC_CHRSET
3146 Boolean forceDbl = CSET_DOUBLE(params->real_chrset);
3147 #else
3148 Boolean forceDbl = False;
3149 #endif
3150
3151 BumpTypedBuffer(XftCharSpec, 2 * len);
3152 sbuf = BfBuf(XftCharSpec);
3153
3154 for (src = dst = 0; src < len; src++) {
3155 FcChar32 wc = *text++;
3156
3157 charWidth = XtermCellWidth(params->xw, (wchar_t) wc);
3158 if (charWidth < 0)
3159 continue;
3160
3161 sbuf[dst].ucs4 = wc;
3162 sbuf[dst].x = (short) (x + fwidth * ncells);
3163 sbuf[dst].y = (short) (y);
3164
3165 currFont = pickXftFont(charWidth, font, wfont);
3166 ncells += charWidth;
3167
3168 if (lastFont != currFont) {
3169 if ((lastFont != 0) && really) {
3170 XftDrawCharSpec(screen->renderDraw,
3171 color,
3172 lastFont,
3173 sbuf + start,
3174 (int) (dst - start));
3175 }
3176 start = dst;
3177 lastFont = currFont;
3178 }
3179 ++dst;
3180
3181 if (forceDbl && charWidth < 2) {
3182 sbuf[dst].ucs4 = ' ';
3183 sbuf[dst].x = (short) (x + fwidth * ncells);
3184 sbuf[dst].y = (short) (y);
3185 ++dst;
3186 ncells += charWidth;
3187 }
3188 }
3189 if ((dst != start) && really) {
3190 XftDrawCharSpec(screen->renderDraw,
3191 color,
3192 lastFont,
3193 sbuf + start,
3194 (int) (dst - start));
3195 }
3196 #else /* !OPT_RENDERWIDE */
3197 if (really) {
3198 XftChar8 *buffer;
3199 int dst;
3200
3201 BumpTypedBuffer(XftChar8, len);
3202 buffer = BfBuf(XftChar8);
3203
3204 for (dst = 0; dst < (int) len; ++dst)
3205 buffer[dst] = CharOf(text[dst]);
3206
3207 XftDrawString8(screen->renderDraw,
3208 color,
3209 font,
3210 x, y, buffer, (int) len);
3211 }
3212 ncells = (int) len;
3213 #endif
3214 xtermNeedSwap(params->xw, 1);
3215 }
3216 return ncells;
3217 }
3218 #define xtermXftWidth(params, attr_flags, color, font, x, y, chars, len) \
3219 xtermXftDrawString(params, attr_flags, color, font, x, y, chars, len, False)
3220 #endif /* OPT_RENDERFONT */
3221
3222 #if OPT_WIDE_CHARS
3223 /*
3224 * Map characters commonly "fixed" by groff back to their ASCII equivalents.
3225 * Also map other useful equivalents.
3226 */
3227 unsigned
AsciiEquivs(unsigned ch)3228 AsciiEquivs(unsigned ch)
3229 {
3230 switch (ch) {
3231 case 0x2010: /* groff "-" */
3232 case 0x2011:
3233 case 0x2012:
3234 case 0x2013:
3235 case 0x2014:
3236 case 0x2015:
3237 case 0x2212: /* groff "\-" */
3238 ch = '-';
3239 break;
3240 case 0x2018: /* groff "`" */
3241 ch = '`';
3242 break;
3243 case 0x2019: /* groff ' */
3244 ch = '\'';
3245 break;
3246 case 0x201C: /* groff lq */
3247 case 0x201D: /* groff rq */
3248 ch = '"';
3249 break;
3250 case 0x2329: /* groff ".URL" */
3251 ch = '<';
3252 break;
3253 case 0x232a: /* groff ".URL" */
3254 ch = '>';
3255 break;
3256 default:
3257 if (ch >= 0xff01 && ch <= 0xff5e) {
3258 /* "Fullwidth" codes (actually double-width) */
3259 ch -= 0xff00;
3260 ch += ANSI_SPA;
3261 break;
3262 }
3263 }
3264 return ch;
3265 }
3266
3267 /*
3268 * Actually this should be called "groff_workaround()" - for the places where
3269 * groff stomps on compatibility. Still, if enough people get used to it,
3270 * this might someday become a quasi-standard.
3271 */
3272 #if OPT_BOX_CHARS
3273 static int
ucs_workaround(XTermDraw * params,unsigned ch,GC gc,int x,int y)3274 ucs_workaround(XTermDraw * params,
3275 unsigned ch,
3276 GC gc,
3277 int x,
3278 int y)
3279 {
3280 TScreen *screen = TScreenOf(params->xw);
3281 int fixed = False;
3282
3283 if (screen->wide_chars && screen->utf8_mode && ch > 256) {
3284 IChar eqv = (IChar) AsciiEquivs(ch);
3285
3286 if (eqv != (IChar) ch) {
3287 int width = CharWidth(screen, ch);
3288
3289 do {
3290 drawXtermText(params,
3291 gc,
3292 x,
3293 y,
3294 &eqv,
3295 1);
3296 x += FontWidth(screen);
3297 eqv = BAD_ASCII;
3298 } while (width-- > 1);
3299
3300 fixed = True;
3301 } else if (ch == HIDDEN_CHAR) {
3302 fixed = True;
3303 }
3304 }
3305 return fixed;
3306 }
3307 #endif /* OPT_BOX_CHARS */
3308 #endif /* OPT_WIDE_CHARS */
3309
3310 /*
3311 * Use this when the characters will not fill the cell area properly. Fill the
3312 * area where we'll write the characters, otherwise we'll get gaps between
3313 * them, e.g., in the original background color.
3314 *
3315 * The cursor is a special case, because the XFillRectangle call only uses the
3316 * foreground, while we've set the cursor color in the background. So we need
3317 * a special GC for that.
3318 */
3319 static void
xtermFillCells(XTermDraw * params,GC gc,int x,int y,Cardinal len)3320 xtermFillCells(XTermDraw * params,
3321 GC gc,
3322 int x,
3323 int y,
3324 Cardinal len)
3325 {
3326 TScreen *screen = TScreenOf(params->xw);
3327 VTwin *currentWin = WhichVWin(screen);
3328
3329 if (!(params->draw_flags & NOBACKGROUND)) {
3330 CgsEnum srcId = getCgsId(params->xw, currentWin, gc);
3331 CgsEnum dstId = gcMAX;
3332 Pixel fg = getCgsFore(params->xw, currentWin, gc);
3333 Pixel bg = getCgsBack(params->xw, currentWin, gc);
3334
3335 switch (srcId) {
3336 case gcVTcursNormal:
3337 case gcVTcursReverse:
3338 dstId = gcVTcursOutline;
3339 break;
3340 case gcVTcursFilled:
3341 case gcVTcursOutline:
3342 /* FIXME */
3343 break;
3344 case gcNorm:
3345 dstId = gcNormReverse;
3346 break;
3347 case gcNormReverse:
3348 dstId = gcNorm;
3349 break;
3350 case gcBold:
3351 dstId = gcBoldReverse;
3352 break;
3353 case gcBoldReverse:
3354 dstId = gcBold;
3355 break;
3356 case gcBorder:
3357 case gcFiller:
3358 dstId = srcId;
3359 break;
3360 #if OPT_BOX_CHARS
3361 case gcLine:
3362 case gcDots:
3363 /* FIXME */
3364 break;
3365 #endif
3366 #if OPT_DEC_CHRSET
3367 case gcCNorm:
3368 case gcCBold:
3369 /* FIXME */
3370 break;
3371 #endif
3372 #if OPT_WIDE_CHARS
3373 case gcWide:
3374 dstId = gcWideReverse;
3375 break;
3376 case gcWBold:
3377 dstId = gcBoldReverse;
3378 break;
3379 case gcWideReverse:
3380 case gcWBoldReverse:
3381 /* FIXME */
3382 break;
3383 #endif
3384 #if OPT_TEK4014
3385 case gcTKcurs:
3386 /* FIXME */
3387 break;
3388 #endif
3389 case gcMAX:
3390 break;
3391 }
3392
3393 if (dstId != gcMAX) {
3394 setCgsFore(params->xw, currentWin, dstId, bg);
3395 setCgsBack(params->xw, currentWin, dstId, fg);
3396
3397 XFillRectangle(screen->display, VDrawable(screen),
3398 getCgsGC(params->xw, currentWin, dstId),
3399 x, y,
3400 len * (Cardinal) FontWidth(screen),
3401 (unsigned) FontHeight(screen));
3402 }
3403 }
3404 }
3405
3406 #if OPT_TRACE
3407 static void
xtermSetClipRectangles(Display * dpy,GC gc,int x,int y,XRectangle * rp,Cardinal nr,int order)3408 xtermSetClipRectangles(Display *dpy,
3409 GC gc,
3410 int x,
3411 int y,
3412 XRectangle * rp,
3413 Cardinal nr,
3414 int order)
3415 {
3416 #if 0
3417 TScreen *screen = TScreenOf(term);
3418 Drawable draw = VDrawable(screen);
3419
3420 XSetClipMask(dpy, gc, None);
3421 XDrawRectangle(screen->display, draw, gc,
3422 x + rp->x - 1,
3423 y + rp->y - 1,
3424 rp->width,
3425 rp->height);
3426 #endif
3427
3428 XSetClipRectangles(dpy, gc,
3429 x, y, rp, (int) nr, order);
3430 TRACE(("clipping @(%3d,%3d) (%3d,%3d)..(%3d,%3d)\n",
3431 y, x,
3432 rp->y, rp->x, rp->height, rp->width));
3433 }
3434
3435 #else
3436 #define xtermSetClipRectangles(dpy, gc, x, y, rp, nr, order) \
3437 XSetClipRectangles(dpy, gc, x, y, rp, (int) nr, order)
3438 #endif
3439
3440 #if OPT_CLIP_BOLD
3441 /*
3442 * This special case is a couple of percent slower, but avoids a lot of pixel
3443 * trash in rxcurses' hanoi.cmd demo (e.g., 10x20 font).
3444 */
3445 #define beginClipping(screen,gc,pwidth,plength) \
3446 if (pwidth > 2) { \
3447 if (screen->use_clipping) { \
3448 XRectangle clip; \
3449 int clip_x = x; \
3450 int clip_y = y - FontHeight(screen) + FontDescent(screen); \
3451 clip.x = 0; \
3452 clip.y = 0; \
3453 clip.height = (unsigned short) FontHeight(screen); \
3454 clip.width = (unsigned short) ((pwidth) * (plength)); \
3455 xtermSetClipRectangles(screen->display, gc, \
3456 clip_x, clip_y, \
3457 &clip, 1, Unsorted); \
3458 } else if (screen->use_border_clipping) { \
3459 XRectangle clip; \
3460 clip.x = 0; \
3461 clip.y = 0; \
3462 clip.height = (unsigned short) Height(screen); \
3463 clip.width = (unsigned short) Width(screen); \
3464 xtermSetClipRectangles(screen->display, gc, \
3465 0, 0, \
3466 &clip, 1, Unsorted); \
3467 } \
3468 }
3469 #define endClipping(screen,gc) \
3470 XSetClipMask(screen->display, gc, None)
3471 #else
3472 #define beginClipping(screen,gc,pwidth,plength) /* nothing */
3473 #define endClipping(screen,gc) /* nothing */
3474 #endif /* OPT_CLIP_BOLD */
3475
3476 #if OPT_RENDERFONT
3477 static int
drawClippedXftString(XTermDraw * params,unsigned attr_flags,XftFont * font,XftColor * fg_color,int x,int y,const IChar * text,Cardinal len)3478 drawClippedXftString(XTermDraw * params,
3479 unsigned attr_flags,
3480 XftFont *font,
3481 XftColor *fg_color,
3482 int x,
3483 int y,
3484 const IChar *text,
3485 Cardinal len)
3486 {
3487 int ncells = xtermXftWidth(params, attr_flags,
3488 fg_color,
3489 font, x, y,
3490 text,
3491 len);
3492 TScreen *screen = TScreenOf(params->xw);
3493 int fontHigh = FontHeight(screen);
3494 int fontWide = FontWidth(screen);
3495
3496 if (fontWide > 2) {
3497 int plength = (ncells ? ncells : 1);
3498 Boolean halfHigh = False;
3499 #if OPT_DEC_CHRSET
3500 Boolean halfWide = False;
3501
3502 switch (params->real_chrset) {
3503 case CSET_SWL:
3504 break;
3505 case CSET_DWL:
3506 halfWide = True;
3507 break;
3508 case CSET_DHL_TOP:
3509 halfHigh = True;
3510 halfWide = True;
3511 break;
3512 case CSET_DHL_BOT:
3513 halfHigh = True;
3514 halfWide = True;
3515 break;
3516 }
3517 if (CSET_DOUBLE(params->real_chrset)) {
3518 fontHigh = font->height;
3519 fontWide = font->max_advance_width;
3520 /* check if this is really a double-height font */
3521 if (halfHigh) {
3522 int wantHigh = (int) (FontHeight(screen) * 1.8);
3523 halfHigh = (fontHigh >= wantHigh);
3524 TRACE(("comparing fontHigh %d/%d vs %d:"
3525 " double-height %s for %s\n",
3526 fontHigh, FontHeight(screen),
3527 wantHigh, BtoS(halfHigh),
3528 visibleDblChrset(params->real_chrset)));
3529 }
3530 fontHigh = (halfHigh
3531 ? (2 * FontHeight(screen))
3532 : FontHeight(screen));
3533 /* check if this is really a double-width font */
3534 if (halfWide) {
3535 int wantWide = (int) (FontWidth(screen) * 1.8);
3536 halfWide = (fontWide >= wantWide);
3537 TRACE(("comparing fontWide %d/%d vs %d:"
3538 " double-width %s for %s\n",
3539 fontWide, FontWidth(screen),
3540 wantWide, BtoS(halfWide),
3541 visibleDblChrset(params->real_chrset)));
3542 }
3543 fontWide = (halfWide
3544 ? (2 * FontWidth(screen))
3545 : FontWidth(screen));
3546 }
3547 #endif
3548 if (screen->use_clipping || halfHigh) {
3549 XRectangle clip;
3550 double adds = ((double) screen->scale_height - 1.0f) * fontHigh;
3551 int height = dimRound(adds + fontHigh);
3552 int descnt = dimRound(adds / 2.0) + FontDescent(screen);
3553 int clip_x = (x);
3554 int clip_y = (y) - height + descnt;
3555
3556 clip.x = 0;
3557 clip.y = 0;
3558 clip.height = (Dimension) height;
3559 clip.width = (Dimension) (fontWide * (Dimension) (plength));
3560
3561 #if OPT_DEC_CHRSET
3562 if (halfHigh) {
3563 int adjust;
3564
3565 clip.height = (unsigned short) FontHeight(screen);
3566 clip_y += descnt;
3567 if (params->real_chrset == CSET_DHL_BOT) {
3568 y -= clip.height;
3569 }
3570 adjust = ((clip_y - OriginY(screen)) % FontHeight(screen));
3571 if (adjust) {
3572 if (adjust > FontHeight(screen) / 2)
3573 adjust -= FontHeight(screen);
3574 clip_y -= adjust;
3575 }
3576 }
3577 #endif
3578 XftDrawSetClipRectangles(screen->renderDraw,
3579 clip_x, clip_y,
3580 &clip, 1);
3581 } else if (screen->use_border_clipping) {
3582 XRectangle clip;
3583
3584 clip.x = (Position) OriginX(screen);
3585 clip.y = (Position) OriginY(screen);
3586 clip.height = (Dimension) Height(screen);
3587 clip.width = (Dimension) Width(screen);
3588
3589 XftDrawSetClipRectangles(screen->renderDraw,
3590 0, 0,
3591 &clip, 1);
3592 }
3593 }
3594
3595 xtermXftDrawString(params, attr_flags,
3596 fg_color,
3597 font, x, y + ScaleShift(screen),
3598 text,
3599 len,
3600 True);
3601 XftDrawSetClip(screen->renderDraw, 0);
3602 return ncells;
3603 }
3604 #endif
3605
3606 #ifndef NO_ACTIVE_ICON
3607 #define WhichVFontData(screen,name) \
3608 (IsIcon(screen) ? getIconicFont(screen) \
3609 : GetNormalFont(screen, name))
3610 #else
3611 #define WhichVFontData(screen,name) \
3612 GetNormalFont(screen, name)
3613 #endif
3614
3615 static void
drawUnderline(XtermWidget xw,GC gc,unsigned attr_flags,unsigned underline_len,int font_width,int x,int y,Bool did_ul)3616 drawUnderline(XtermWidget xw,
3617 GC gc,
3618 unsigned attr_flags,
3619 unsigned underline_len,
3620 int font_width,
3621 int x,
3622 int y,
3623 Bool did_ul)
3624 {
3625 TScreen *screen = TScreenOf(xw);
3626
3627 if (screen->underline && !did_ul) {
3628 int repeat = 0;
3629 int descent = FontDescent(screen);
3630 int length = x + (int) underline_len * font_width - 1;
3631
3632 #if OPT_WIDE_ATTRS
3633 if ((attr_flags & ATR_STRIKEOUT)) {
3634 int where = y - ((3 * FontAscent(screen)) / 8);
3635 XDrawLine(screen->display, VDrawable(screen), gc,
3636 x, where,
3637 length,
3638 where);
3639 }
3640 if ((attr_flags & ATR_DBL_UNDER)) {
3641 repeat = 2;
3642 } else
3643 #endif
3644 if ((attr_flags & UNDERLINE)) {
3645 repeat = 1;
3646 }
3647 while (repeat-- > 0) {
3648 if (descent-- > 1)
3649 y++;
3650 XDrawLine(screen->display, VDrawable(screen), gc,
3651 x, y,
3652 length,
3653 y);
3654 }
3655 }
3656 }
3657
3658 #if OPT_WIDE_ATTRS
3659 /*
3660 * As a special case, we are currently allowing italic fonts to be inexact
3661 * matches for the normal font's size. That introduces a problem: either the
3662 * ascent or descent may be shorter, leaving a gap that has to be filled in.
3663 * Or they may be larger, requiring clipping. Check for both cases.
3664 */
3665 static int
fixupItalics(XTermDraw * params,GC gc,XTermFonts * curFont,int y,int x,int font_width,Cardinal len)3666 fixupItalics(XTermDraw * params,
3667 GC gc,
3668 XTermFonts * curFont,
3669 int y, int x,
3670 int font_width,
3671 Cardinal len)
3672 {
3673 TScreen *screen = TScreenOf(params->xw);
3674 VTwin *cgsWin = WhichVWin(screen);
3675 XFontStruct *realFp = curFont->fs;
3676 XFontStruct *thisFp = getCgsFont(params->xw, cgsWin, gc)->fs;
3677 int need_clipping = 0;
3678 int need_filling = 0;
3679
3680 if (thisFp->ascent > realFp->ascent)
3681 need_clipping = 1;
3682 else if (thisFp->ascent < realFp->ascent)
3683 need_filling = 1;
3684
3685 if (thisFp->descent > realFp->descent)
3686 need_clipping = 1;
3687 else if (thisFp->descent < realFp->descent)
3688 need_filling = 1;
3689
3690 if (need_clipping) {
3691 beginClipping(screen, gc, font_width, (int) len);
3692 }
3693 if (need_filling) {
3694 xtermFillCells(params,
3695 gc,
3696 x,
3697 y - realFp->ascent,
3698 len);
3699 }
3700 return need_clipping;
3701 }
3702 #endif
3703
3704 #if OPT_DEC_CHRSET
3705 static int
fakeDoubleChars(XTermDraw * params,GC gc,int y,int x,const IChar * text,Cardinal len)3706 fakeDoubleChars(XTermDraw * params,
3707 GC gc,
3708 int y,
3709 int x,
3710 const IChar *text,
3711 Cardinal len)
3712 {
3713 unsigned need = 2 * len;
3714 IChar *temp = TypeMallocN(IChar, need);
3715
3716 if (temp != 0) {
3717 unsigned n = 0;
3718 XTermDraw recur = *params;
3719
3720 recur.this_chrset = CSET_SWL;
3721
3722 while (len--) {
3723 temp[n++] = *text++;
3724 temp[n++] = ' ';
3725 }
3726 x = drawXtermText(&recur,
3727 gc,
3728 x, y,
3729 temp,
3730 n);
3731 free(temp);
3732 }
3733 return x;
3734 }
3735 #endif /* OPT_DEC_CHRSET */
3736
3737 #define SetMissing(tag) \
3738 TRACE(("%s %s: missing %d %04X\n", __FILE__, tag, missing, ch)); \
3739 missing = 1
3740
3741 #define MaxImageString 255
3742
3743 /*
3744 * Draws text with the specified combination of bold/underline. The return
3745 * value is the updated x position.
3746 */
3747 int
drawXtermText(XTermDraw * params,GC gc,int start_x,int start_y,const IChar * text,Cardinal len)3748 drawXtermText(XTermDraw * params,
3749 GC gc,
3750 int start_x,
3751 int start_y,
3752 const IChar *text,
3753 Cardinal len)
3754 {
3755 XTermDraw recur = *params;
3756 TScreen *screen = TScreenOf(recur.xw);
3757 int x = start_x;
3758 int y = start_y;
3759 int y_shift = ScaleShift(screen);
3760 Cardinal real_length = len;
3761 Cardinal underline_len = 0;
3762 /* Intended width of the font to draw (as opposed to the actual width of
3763 the X font, and the width of the default font) */
3764 int font_width = (((recur.draw_flags & DOUBLEWFONT) ? 2 : 1)
3765 * screen->fnt_wide);
3766 Bool did_ul = False;
3767 XTermFonts *curFont;
3768 #if OPT_WIDE_ATTRS
3769 int need_clipping = 0;
3770 #endif
3771
3772 #if OPT_WIDE_CHARS
3773 if (text == 0)
3774 return 0;
3775 #endif
3776 TRACE(("DRAWTEXT%c[%4d,%4d] (%d)%3d:%s\n",
3777 screen->cursor_state == OFF ? ' ' : '*',
3778 y, x, recur.this_chrset, len,
3779 visibleIChars(text, len)));
3780
3781 #if OPT_DEC_CHRSET
3782 if (CSET_DOUBLE(recur.this_chrset)) {
3783 /* We could try drawing double-size characters in the icon, but
3784 * given that the icon font is usually nil or nil2, there
3785 * doesn't seem to be much point.
3786 */
3787 int inx = 0;
3788 GC gc2;
3789
3790 #if OPT_RENDERFONT
3791 if (UsingRenderFont(recur.xw)) {
3792 if (!IsIcon(screen) && screen->font_doublesize) {
3793 TRACE(("drawing %s\n", visibleDblChrset((unsigned) recur.this_chrset)));
3794 recur.real_chrset = recur.this_chrset;
3795 recur.this_chrset = CSET_SWL;
3796 x = drawXtermText(&recur,
3797 gc,
3798 x, y,
3799 text,
3800 len);
3801 x += ((int) len) * FontWidth(screen);
3802 } else {
3803 x = fakeDoubleChars(&recur,
3804 gc, y, x,
3805 text, len);
3806 }
3807 } else
3808 #endif
3809 if ((!IsIcon(screen) && screen->font_doublesize)
3810 && (gc2 = xterm_DoubleGC(&recur, gc, &inx)) != 0) {
3811 /* draw actual double-sized characters */
3812 XFontStruct *fs = getDoubleFont(screen, inx)->fs;
3813 XRectangle rect, *rp = ▭
3814 Cardinal nr = 1;
3815
3816 font_width *= 2;
3817 recur.draw_flags |= DOUBLEWFONT;
3818
3819 rect.x = 0;
3820 rect.y = 0;
3821 rect.width = (unsigned short) ((int) len * font_width);
3822 rect.height = (unsigned short) (FontHeight(screen));
3823
3824 TRACE(("drawing %s\n", visibleDblChrset((unsigned) recur.this_chrset)));
3825 switch (recur.this_chrset) {
3826 case CSET_DHL_TOP:
3827 rect.y = (short) -(fs->ascent / 2);
3828 y -= rect.y;
3829 recur.draw_flags |= DOUBLEHFONT;
3830 break;
3831 case CSET_DHL_BOT:
3832 rect.y = (short) (rect.height - (fs->ascent / 2));
3833 y -= rect.y;
3834 recur.draw_flags |= DOUBLEHFONT;
3835 break;
3836 case CSET_DWL:
3837 break;
3838 default:
3839 nr = 0;
3840 break;
3841 }
3842
3843 if (nr) {
3844 xtermSetClipRectangles(screen->display, gc2,
3845 x, y, rp, nr, YXBanded);
3846 xtermFillCells(&recur, gc, x, y + rect.y, len * 2);
3847 } else {
3848 XSetClipMask(screen->display, gc2, None);
3849 }
3850
3851 /* Call ourselves recursively with the new gc */
3852
3853 /*
3854 * If we're trying to use proportional font, or if the
3855 * font server didn't give us what we asked for wrt
3856 * width, position each character independently.
3857 */
3858 if (screen->fnt_prop
3859 || (fs->min_bounds.width != fs->max_bounds.width)
3860 || (fs->min_bounds.width != 2 * FontWidth(screen))) {
3861 /* It is hard to fall-through to the main
3862 branch: in a lot of places the check
3863 for the cached font info is for
3864 normal/bold fonts only. */
3865 XTermDraw param2 = recur;
3866 param2.this_chrset = CSET_SWL;
3867 while (len--) {
3868 x = drawXtermText(¶m2,
3869 gc2,
3870 x, y,
3871 text++,
3872 1);
3873 x += FontWidth(screen);
3874 }
3875 } else {
3876 XTermDraw param2 = recur;
3877 param2.this_chrset = CSET_SWL;
3878 x = drawXtermText(¶m2,
3879 gc2,
3880 x, y,
3881 text,
3882 len);
3883 x += (int) len *FontWidth(screen);
3884 }
3885
3886 } else { /* simulate double-sized characters */
3887 x = fakeDoubleChars(&recur,
3888 gc, y, x,
3889 text, len);
3890 }
3891 TRACE(("DrewText [%4d,%4d]\n", y, x));
3892 return x;
3893 }
3894 #endif
3895 #if OPT_RENDERFONT
3896 if (UsingRenderFont(recur.xw)) {
3897 VTwin *currentWin = WhichVWin(screen);
3898 Display *dpy = screen->display;
3899 XftFont *font;
3900 XftFont *font0;
3901 XGCValues values;
3902 #if OPT_RENDERWIDE
3903 XftFont *wfont;
3904 XftFont *wfont0;
3905 #endif
3906
3907 #if OPT_DEC_CHRSET
3908 /*
3909 * If this is a VT100-style double-width font, ensure that everything
3910 * is printed using two-columns per character.
3911 */
3912 Boolean forceDbl = CSET_DOUBLE(recur.real_chrset);
3913 #else
3914 Boolean forceDbl = False;
3915 #endif
3916
3917 (void) forceDbl;
3918 /*
3919 * Defer creating the drawable until we need it.
3920 */
3921 if (!screen->renderDraw) {
3922 int scr;
3923 Drawable draw = VDrawable(screen);
3924 Visual *visual;
3925
3926 scr = DefaultScreen(dpy);
3927 visual = DefaultVisual(dpy, scr);
3928 screen->renderDraw = XftDrawCreate(dpy, draw, visual,
3929 DefaultColormap(dpy, scr));
3930 }
3931
3932 /*
3933 * font0/wfont0 provide fallback to non-bolded Xft font if a glyph is
3934 * not found in the bold version.
3935 */
3936 #define IS_BOLD (recur.attr_flags & BOLDATTR(screen))
3937 #define NOT_BOLD (recur.attr_flags & ~BOLDATTR(screen))
3938 font = getNormXftFont(&recur, recur.attr_flags, &did_ul);
3939 font0 = IS_BOLD ? getNormXftFont(&recur, NOT_BOLD, &did_ul) : font;
3940 (void) font0;
3941 #if OPT_RENDERWIDE
3942 wfont = getWideXftFont(&recur, recur.attr_flags);
3943 wfont0 = IS_BOLD ? getWideXftFont(&recur, NOT_BOLD) : wfont;
3944 #endif
3945
3946 #define GET_XFT_FG() getXftColor(recur.xw, values.foreground)
3947 #define GET_XFT_BG() getXftColor(recur.xw, values.background)
3948
3949 values.foreground = getCgsFore(recur.xw, currentWin, gc);
3950 values.background = getCgsBack(recur.xw, currentWin, gc);
3951
3952 if (!(recur.draw_flags & NOBACKGROUND)) {
3953 XftColor *bg_color = GET_XFT_BG();
3954 int ncells = xtermXftWidth(&recur, recur.attr_flags,
3955 bg_color,
3956 font, x, y,
3957 text,
3958 len);
3959 XftDrawRect(screen->renderDraw,
3960 bg_color,
3961 x, y,
3962 (unsigned) (ncells * FontWidth(screen)),
3963 (unsigned) FontHeight(screen));
3964 }
3965
3966 y += font->ascent;
3967 #if OPT_BOX_CHARS
3968 {
3969 /* adding code to substitute simulated line-drawing characters */
3970 int last, first = 0;
3971 int curX = x;
3972
3973 for (last = 0; last < (int) len; last++) {
3974 Boolean replace = False;
3975 Boolean missing = False;
3976 unsigned ch = (unsigned) text[last];
3977 int filler = 0;
3978 #if OPT_WIDE_CHARS
3979 int needed = forceDbl ? 2 : CharWidth(screen, ch);
3980 XftFont *currFont = pickXftFont(needed, font, wfont);
3981 XftFont *tempFont = 0;
3982 #define CURR_TEMP (tempFont ? tempFont : currFont)
3983
3984 if (xtermIsDecGraphic(ch) || ch == 0) {
3985 /*
3986 * Xft generally does not have the line-drawing characters
3987 * in cells 0-31. Assume this (we cannot inspect the
3988 * picture easily...), and attempt to fill in from real
3989 * line-drawing character in the font at the Unicode
3990 * position. Failing that, use our own box-characters.
3991 */
3992 if (screen->force_box_chars
3993 || screen->broken_box_chars
3994 || xtermXftMissing(recur.xw,
3995 currFont,
3996 dec2ucs(screen, ch))) {
3997 SetMissing("case 1");
3998 } else {
3999 ch = dec2ucs(screen, ch);
4000 replace = True;
4001 }
4002 } else if (ch >= 256) {
4003 /*
4004 * If we're reading UTF-8 from the client, we may have a
4005 * line-drawing character. Translate it back to our
4006 * box-code if Xft tells us that the glyph is missing.
4007 */
4008 if_OPT_WIDE_CHARS(screen, {
4009 unsigned part = ucs2dec(screen, ch);
4010 if (xtermIsDecGraphic(part)) {
4011 if (screen->force_box_chars
4012 || screen->broken_box_chars) {
4013 SetMissing("case 2");
4014 ch = part;
4015 }
4016 }
4017 if (!missing && xtermXftMissing(recur.xw, currFont, ch)) {
4018 XftFont *test = findXftGlyph(recur.xw, currFont, ch);
4019 if (test == 0)
4020 test = pickXftFont(needed, font0, wfont0);
4021 if (!xtermXftMissing(recur.xw, test, ch)) {
4022 tempFont = test;
4023 replace = True;
4024 filler = 0;
4025 } else if ((part = AsciiEquivs(ch)) != ch) {
4026 filler = needed - 1;
4027 ch = part;
4028 replace = True;
4029 } else if (ch != HIDDEN_CHAR) {
4030 SetMissing("case 3");
4031 }
4032 }
4033 });
4034 }
4035 #else
4036 XftFont *currFont = font;
4037 #define CURR_TEMP (currFont)
4038 if (xtermIsDecGraphic(ch)) {
4039 /*
4040 * Xft generally does not have the line-drawing characters
4041 * in cells 1-31. Check for this, and attempt to fill in
4042 * from real line-drawing character in the font at the
4043 * Unicode position. Failing that, use our own
4044 * box-characters.
4045 */
4046 if (xtermXftMissing(recur.xw, currFont, ch)) {
4047 SetMissing("case 4");
4048 }
4049 }
4050 #endif
4051
4052 /*
4053 * If we now have one of our box-codes, draw it directly.
4054 */
4055 if (missing || replace) {
4056 /* line drawing character time */
4057 if (last > first) {
4058 int nc = drawClippedXftString(&recur,
4059 recur.attr_flags,
4060 currFont,
4061 GET_XFT_FG(),
4062 curX,
4063 y,
4064 text + first,
4065 (Cardinal) (last - first));
4066 curX += nc * FontWidth(screen);
4067 underline_len += (Cardinal) nc;
4068 }
4069 if (missing) {
4070 Dimension old_wide = screen->fnt_wide;
4071 Dimension old_high = screen->fnt_high;
4072 screen->fnt_wide = (Dimension) FontWidth(screen);
4073 screen->fnt_high = (Dimension) FontHeight(screen);
4074
4075 xtermDrawBoxChar(&recur, ch,
4076 gc,
4077 curX,
4078 y - FontAscent(screen),
4079 1,
4080 True);
4081 curX += FontWidth(screen);
4082 underline_len += 1;
4083 screen->fnt_wide = old_wide;
4084 screen->fnt_high = old_high;
4085 } else {
4086 IChar ch2 = (IChar) ch;
4087 int nc = drawClippedXftString(&recur,
4088 recur.attr_flags,
4089 CURR_TEMP,
4090 GET_XFT_FG(),
4091 curX,
4092 y,
4093 &ch2,
4094 1);
4095 curX += nc * FontWidth(screen);
4096 underline_len += (Cardinal) nc;
4097 if (filler) {
4098 ch2 = ' ';
4099 nc = drawClippedXftString(&recur,
4100 recur.attr_flags,
4101 CURR_TEMP,
4102 GET_XFT_FG(),
4103 curX,
4104 y,
4105 &ch2,
4106 1);
4107 curX += nc * FontWidth(screen);
4108 underline_len += (Cardinal) nc;
4109 }
4110 }
4111 first = last + 1;
4112 }
4113 }
4114 if (last > first) {
4115 underline_len += (Cardinal)
4116 drawClippedXftString(&recur,
4117 recur.attr_flags,
4118 font,
4119 GET_XFT_FG(),
4120 curX,
4121 y,
4122 text + first,
4123 (Cardinal) (last - first));
4124 }
4125 }
4126 #else
4127 {
4128 underline_len += (Cardinal)
4129 drawClippedXftString(&recur,
4130 recur.attr_flags,
4131 font,
4132 GET_XFT_FG(),
4133 x,
4134 y,
4135 text,
4136 len);
4137 }
4138 #endif /* OPT_BOX_CHARS */
4139
4140 drawUnderline(recur.xw,
4141 gc,
4142 recur.attr_flags,
4143 underline_len,
4144 FontWidth(screen),
4145 x,
4146 y + y_shift,
4147 did_ul);
4148
4149 x += (int) len *FontWidth(screen);
4150
4151 return x;
4152 }
4153 #endif /* OPT_RENDERFONT */
4154 curFont = ((recur.attr_flags & BOLDATTR(screen))
4155 ? WhichVFontData(screen, fBold)
4156 : WhichVFontData(screen, fNorm));
4157 /*
4158 * If we're asked to display a proportional font, do this with a fixed
4159 * pitch. Yes, it's ugly. But we cannot distinguish the use of xterm
4160 * as a dumb terminal vs its use as in fullscreen programs such as vi.
4161 * Hint: do not try to use a proportional font in the icon.
4162 */
4163 if (!IsIcon(screen) && !(recur.draw_flags & CHARBYCHAR) && screen->fnt_prop) {
4164 int adj, width;
4165
4166 while (len--) {
4167 int cells = WideCells(*text);
4168 #if OPT_BOX_CHARS
4169 #if OPT_WIDE_CHARS
4170 if (*text == HIDDEN_CHAR) {
4171 ++text;
4172 continue;
4173 } else
4174 #endif
4175 if (IsXtermMissingChar(screen, *text, curFont)) {
4176 adj = 0;
4177 } else
4178 #endif
4179 {
4180 if_WIDE_OR_NARROW(screen, {
4181 XChar2b temp[1];
4182 temp[0].byte2 = LO_BYTE(*text);
4183 temp[0].byte1 = HI_BYTE(*text);
4184 width = XTextWidth16(curFont->fs, temp, 1);
4185 }
4186 , {
4187 char temp[1];
4188 temp[0] = (char) LO_BYTE(*text);
4189 width = XTextWidth(curFont->fs, temp, 1);
4190 });
4191 adj = (FontWidth(screen) - width) / 2;
4192 if (adj < 0)
4193 adj = 0;
4194 }
4195 xtermFillCells(&recur, gc, x, y, (Cardinal) cells);
4196 recur.draw_flags |= (NOBACKGROUND | CHARBYCHAR);
4197 x = drawXtermText(&recur,
4198 gc, x + adj, y,
4199 text++, 1) - adj;
4200 }
4201
4202 return x;
4203 }
4204 #if OPT_BOX_CHARS
4205 /*
4206 * Draw some substitutions, if needed. The font may not include the
4207 * line-drawing set, or it may be incomplete (in which case we'll draw an
4208 * empty space via xtermDrawBoxChar), or we may be told to force our
4209 * line-drawing.
4210 *
4211 * The empty space is a special case which can be overridden with the
4212 * showMissingGlyphs resource to produce an outline. Not all fonts in
4213 * "modern" (sic) X provide an empty space; some use a thick outline or
4214 * something like the replacement character. If you would rather not see
4215 * that, you can set assumeAllChars.
4216 */
4217 if (!IsIcon(screen)
4218 && !(recur.draw_flags & NOTRANSLATION)
4219 && (!screen->fnt_boxes
4220 || (FontIsIncomplete(curFont) && !screen->assume_all_chars)
4221 || recur.xw->work.force_wideFont
4222 || screen->force_box_chars)) {
4223 /*
4224 * Fill in missing box-characters. Find regions without missing
4225 * characters, and draw them calling ourselves recursively. Draw
4226 * missing characters via xtermDrawBoxChar().
4227 */
4228 int last, first = 0;
4229 Bool drewBoxes = False;
4230
4231 for (last = 0; last < (int) len; last++) {
4232 unsigned ch = (unsigned) text[last];
4233 Bool isMissing;
4234 int ch_width;
4235 #if OPT_WIDE_CHARS
4236
4237 if (ch == HIDDEN_CHAR) {
4238 if (last > first) {
4239 XTermDraw param2 = recur;
4240 param2.draw_flags |= NOTRANSLATION;
4241 x = drawXtermText(¶m2,
4242 gc,
4243 x, y,
4244 text + first,
4245 (unsigned) (last - first));
4246 }
4247 first = last + 1;
4248 drewBoxes = True;
4249 continue;
4250 }
4251 ch_width = CharWidth(screen, ch);
4252 isMissing =
4253 IsXtermMissingChar(screen, ch,
4254 ((recur.on_wide || ch_width > 1)
4255 && okFont(NormalWFont(screen)))
4256 ? WhichVFontData(screen, fWide)
4257 : curFont);
4258 #else
4259 isMissing = IsXtermMissingChar(screen, ch, curFont);
4260 ch_width = 1;
4261 #endif
4262 /*
4263 * If the character is not missing, but we're in wide-character
4264 * mode and the character happens to be a wide-character that
4265 * corresponds to the line-drawing set, allow the forceBoxChars
4266 * resource (or menu entry) to force it to display using our
4267 * tables.
4268 */
4269 if_OPT_WIDE_CHARS(screen, {
4270 if (!isMissing
4271 && TScreenOf(recur.xw)->force_box_chars) {
4272 if (ch > 255
4273 && ucs2dec(screen, ch) < 32) {
4274 ch = ucs2dec(screen, ch);
4275 isMissing = True;
4276 } else if (ch < 32) {
4277 isMissing = True;
4278 }
4279 }
4280 });
4281
4282 if (isMissing) {
4283 if (last > first) {
4284 XTermDraw param2 = recur;
4285 param2.draw_flags |= NOTRANSLATION;
4286 x = drawXtermText(&recur,
4287 gc,
4288 x, y,
4289 text + first,
4290 (unsigned) (last - first));
4291 }
4292 #if OPT_WIDE_CHARS
4293 if (ch_width <= 0 && ch < 32)
4294 ch_width = 1; /* special case for line-drawing */
4295 else if (ch_width < 0)
4296 ch_width = 1; /* special case for combining char */
4297 if (!ucs_workaround(&recur, ch, gc, x, y)) {
4298 xtermDrawBoxChar(&recur, ch, gc, x, y, ch_width, False);
4299 }
4300 #else
4301 xtermDrawBoxChar(&recur, ch, gc, x, y, ch_width, False);
4302 #endif
4303 x += (ch_width * FontWidth(screen));
4304 first = last + 1;
4305 drewBoxes = True;
4306 } else if (ch_width == 2
4307 && recur.xw->work.force_wideFont
4308 && !(recur.draw_flags & NOTRANSLATION)) {
4309 XTermDraw param2 = recur;
4310 param2.draw_flags |= NOTRANSLATION;
4311 /*
4312 * Not a "box" character, but a special case treated similarly.
4313 */
4314 (void) drawXtermText(¶m2,
4315 gc,
4316 x, y,
4317 text + first,
4318 (unsigned) (1 + last - first));
4319 x += (ch_width * FontWidth(screen));
4320 first = last + 1;
4321 drewBoxes = True;
4322 }
4323 }
4324 if (last <= first) {
4325 return x;
4326 }
4327 text += first;
4328 len = (Cardinal) (last - first);
4329 recur.draw_flags |= NOTRANSLATION;
4330 if (drewBoxes) {
4331 return drawXtermText(&recur,
4332 gc,
4333 x,
4334 y,
4335 text,
4336 len);
4337 }
4338 }
4339 #endif /* OPT_BOX_CHARS */
4340 /*
4341 * Behave as if the font has (maybe Unicode-replacements for) drawing
4342 * characters in the range 1-31 (either we were not asked to ignore them,
4343 * or the caller made sure that there is none).
4344 */
4345 #if OPT_WIDE_ATTRS
4346 #define AttrFlags() recur.attr_flags
4347 #define DrawFlags() recur.draw_flags
4348 #else
4349 #define AttrFlags() (recur.attr_flags & DRAWX_MASK)
4350 #define DrawFlags() (recur.draw_flags & (unsigned)~DRAWX_MASK)
4351 #endif
4352 TRACE(("drawtext%c[%4d,%4d] {%#x,%#x} (%d) %d:%s\n",
4353 screen->cursor_state == OFF ? ' ' : '*',
4354 y, x,
4355 AttrFlags(),
4356 DrawFlags(),
4357 recur.this_chrset, len,
4358 visibleIChars(text, len)));
4359 if (screen->scale_height != 1.0f) {
4360 xtermFillCells(&recur, gc, x, y, (Cardinal) len);
4361 }
4362 y += FontAscent(screen);
4363
4364 #if OPT_WIDE_CHARS
4365
4366 if (screen->wide_chars || screen->unicode_font) {
4367 XChar2b *buffer;
4368 Bool needWide = False;
4369 int src, dst;
4370 Bool useBoldFont;
4371 int ascent_adjust = 0;
4372
4373 BumpTypedBuffer(XChar2b, len);
4374 buffer = BfBuf(XChar2b);
4375
4376 for (src = dst = 0; src < (int) len; src++) {
4377 IChar ch = text[src];
4378
4379 if (ch == HIDDEN_CHAR)
4380 continue;
4381
4382 #if OPT_BOX_CHARS
4383 if ((screen->fnt_boxes == 1) && (ch >= 256)) {
4384 unsigned part = ucs2dec(screen, ch);
4385 if (part < 32)
4386 ch = (IChar) part;
4387 }
4388 #endif
4389
4390 if (!needWide
4391 && !IsIcon(screen)
4392 && ((recur.on_wide || CharWidth(screen, ch) > 1)
4393 && okFont(NormalWFont(screen)))) {
4394 needWide = True;
4395 }
4396 #if OPT_WIDER_ICHAR
4397 /*
4398 * bitmap-fonts are limited to 16-bits.
4399 */
4400 if (ch > NARROW_ICHAR) {
4401 ch = 0;
4402 }
4403 #endif
4404 buffer[dst].byte2 = LO_BYTE(ch);
4405 buffer[dst].byte1 = HI_BYTE(ch);
4406 #if OPT_MINI_LUIT
4407 #define UCS2SBUF(value) buffer[dst].byte2 = LO_BYTE(value);\
4408 buffer[dst].byte1 = HI_BYTE(value)
4409
4410 #define Map2Sbuf(from,to) (text[src] == from) { UCS2SBUF(to); }
4411
4412 if (screen->latin9_mode && !screen->utf8_mode && text[src] < 256) {
4413
4414 /* see http://www.cs.tut.fi/~jkorpela/latin9.html */
4415 /* *INDENT-OFF* */
4416 if Map2Sbuf(0xa4, 0x20ac)
4417 else if Map2Sbuf(0xa6, 0x0160)
4418 else if Map2Sbuf(0xa8, 0x0161)
4419 else if Map2Sbuf(0xb4, 0x017d)
4420 else if Map2Sbuf(0xb8, 0x017e)
4421 else if Map2Sbuf(0xbc, 0x0152)
4422 else if Map2Sbuf(0xbd, 0x0153)
4423 else if Map2Sbuf(0xbe, 0x0178)
4424 /* *INDENT-ON* */
4425
4426 }
4427 if (screen->unicode_font
4428 && (text[src] == ANSI_DEL ||
4429 text[src] < ANSI_SPA)) {
4430 unsigned ni = dec2ucs(screen,
4431 (unsigned) ((text[src] == ANSI_DEL)
4432 ? 0
4433 : text[src]));
4434 UCS2SBUF(ni);
4435 }
4436 #endif /* OPT_MINI_LUIT */
4437 ++dst;
4438 }
4439
4440 /*
4441 * Check for special case where the bold font lacks glyphs found in the
4442 * normal font, and drop down to normal fonts with overstriking to help
4443 * show the actual characters.
4444 */
4445 useBoldFont = ((recur.attr_flags & BOLDATTR(screen)) != 0);
4446 if (useBoldFont) {
4447 XTermFonts *norm = 0;
4448 XTermFonts *bold = 0;
4449 Bool noBold, noNorm;
4450
4451 if (needWide && okFont(BoldWFont(screen))) {
4452 norm = WhichVFontData(screen, fWide);
4453 bold = WhichVFontData(screen, fWBold);
4454 } else if (okFont(BoldFont(screen))) {
4455 norm = WhichVFontData(screen, fNorm);
4456 bold = WhichVFontData(screen, fBold);
4457 } else {
4458 useBoldFont = False;
4459 }
4460
4461 if (useBoldFont && FontIsIncomplete(bold)) {
4462 for (src = 0; src < (int) len; src++) {
4463 IChar ch = text[src];
4464
4465 if (ch == HIDDEN_CHAR)
4466 continue;
4467
4468 noBold = IsXtermMissingChar(screen, ch, bold);
4469 if (noBold) {
4470 noNorm = IsXtermMissingChar(screen, ch, norm);
4471 if (!noNorm) {
4472 useBoldFont = False;
4473 break;
4474 }
4475 }
4476 }
4477 }
4478 }
4479
4480 /* FIXME This is probably wrong. But it works. */
4481 underline_len = len;
4482
4483 /* Set the drawing font */
4484 if (!(recur.draw_flags & (DOUBLEHFONT | DOUBLEWFONT))) {
4485 VTwin *currentWin = WhichVWin(screen);
4486 VTFontEnum fntId;
4487 CgsEnum cgsId;
4488 Pixel fg = getCgsFore(recur.xw, currentWin, gc);
4489 Pixel bg = getCgsBack(recur.xw, currentWin, gc);
4490
4491 if (needWide
4492 && useBoldFont
4493 && okFont(BoldWFont(screen))) {
4494 fntId = fWBold;
4495 cgsId = gcWBold;
4496 } else if (needWide) {
4497 fntId = fWide;
4498 cgsId = gcWide;
4499 } else if (useBoldFont) {
4500 fntId = fBold;
4501 cgsId = gcBold;
4502 } else {
4503 fntId = fNorm;
4504 cgsId = gcNorm;
4505 }
4506
4507 setCgsFore(recur.xw, currentWin, cgsId, fg);
4508 setCgsBack(recur.xw, currentWin, cgsId, bg);
4509 gc = getCgsGC(recur.xw, currentWin, cgsId);
4510
4511 #if OPT_WIDE_ATTRS
4512 #if OPT_DEC_CHRSET
4513 if (!(CSET_DOUBLE(recur.this_chrset) || (recur.draw_flags & DOUBLEWFONT)))
4514 #endif
4515 need_clipping = fixupItalics(&recur,
4516 gc,
4517 getCgsFont(recur.xw,
4518 currentWin, gc),
4519 y, x, font_width, len);
4520 #endif
4521 if (fntId != fNorm) {
4522 XFontStruct *thisFp = WhichVFont(screen, fntId);
4523 ascent_adjust = (thisFp->ascent
4524 - NormalFont(screen)->ascent);
4525 if (thisFp->max_bounds.width ==
4526 NormalFont(screen)->max_bounds.width * 2) {
4527 underline_len = real_length = (Cardinal) (dst * 2);
4528 } else if (cgsId == gcWide || cgsId == gcWBold) {
4529 underline_len = real_length = (Cardinal) (dst * 2);
4530 xtermFillCells(&recur,
4531 gc,
4532 x,
4533 y - thisFp->ascent,
4534 real_length);
4535 }
4536 }
4537 }
4538
4539 if (recur.draw_flags & NOBACKGROUND) {
4540 XDrawString16(screen->display,
4541 VDrawable(screen), gc,
4542 x, y + y_shift + ascent_adjust,
4543 buffer, dst);
4544 } else if (dst <= MaxImageString) {
4545 XDrawImageString16(screen->display,
4546 VDrawable(screen), gc,
4547 x, y + y_shift + ascent_adjust,
4548 buffer, dst);
4549 } else {
4550 int b_pos;
4551 int b_max = MaxImageString;
4552 for (b_pos = 0; b_pos < dst; b_pos += b_max) {
4553 if (b_pos + b_max > dst)
4554 b_max = (dst - b_pos);
4555 XDrawImageString16(screen->display,
4556 VDrawable(screen), gc,
4557 x + (b_pos * FontWidth(screen)),
4558 y + y_shift + ascent_adjust,
4559 buffer + b_pos,
4560 b_max);
4561 }
4562 }
4563 #if OPT_WIDE_ATTRS
4564 if (need_clipping) {
4565 endClipping(screen, gc);
4566 }
4567 #endif
4568
4569 if ((recur.attr_flags & BOLDATTR(screen)) && (screen->enbolden || !useBoldFont)) {
4570 if (!(recur.draw_flags & (DOUBLEWFONT | DOUBLEHFONT))) {
4571 beginClipping(screen, gc, (Cardinal) font_width, len);
4572 }
4573 XDrawString16(screen->display, VDrawable(screen), gc,
4574 x + 1,
4575 y + y_shift + ascent_adjust,
4576 buffer, dst);
4577 if (!(recur.draw_flags & (DOUBLEWFONT | DOUBLEHFONT))) {
4578 endClipping(screen, gc);
4579 }
4580 }
4581
4582 } else
4583 #endif /* OPT_WIDE_CHARS */
4584 {
4585 int length = (int) len; /* X should have used unsigned */
4586 #if OPT_WIDE_CHARS
4587 char *buffer;
4588 int dst;
4589
4590 BumpTypedBuffer(char, len);
4591 buffer = BfBuf(char);
4592
4593 for (dst = 0; dst < length; ++dst)
4594 buffer[dst] = (char) LO_BYTE(text[dst]);
4595 #else
4596 char *buffer = (char *) text;
4597 #endif
4598
4599 #if OPT_WIDE_ATTRS
4600 #if OPT_DEC_CHRSET
4601 if (!(CSET_DOUBLE(recur.this_chrset) || (recur.draw_flags & DOUBLEWFONT)))
4602 #endif
4603 need_clipping = fixupItalics(&recur, gc, curFont,
4604 y, x, font_width, len);
4605 #endif
4606
4607 if (recur.draw_flags & NOBACKGROUND) {
4608 XDrawString(screen->display, VDrawable(screen), gc,
4609 x, y + y_shift, buffer, length);
4610 } else if (length <= MaxImageString) {
4611 XDrawImageString(screen->display, VDrawable(screen), gc,
4612 x, y + y_shift, buffer, length);
4613 } else {
4614 int b_pos;
4615 int b_max = MaxImageString;
4616 for (b_pos = 0; b_pos < length; b_pos += b_max) {
4617 if (b_pos + b_max > length)
4618 b_max = (length - b_pos);
4619 XDrawImageString(screen->display,
4620 VDrawable(screen), gc,
4621 x + (b_pos * FontWidth(screen)),
4622 y + y_shift,
4623 buffer + b_pos,
4624 b_max);
4625 }
4626 }
4627
4628 #if OPT_WIDE_ATTRS
4629 if (need_clipping) {
4630 endClipping(screen, gc);
4631 }
4632 #endif
4633 underline_len = (Cardinal) length;
4634 if ((recur.attr_flags & BOLDATTR(screen)) && screen->enbolden) {
4635 if (!(recur.draw_flags & (DOUBLEWFONT | DOUBLEHFONT))) {
4636 beginClipping(screen, gc, font_width, length);
4637 }
4638 XDrawString(screen->display, VDrawable(screen), gc,
4639 x + 1, y + y_shift, buffer, length);
4640 if (!(recur.draw_flags & (DOUBLEWFONT | DOUBLEHFONT))) {
4641 endClipping(screen, gc);
4642 }
4643 }
4644 }
4645
4646 drawUnderline(recur.xw,
4647 gc,
4648 recur.attr_flags,
4649 underline_len,
4650 font_width,
4651 x,
4652 y + y_shift,
4653 did_ul);
4654
4655 x += ((int) real_length) * FontWidth(screen);
4656 return x;
4657 }
4658
4659 #if OPT_WIDE_CHARS
4660 /*
4661 * Allocate buffer - workaround for wide-character interfaces.
4662 */
4663 void
allocXtermChars(ScrnPtr * buffer,Cardinal length)4664 allocXtermChars(ScrnPtr *buffer, Cardinal length)
4665 {
4666 if (*buffer == 0) {
4667 *buffer = (ScrnPtr) XtMalloc(length);
4668 } else {
4669 *buffer = (ScrnPtr) XtRealloc((char *) *buffer, length);
4670 }
4671 }
4672 #endif
4673
4674 /* set up size hints for window manager; min 1 char by 1 char */
4675 void
xtermSizeHints(XtermWidget xw,int scrollbarWidth)4676 xtermSizeHints(XtermWidget xw, int scrollbarWidth)
4677 {
4678 TScreen *screen = TScreenOf(xw);
4679
4680 TRACE(("xtermSizeHints\n"));
4681 TRACE((" border %d\n", xw->core.border_width));
4682 TRACE((" scrollbar %d\n", scrollbarWidth));
4683
4684 xw->hints.base_width = 2 * screen->border + scrollbarWidth;
4685 xw->hints.base_height = 2 * screen->border;
4686
4687 #if OPT_TOOLBAR
4688 TRACE((" toolbar %d\n", ToolbarHeight(xw)));
4689
4690 xw->hints.base_height += ToolbarHeight(xw);
4691 xw->hints.base_height += BorderWidth(xw) * 2;
4692 xw->hints.base_width += BorderWidth(xw) * 2;
4693 #endif
4694
4695 if (xw->misc.resizeByPixel) {
4696 xw->hints.width_inc = 1;
4697 xw->hints.height_inc = 1;
4698 } else {
4699 xw->hints.width_inc = FontWidth(screen);
4700 xw->hints.height_inc = FontHeight(screen);
4701 }
4702 xw->hints.min_width = xw->hints.base_width + xw->hints.width_inc;
4703 xw->hints.min_height = xw->hints.base_height + xw->hints.height_inc;
4704
4705 xw->hints.width = MaxCols(screen) * FontWidth(screen) + xw->hints.min_width;
4706 xw->hints.height = MaxRows(screen) * FontHeight(screen) + xw->hints.min_height;
4707
4708 xw->hints.flags |= (PSize | PBaseSize | PMinSize | PResizeInc);
4709
4710 TRACE_HINTS(&(xw->hints));
4711 }
4712
4713 void
getXtermSizeHints(XtermWidget xw)4714 getXtermSizeHints(XtermWidget xw)
4715 {
4716 TScreen *screen = TScreenOf(xw);
4717 long supp;
4718
4719 if (!XGetWMNormalHints(screen->display, VShellWindow(xw),
4720 &xw->hints, &supp))
4721 memset(&xw->hints, 0, sizeof(xw->hints));
4722 TRACE_HINTS(&(xw->hints));
4723 }
4724
4725 CgsEnum
whichXtermCgs(XtermWidget xw,unsigned attr_flags,Bool hilite)4726 whichXtermCgs(XtermWidget xw, unsigned attr_flags, Bool hilite)
4727 {
4728 TScreen *screen = TScreenOf(xw);
4729 CgsEnum cgsId = gcMAX;
4730
4731 if (ReverseOrHilite(screen, attr_flags, hilite)) {
4732 if (attr_flags & BOLDATTR(screen)) {
4733 cgsId = gcBoldReverse;
4734 } else {
4735 cgsId = gcNormReverse;
4736 }
4737 } else {
4738 if (attr_flags & BOLDATTR(screen)) {
4739 cgsId = gcBold;
4740 } else {
4741 cgsId = gcNorm;
4742 }
4743 }
4744 return cgsId;
4745 }
4746
4747 /*
4748 * Returns a GC, selected according to the font (reverse/bold/normal) that is
4749 * required for the current position (implied). The GC is updated with the
4750 * current screen foreground and background colors.
4751 */
4752 GC
updatedXtermGC(XtermWidget xw,unsigned attr_flags,CellColor fg_bg,Bool hilite)4753 updatedXtermGC(XtermWidget xw, unsigned attr_flags, CellColor fg_bg,
4754 Bool hilite)
4755 {
4756 TScreen *screen = TScreenOf(xw);
4757 VTwin *win = WhichVWin(screen);
4758 CgsEnum cgsId = whichXtermCgs(xw, attr_flags, hilite);
4759 Pixel my_fg = extract_fg(xw, fg_bg, attr_flags);
4760 Pixel my_bg = extract_bg(xw, fg_bg, attr_flags);
4761 Pixel fg_pix = getXtermFG(xw, attr_flags, (int) my_fg);
4762 Pixel bg_pix = getXtermBG(xw, attr_flags, (int) my_bg);
4763 Pixel xx_pix;
4764 #if OPT_HIGHLIGHT_COLOR
4765 Boolean reverse2 = ((attr_flags & INVERSE) && hilite);
4766 Pixel selbg_pix = T_COLOR(screen, HIGHLIGHT_BG);
4767 Pixel selfg_pix = T_COLOR(screen, HIGHLIGHT_FG);
4768 Boolean always = screen->hilite_color;
4769 Boolean use_selbg = (Boolean) (always &&
4770 isNotForeground(xw, fg_pix, bg_pix, selbg_pix));
4771 Boolean use_selfg = (Boolean) (always &&
4772 isNotBackground(xw, fg_pix, bg_pix, selfg_pix));
4773 #endif
4774
4775 (void) fg_bg;
4776 (void) my_bg;
4777 (void) my_fg;
4778
4779 /*
4780 * Discard video attributes overridden by colorXXXMode's.
4781 */
4782 checkVeryBoldColors(attr_flags, my_fg);
4783
4784 if (ReverseOrHilite(screen, attr_flags, hilite)) {
4785 #if OPT_HIGHLIGHT_COLOR
4786 if (!screen->hilite_color) {
4787 if (selbg_pix != T_COLOR(screen, TEXT_FG)
4788 && selbg_pix != fg_pix
4789 && selbg_pix != bg_pix
4790 && selbg_pix != xw->dft_foreground) {
4791 bg_pix = fg_pix;
4792 fg_pix = selbg_pix;
4793 }
4794 }
4795 #endif
4796 EXCHANGE(fg_pix, bg_pix, xx_pix);
4797 #if OPT_HIGHLIGHT_COLOR
4798 if (screen->hilite_color) {
4799 if (screen->hilite_reverse) {
4800 if (use_selbg) {
4801 if (use_selfg) {
4802 bg_pix = fg_pix;
4803 } else {
4804 fg_pix = bg_pix;
4805 bg_pix = selbg_pix;
4806 }
4807 }
4808 if (use_selfg)
4809 fg_pix = selfg_pix;
4810 }
4811 }
4812 #endif
4813 } else if ((attr_flags & INVERSE) && hilite) {
4814 #if OPT_HIGHLIGHT_COLOR
4815 if (!screen->hilite_color) {
4816 if (selbg_pix != T_COLOR(screen, TEXT_FG)
4817 && selbg_pix != fg_pix
4818 && selbg_pix != bg_pix
4819 && selbg_pix != xw->dft_foreground) {
4820 bg_pix = fg_pix;
4821 fg_pix = selbg_pix;
4822 }
4823 }
4824 #endif
4825 /* double-reverse... EXCHANGE(fg_pix, bg_pix, xx_pix); */
4826 #if OPT_HIGHLIGHT_COLOR
4827 if (screen->hilite_color) {
4828 if (screen->hilite_reverse) {
4829 if (use_selbg) {
4830 if (use_selfg ^ reverse2) {
4831 bg_pix = fg_pix;
4832 } else {
4833 fg_pix = bg_pix;
4834 }
4835 if (reverse2) {
4836 fg_pix = selbg_pix;
4837 } else {
4838 bg_pix = selbg_pix;
4839 }
4840 }
4841 if (use_selfg) {
4842 if (reverse2) {
4843 bg_pix = selfg_pix;
4844 } else {
4845 fg_pix = selfg_pix;
4846 }
4847 }
4848 }
4849 }
4850 #endif
4851 }
4852 #if OPT_HIGHLIGHT_COLOR
4853 if (!screen->hilite_color || !screen->hilite_reverse) {
4854 if (hilite && !screen->hilite_reverse) {
4855 if (use_selbg) {
4856 if (reverse2)
4857 fg_pix = selbg_pix;
4858 else
4859 bg_pix = selbg_pix;
4860 }
4861 if (use_selfg) {
4862 if (reverse2)
4863 bg_pix = selfg_pix;
4864 else
4865 fg_pix = selfg_pix;
4866 }
4867 }
4868 }
4869 #endif
4870
4871 #if OPT_BLINK_TEXT
4872 if ((screen->blink_state == ON) &&
4873 (!screen->blink_as_bold) &&
4874 (attr_flags & BLINK)) {
4875 fg_pix = bg_pix;
4876 }
4877 #endif
4878
4879 setCgsFore(xw, win, cgsId, fg_pix);
4880 setCgsBack(xw, win, cgsId, bg_pix);
4881 return getCgsGC(xw, win, cgsId);
4882 }
4883
4884 /*
4885 * Resets the foreground/background of the GC returned by 'updatedXtermGC()'
4886 * to the values that would be set in SGR_Foreground and SGR_Background. This
4887 * duplicates some logic, but only modifies 1/4 as many GC's.
4888 */
4889 void
resetXtermGC(XtermWidget xw,unsigned attr_flags,Bool hilite)4890 resetXtermGC(XtermWidget xw, unsigned attr_flags, Bool hilite)
4891 {
4892 TScreen *screen = TScreenOf(xw);
4893 VTwin *win = WhichVWin(screen);
4894 CgsEnum cgsId = whichXtermCgs(xw, attr_flags, hilite);
4895 Pixel fg_pix = getXtermFG(xw, attr_flags, xw->cur_foreground);
4896 Pixel bg_pix = getXtermBG(xw, attr_flags, xw->cur_background);
4897
4898 checkVeryBoldColors(attr_flags, xw->cur_foreground);
4899
4900 if (ReverseOrHilite(screen, attr_flags, hilite)) {
4901 setCgsFore(xw, win, cgsId, bg_pix);
4902 setCgsBack(xw, win, cgsId, fg_pix);
4903 } else {
4904 setCgsFore(xw, win, cgsId, fg_pix);
4905 setCgsBack(xw, win, cgsId, bg_pix);
4906 }
4907 }
4908
4909 #if OPT_ISO_COLORS
4910 /*
4911 * Extract the foreground-color index from a color pair.
4912 * If we've got BOLD or UNDERLINE color-mode active, those will be used.
4913 */
4914 Pixel
extract_fg(XtermWidget xw,CellColor color,unsigned attr_flags)4915 extract_fg(XtermWidget xw, CellColor color, unsigned attr_flags)
4916 {
4917 unsigned fg = ExtractForeground(color);
4918
4919 if (TScreenOf(xw)->colorAttrMode
4920 || (fg == ExtractBackground(color))) {
4921 fg = MapToColorMode(fg, TScreenOf(xw), attr_flags);
4922 }
4923 return fg;
4924 }
4925
4926 /*
4927 * Extract the background-color index from a color pair.
4928 * If we've got INVERSE color-mode active, that will be used.
4929 */
4930 Pixel
extract_bg(XtermWidget xw,CellColor color,unsigned attr_flags)4931 extract_bg(XtermWidget xw, CellColor color, unsigned attr_flags)
4932 {
4933 unsigned bg = ExtractBackground(color);
4934
4935 if (TScreenOf(xw)->colorAttrMode
4936 || (bg == ExtractForeground(color))) {
4937 if (TScreenOf(xw)->colorRVMode && (attr_flags & INVERSE))
4938 bg = COLOR_RV;
4939 }
4940 return bg;
4941 }
4942
4943 /*
4944 * Combine the current foreground and background into a single 8-bit number.
4945 * Note that we're storing the SGR foreground, since cur_foreground may be set
4946 * to COLOR_UL, COLOR_BD or COLOR_BL, which would make the code larger than 8
4947 * bits.
4948 *
4949 * This assumes that fg/bg are equal when we override with one of the special
4950 * attribute colors.
4951 */
4952 CellColor
makeColorPair(XtermWidget xw)4953 makeColorPair(XtermWidget xw)
4954 {
4955 CellColor result;
4956
4957 #if OPT_DIRECT_COLOR
4958 result.fg = xw->cur_foreground;
4959 result.bg = xw->cur_background;
4960 #else
4961 int fg = xw->cur_foreground;
4962 int bg = xw->cur_background;
4963 unsigned my_bg = okIndexedColor(bg) ? (unsigned) bg : 0;
4964 unsigned my_fg = okIndexedColor(fg) ? (unsigned) fg : my_bg;
4965
4966 result = (CellColor) (my_fg | (my_bg << COLOR_BITS));
4967 #endif
4968
4969 return result;
4970 }
4971
4972 /*
4973 * Using the "current" SGR background, clear a rectangle.
4974 */
4975 void
ClearCurBackground(XtermWidget xw,int top,int left,unsigned height,unsigned width,unsigned fw)4976 ClearCurBackground(XtermWidget xw,
4977 int top,
4978 int left,
4979 unsigned height,
4980 unsigned width,
4981 unsigned fw)
4982 {
4983 TScreen *screen = TScreenOf(xw);
4984
4985 TRACE(("ClearCurBackground %d,%d %dx%d with %d\n",
4986 top, left, height, width, xw->cur_background));
4987
4988 assert((int) width > 0);
4989 assert((left + (int) width) <= screen->max_col + 1);
4990 assert((int) height <= screen->max_row + 1);
4991
4992 if (VWindow(screen)) {
4993 set_background(xw, xw->cur_background);
4994
4995 xtermClear2(xw,
4996 CursorX2(screen, left, fw),
4997 CursorY2(screen, top),
4998 (width * fw),
4999 (height * (unsigned) FontHeight(screen)));
5000
5001 set_background(xw, -1);
5002 }
5003 }
5004 #endif /* OPT_ISO_COLORS */
5005
5006 Pixel
getXtermBackground(XtermWidget xw,unsigned attr_flags,int color)5007 getXtermBackground(XtermWidget xw, unsigned attr_flags, int color)
5008 {
5009 Pixel result = T_COLOR(TScreenOf(xw), TEXT_BG);
5010
5011 #if OPT_ISO_COLORS
5012 if (color >= 0) {
5013 if_OPT_DIRECT_COLOR2_else(TScreenOf(xw), (attr_flags & ATR_DIRECT_BG), {
5014 result = (Pixel) color;
5015 }) if ((attr_flags & BG_COLOR) && (color < MAXCOLORS)) {
5016 result = GET_COLOR_RES(xw, TScreenOf(xw)->Acolors[color]);
5017 }
5018 }
5019 #else
5020 (void) attr_flags;
5021 (void) color;
5022 #endif
5023 return result;
5024 }
5025
5026 Pixel
getXtermForeground(XtermWidget xw,unsigned attr_flags,int color)5027 getXtermForeground(XtermWidget xw, unsigned attr_flags, int color)
5028 {
5029 Pixel result = T_COLOR(TScreenOf(xw), TEXT_FG);
5030
5031 #if OPT_ISO_COLORS
5032 if_OPT_DIRECT_COLOR2_else(TScreenOf(xw), (attr_flags & ATR_DIRECT_FG), {
5033 result = (Pixel) color;
5034 })
5035 if ((attr_flags & FG_COLOR) &&
5036 (color >= 0 && color < MAXCOLORS)) {
5037 result = GET_COLOR_RES(xw, TScreenOf(xw)->Acolors[color]);
5038 }
5039 #else
5040 (void) attr_flags;
5041 (void) color;
5042 #endif
5043
5044 #if OPT_WIDE_ATTRS
5045 #define DIM_IT(n) work.n = (unsigned short) ((2 * (unsigned)work.n) / 3)
5046 if ((attr_flags & ATR_FAINT)) {
5047 static Pixel last_in;
5048 static Pixel last_out;
5049 if ((result != last_in)
5050 && ((color >= 0)
5051 || (result != (Pixel) color))) {
5052 XColor work;
5053 last_in = result;
5054 work.pixel = result;
5055 if (QueryOneColor(xw, &work)) {
5056 DIM_IT(red);
5057 DIM_IT(green);
5058 DIM_IT(blue);
5059 if (allocateBestRGB(xw, &work)) {
5060 result = work.pixel;
5061 }
5062 }
5063 last_out = result;
5064 } else {
5065 result = last_out;
5066 }
5067 }
5068 #endif
5069 return result;
5070 }
5071
5072 /*
5073 * Returns a single base character for the given cell.
5074 */
5075 unsigned
getXtermCell(TScreen * screen,int row,int col)5076 getXtermCell(TScreen *screen, int row, int col)
5077 {
5078 CLineData *ld = getLineData(screen, row);
5079
5080 return ((ld && (col < (int) ld->lineSize))
5081 ? ld->charData[col]
5082 : (unsigned) ' ');
5083 }
5084
5085 /*
5086 * Sets a single base character for the given cell.
5087 */
5088 void
putXtermCell(TScreen * screen,int row,int col,int ch)5089 putXtermCell(TScreen *screen, int row, int col, int ch)
5090 {
5091 LineData *ld = getLineData(screen, row);
5092
5093 if (ld && (col < (int) ld->lineSize)) {
5094 ld->charData[col] = (CharData) ch;
5095 if_OPT_WIDE_CHARS(screen, {
5096 size_t off;
5097 for_each_combData(off, ld) {
5098 ld->combData[off][col] = 0;
5099 }
5100 });
5101 }
5102 }
5103
5104 #if OPT_WIDE_CHARS
5105 /*
5106 * Add a combining character for the given cell
5107 */
5108 void
addXtermCombining(TScreen * screen,int row,int col,unsigned ch)5109 addXtermCombining(TScreen *screen, int row, int col, unsigned ch)
5110 {
5111 if (ch != 0) {
5112 LineData *ld = getLineData(screen, row);
5113 size_t off;
5114
5115 TRACE(("addXtermCombining %d,%d U+%04X (%d)\n",
5116 row, col, ch, CharWidth(screen, ch)));
5117
5118 for_each_combData(off, ld) {
5119 if (!ld->combData[off][col]) {
5120 ld->combData[off][col] = (CharData) ch;
5121 break;
5122 }
5123 }
5124 }
5125 }
5126
5127 unsigned
getXtermCombining(TScreen * screen,int row,int col,int off)5128 getXtermCombining(TScreen *screen, int row, int col, int off)
5129 {
5130 CLineData *ld = getLineData(screen, row);
5131 return (ld->combSize ? ld->combData[off][col] : 0U);
5132 }
5133 #endif
5134
5135 void
update_keyboard_type(void)5136 update_keyboard_type(void)
5137 {
5138 update_delete_del();
5139 update_tcap_fkeys();
5140 update_old_fkeys();
5141 update_hp_fkeys();
5142 update_sco_fkeys();
5143 update_sun_fkeys();
5144 update_sun_kbd();
5145 }
5146
5147 void
set_keyboard_type(XtermWidget xw,xtermKeyboardType type,Bool set)5148 set_keyboard_type(XtermWidget xw, xtermKeyboardType type, Bool set)
5149 {
5150 xtermKeyboardType save = xw->keyboard.type;
5151
5152 TRACE(("set_keyboard_type(%s, %s) currently %s\n",
5153 visibleKeyboardType(type),
5154 BtoS(set),
5155 visibleKeyboardType(xw->keyboard.type)));
5156 if (set) {
5157 xw->keyboard.type = type;
5158 } else {
5159 xw->keyboard.type = keyboardIsDefault;
5160 }
5161
5162 if (save != xw->keyboard.type) {
5163 update_keyboard_type();
5164 }
5165 }
5166
5167 void
toggle_keyboard_type(XtermWidget xw,xtermKeyboardType type)5168 toggle_keyboard_type(XtermWidget xw, xtermKeyboardType type)
5169 {
5170 xtermKeyboardType save = xw->keyboard.type;
5171
5172 TRACE(("toggle_keyboard_type(%s) currently %s\n",
5173 visibleKeyboardType(type),
5174 visibleKeyboardType(xw->keyboard.type)));
5175 if (xw->keyboard.type == type) {
5176 xw->keyboard.type = keyboardIsDefault;
5177 } else {
5178 xw->keyboard.type = type;
5179 }
5180
5181 if (save != xw->keyboard.type) {
5182 update_keyboard_type();
5183 }
5184 }
5185
5186 const char *
visibleKeyboardType(xtermKeyboardType type)5187 visibleKeyboardType(xtermKeyboardType type)
5188 {
5189 const char *result = "?";
5190 switch (type) {
5191 CASETYPE(keyboardIsLegacy); /* bogus vt220 codes for F1-F4, etc. */
5192 CASETYPE(keyboardIsDefault);
5193 CASETYPE(keyboardIsHP);
5194 CASETYPE(keyboardIsSCO);
5195 CASETYPE(keyboardIsSun);
5196 CASETYPE(keyboardIsTermcap);
5197 CASETYPE(keyboardIsVT220);
5198 }
5199 return result;
5200 }
5201
5202 static void
init_keyboard_type(XtermWidget xw,xtermKeyboardType type,Bool set)5203 init_keyboard_type(XtermWidget xw, xtermKeyboardType type, Bool set)
5204 {
5205 TRACE(("init_keyboard_type(%s, %s) currently %s\n",
5206 visibleKeyboardType(type),
5207 BtoS(set),
5208 visibleKeyboardType(xw->keyboard.type)));
5209 if (set) {
5210 /*
5211 * Check for conflicts, e.g., if someone asked for both Sun and HP
5212 * function keys.
5213 */
5214 if (guard_keyboard_type) {
5215 xtermWarning("Conflicting keyboard type option (%s/%s)\n",
5216 visibleKeyboardType(xw->keyboard.type),
5217 visibleKeyboardType(type));
5218 }
5219 xw->keyboard.type = type;
5220 guard_keyboard_type = True;
5221 update_keyboard_type();
5222 }
5223 }
5224
5225 /*
5226 * If the keyboardType resource is set, use that, overriding the individual
5227 * boolean resources for different keyboard types.
5228 */
5229 void
decode_keyboard_type(XtermWidget xw,XTERM_RESOURCE * rp)5230 decode_keyboard_type(XtermWidget xw, XTERM_RESOURCE * rp)
5231 {
5232 #define DATA(n, t, f) { n, t, XtOffsetOf(XTERM_RESOURCE, f) }
5233 #define FLAG(n) *(Boolean *)(((char *)rp) + table[n].offset)
5234 static struct {
5235 const char *name;
5236 xtermKeyboardType type;
5237 unsigned offset;
5238 } table[] = {
5239 DATA(NAME_OLD_KT, keyboardIsLegacy, oldKeyboard),
5240 #if OPT_HP_FUNC_KEYS
5241 DATA(NAME_HP_KT, keyboardIsHP, hpFunctionKeys),
5242 #endif
5243 #if OPT_SCO_FUNC_KEYS
5244 DATA(NAME_SCO_KT, keyboardIsSCO, scoFunctionKeys),
5245 #endif
5246 #if OPT_SUN_FUNC_KEYS
5247 DATA(NAME_SUN_KT, keyboardIsSun, sunFunctionKeys),
5248 #endif
5249 #if OPT_SUNPC_KBD
5250 DATA(NAME_VT220_KT, keyboardIsVT220, sunKeyboard),
5251 #endif
5252 #if OPT_TCAP_FKEYS
5253 DATA(NAME_TCAP_KT, keyboardIsTermcap, termcapKeys),
5254 #endif
5255 };
5256 Cardinal n;
5257 TScreen *screen = TScreenOf(xw);
5258
5259 TRACE(("decode_keyboard_type(%s)\n", rp->keyboardType));
5260 if (!x_strcasecmp(rp->keyboardType, "unknown")) {
5261 /*
5262 * Let the individual resources comprise the keyboard-type.
5263 */
5264 for (n = 0; n < XtNumber(table); ++n)
5265 init_keyboard_type(xw, table[n].type, FLAG(n));
5266 } else if (!x_strcasecmp(rp->keyboardType, "default")) {
5267 /*
5268 * Set the keyboard-type to the Sun/PC type, allowing modified
5269 * function keys, etc.
5270 */
5271 for (n = 0; n < XtNumber(table); ++n)
5272 init_keyboard_type(xw, table[n].type, False);
5273 } else {
5274 Bool found = False;
5275
5276 /*
5277 * Special case: oldXtermFKeys should have been like the others.
5278 */
5279 if (!x_strcasecmp(rp->keyboardType, NAME_OLD_KT)) {
5280 TRACE(("special case, setting oldXtermFKeys\n"));
5281 screen->old_fkeys = True;
5282 screen->old_fkeys0 = True;
5283 }
5284
5285 /*
5286 * Choose an individual keyboard type.
5287 */
5288 for (n = 0; n < XtNumber(table); ++n) {
5289 if (!x_strcasecmp(rp->keyboardType, table[n].name + 1)) {
5290 FLAG(n) = True;
5291 found = True;
5292 } else {
5293 FLAG(n) = False;
5294 }
5295 init_keyboard_type(xw, table[n].type, FLAG(n));
5296 }
5297 if (!found) {
5298 xtermWarning("KeyboardType resource \"%s\" not found\n",
5299 rp->keyboardType);
5300 }
5301 }
5302 #undef DATA
5303 #undef FLAG
5304 }
5305
5306 #if OPT_WIDE_CHARS
5307 #if defined(HAVE_WCHAR_H) && defined(HAVE_WCWIDTH)
5308 /*
5309 * If xterm is running in a UTF-8 locale, it is still possible to encounter
5310 * old runtime configurations which yield incomplete or inaccurate data.
5311 */
5312 static Bool
systemWcwidthOk(int samplesize,int samplepass)5313 systemWcwidthOk(int samplesize, int samplepass)
5314 {
5315 wchar_t n;
5316 int oops = 0;
5317
5318 for (n = 21; n <= 25; ++n) {
5319 wchar_t code = (wchar_t) dec2ucs(NULL, (unsigned) n);
5320 int system_code = wcwidth(code);
5321 int intern_code = mk_wcwidth(code);
5322
5323 /*
5324 * Solaris 10 wcwidth() returns "2" for all of the line-drawing (page
5325 * 0x2500) and most of the geometric shapes (a few are excluded, just
5326 * to make it more difficult to use). Do a sanity check to avoid using
5327 * it.
5328 */
5329 if ((system_code < 0 && intern_code >= 1)
5330 || (system_code >= 0 && intern_code != system_code)) {
5331 TRACE(("systemWcwidthOk: broken system line-drawing wcwidth\n"));
5332 oops += (samplepass + 1);
5333 break;
5334 }
5335 }
5336
5337 for (n = 0; n < (wchar_t) samplesize; ++n) {
5338 int system_code = wcwidth(n);
5339 int intern_code = mk_wcwidth(n);
5340
5341 /*
5342 * When this check was originally implemented, there were few if any
5343 * libraries with full Unicode coverage. Time passes, and it is
5344 * possible to make a full comparison of the BMP. There are some
5345 * differences: mk_wcwidth() marks some codes as combining and some
5346 * as single-width, differing from GNU libc.
5347 */
5348 if ((system_code < 0 && intern_code >= 1)
5349 || (system_code >= 0 && intern_code != system_code)) {
5350 TRACE((".. width(U+%04X) = %d, expected %d\n",
5351 (unsigned) n, system_code, intern_code));
5352 if (++oops > samplepass)
5353 break;
5354 }
5355 }
5356 TRACE(("systemWcwidthOk: %d/%d mismatches, allowed %d\n",
5357 oops, (int) n, samplepass));
5358 return (oops <= samplepass);
5359 }
5360 #endif /* HAVE_WCWIDTH */
5361
5362 void
decode_wcwidth(XtermWidget xw)5363 decode_wcwidth(XtermWidget xw)
5364 {
5365 int mode = ((xw->misc.cjk_width ? 2 : 0)
5366 + (xw->misc.mk_width ? 1 : 0)
5367 + 1);
5368
5369 switch (mode) {
5370 default:
5371 #if defined(HAVE_WCHAR_H) && defined(HAVE_WCWIDTH)
5372 if (xtermEnvUTF8() &&
5373 systemWcwidthOk(xw->misc.mk_samplesize, xw->misc.mk_samplepass)) {
5374 my_wcwidth = wcwidth;
5375 TRACE(("using system wcwidth() function\n"));
5376 break;
5377 }
5378 #endif
5379 /* FALLTHRU */
5380 case 2:
5381 my_wcwidth = &mk_wcwidth;
5382 TRACE(("using MK wcwidth() function\n"));
5383 break;
5384 case 3:
5385 /* FALLTHRU */
5386 case 4:
5387 my_wcwidth = &mk_wcwidth_cjk;
5388 TRACE(("using MK-CJK wcwidth() function\n"));
5389 break;
5390 }
5391
5392 for (first_widechar = 128; first_widechar < 4500; ++first_widechar) {
5393 if (my_wcwidth((wchar_t) first_widechar) > 1) {
5394 TRACE(("first_widechar %#x\n", first_widechar));
5395 break;
5396 }
5397 }
5398 }
5399 #endif
5400
5401 /*
5402 * Extend a (normally) boolean resource value by checking for additional values
5403 * which will be mapped into true/false.
5404 */
5405 int
extendedBoolean(const char * value,const FlagList * table,Cardinal limit)5406 extendedBoolean(const char *value, const FlagList * table, Cardinal limit)
5407 {
5408 int result = -1;
5409 long check;
5410 char *next;
5411 Cardinal n;
5412
5413 if ((x_strcasecmp(value, "true") == 0)
5414 || (x_strcasecmp(value, "yes") == 0)
5415 || (x_strcasecmp(value, "on") == 0)) {
5416 result = True;
5417 } else if ((x_strcasecmp(value, "false") == 0)
5418 || (x_strcasecmp(value, "no") == 0)
5419 || (x_strcasecmp(value, "off") == 0)) {
5420 result = False;
5421 } else if ((check = strtol(value, &next, 0)) >= 0 && FullS2L(value, next)) {
5422 if (check >= (long) limit) /* i.e., past False=0, True=1 */
5423 check = True;
5424 result = (int) check;
5425 } else {
5426 for (n = 0; n < limit - 2; ++n) {
5427 if (table[n].name == NULL) {
5428 break;
5429 } else if (x_strcasecmp(value, table[n].name) == 0) {
5430 result = table[n].code;
5431 break;
5432 }
5433 }
5434 }
5435
5436 if (result < 0) {
5437 xtermWarning("Unrecognized keyword: %s\n", value);
5438 result = False;
5439 }
5440
5441 TRACE(("extendedBoolean(%s) = %d\n", value, result));
5442 return result;
5443 }
5444
5445 /*
5446 * Something like round() from math library, but round() is less widely-used
5447 * than xterm. Also, there are no negative numbers to complicate this.
5448 */
5449 int
dimRound(double value)5450 dimRound(double value)
5451 {
5452 int result = (int) value;
5453 if (result < value)
5454 ++result;
5455 return result;
5456 }
5457
5458 /*
5459 * Find the geometry of the specified Xinerama screen
5460 */
5461 static void
find_xinerama_screen(Display * display,int screen,struct Xinerama_geometry * ret)5462 find_xinerama_screen(Display *display, int screen, struct Xinerama_geometry *ret)
5463 {
5464 #ifdef HAVE_X11_EXTENSIONS_XINERAMA_H
5465 XineramaScreenInfo *screens;
5466 int nb_screens;
5467
5468 if (screen == -1) /* already inited */
5469 return;
5470 screens = XineramaQueryScreens(display, &nb_screens);
5471 if (screen >= nb_screens) {
5472 xtermWarning("Xinerama screen %d does not exist\n", screen);
5473 return;
5474 }
5475 if (screen == -2) {
5476 int ptr_x, ptr_y;
5477 int dummy_int, i;
5478 unsigned dummy_uint;
5479 Window dummy_win;
5480 if (nb_screens == 0)
5481 return;
5482 XQueryPointer(display, DefaultRootWindow(display),
5483 &dummy_win, &dummy_win,
5484 &ptr_x, &ptr_y,
5485 &dummy_int, &dummy_int, &dummy_uint);
5486 for (i = 0; i < nb_screens; i++) {
5487 if ((ptr_x - screens[i].x_org) < screens[i].width &&
5488 (ptr_y - screens[i].y_org) < screens[i].height) {
5489 screen = i;
5490 break;
5491 }
5492 }
5493 if (screen < 0) {
5494 xtermWarning("Mouse not in any Xinerama screen, using 0\n");
5495 screen = 0;
5496 }
5497 }
5498 ret->scr_x = screens[screen].x_org;
5499 ret->scr_y = screens[screen].y_org;
5500 ret->scr_w = screens[screen].width;
5501 ret->scr_h = screens[screen].height;
5502 #else /* HAVE_X11_EXTENSIONS_XINERAMA_H */
5503 (void) display;
5504 (void) ret;
5505 if (screen > 0)
5506 xtermWarning("Xinerama support not enabled\n");
5507 #endif /* HAVE_X11_EXTENSIONS_XINERAMA_H */
5508 }
5509
5510 /*
5511 * Parse the screen code after the @ in a geometry string.
5512 */
5513 static void
parse_xinerama_screen(Display * display,const char * str,struct Xinerama_geometry * ret)5514 parse_xinerama_screen(Display *display, const char *str, struct Xinerama_geometry *ret)
5515 {
5516 int screen = -1;
5517 char *end;
5518
5519 if (*str == 'g') {
5520 screen = -1;
5521 str++;
5522 } else if (*str == 'c') {
5523 screen = -2;
5524 str++;
5525 } else {
5526 long s = strtol(str, &end, 0);
5527 if (FullS2L(str, end) && ((int) s >= 0)) {
5528 screen = (int) s;
5529 str = end;
5530 }
5531 }
5532 if (*str) {
5533 xtermWarning("invalid Xinerama specification '%s'\n", str);
5534 return;
5535 }
5536 if (screen == -1) /* already done */
5537 return;
5538 find_xinerama_screen(display, screen, ret);
5539 }
5540
5541 /*
5542 * Parse a geometry string with extra Xinerama specification:
5543 * <w>x<h>+<x>+<y>@<screen>.
5544 */
5545 int
XParseXineramaGeometry(Display * display,char * parsestring,struct Xinerama_geometry * ret)5546 XParseXineramaGeometry(Display *display, char *parsestring, struct Xinerama_geometry *ret)
5547 {
5548 char *at, buf[128];
5549
5550 ret->scr_x = 0;
5551 ret->scr_y = 0;
5552 ret->scr_w = DisplayWidth(display, DefaultScreen(display));
5553 ret->scr_h = DisplayHeight(display, DefaultScreen(display));
5554 at = strchr(parsestring, '@');
5555 if (at != NULL && (size_t) (at - parsestring) < sizeof(buf) - 1) {
5556 memcpy(buf, parsestring, (size_t) (at - parsestring));
5557 buf[at - parsestring] = 0;
5558 parsestring = buf;
5559 parse_xinerama_screen(display, at + 1, ret);
5560 }
5561 return ((strlen(parsestring) <= MAX_U_STRING)
5562 ? XParseGeometry(parsestring, &ret->x, &ret->y, &ret->w, &ret->h)
5563 : 0);
5564 }
5565
5566 #if USE_DOUBLE_BUFFER
5567 Window
VDrawable(TScreen * screen)5568 VDrawable(TScreen *screen)
5569 {
5570 screen->needSwap = 1;
5571 return WhichVWin(screen)->drawable;
5572 }
5573 #endif
5574
5575 #if OPT_RENDERFONT
5576 #ifndef discardRenderDraw
5577 void
discardRenderDraw(TScreen * screen)5578 discardRenderDraw(TScreen *screen)
5579 {
5580 if (
5581 #if USE_DOUBLE_BUFFER
5582 resource.buffered &&
5583 #endif
5584 screen->renderDraw) {
5585 XftDrawDestroy(screen->renderDraw);
5586 screen->renderDraw = NULL;
5587 }
5588 }
5589 #endif
5590 #endif /* OPT_RENDERFONT */
5591
5592 char *
xtermSetLocale(int category,String after)5593 xtermSetLocale(int category, String after)
5594 {
5595 char *before = x_strdup(setlocale(category, 0));
5596
5597 (void) setlocale(category, after);
5598 TRACE(("before setlocale :%s\n", NonNull(before)));
5599 TRACE(("updated locale :%s\n", NonNull(setlocale(category, 0))));
5600 return before;
5601 }
5602
5603 void
xtermResetLocale(int category,char * before)5604 xtermResetLocale(int category, char *before)
5605 {
5606 (void) setlocale(category, before);
5607 free(before);
5608 TRACE(("restored locale :%s\n", NonNull(setlocale(category, 0))));
5609 }
5610