xref: /netbsd-src/lib/libcurses/slk.c (revision 53b02e147d4ed531c0d2a5ca9b3e8026ba3e99b5)
1 /*	$NetBSD: slk.c,v 1.16 2021/09/06 07:45:48 rin Exp $	*/
2 
3 /*-
4  * Copyright (c) 2017 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Roy Marples.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 #ifndef lint
34 __RCSID("$NetBSD: slk.c,v 1.16 2021/09/06 07:45:48 rin Exp $");
35 #endif				/* not lint */
36 
37 #include <ctype.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #ifdef HAVE_WCHAR
41 #include <wctype.h>
42 #endif
43 
44 #include "curses.h"
45 #include "curses_private.h"
46 
47 /* Terminals with real soft labels have NOT been tested.
48  * If you have such a device, please let us know so this comment
49  * can be adjusted. */
50 
51 /* POSIX says that each label can be up to 8 columns.
52  * However, our implementation can allow labels to expand beyond that. */
53 //#define	SLK_SIZE_DYNAMIC
54 #ifdef SLK_SIZE_DYNAMIC
55 #define	SLK_SIZE	MAX_SLK_LABEL
56 #else
57 #define	SLK_SIZE	MAX_SLK_COLS
58 #endif
59 
60 static int	 slk_fmt = SLK_FMT_INVAL;	/* fmt of slk_init */
61 
62 /* Safe variants of public functions. */
63 static int	 __slk_attroff(SCREEN *, const chtype);
64 static int	 __slk_attron(SCREEN *, const chtype);
65 static int	 __slk_attrset(SCREEN *, const chtype);
66 #ifdef HAVE_WCHAR
67 static int	 __slk_attr_off(SCREEN *, const attr_t, void *);
68 static int	 __slk_attr_on(SCREEN *, const attr_t, void *);
69 static int	 __slk_attr_set(SCREEN *, const attr_t, short, void *opt);
70 static int	 __slk_color(SCREEN *, short);
71 #endif
72 
73 static int	 __slk_clear(SCREEN *);
74 static char	*__slk_label(SCREEN *, int);
75 static int	 __slk_restore(SCREEN *);
76 static int	 __slk_set(SCREEN *, int, const char *, int);
77 static int	 __slk_touch(SCREEN *);
78 #ifdef HAVE_WCHAR
79 static int	 __slk_wset(SCREEN *, int, const wchar_t *, int);
80 #endif
81 
82 /* Internal engine parts. */
83 static int	 __slk_ripoffline(WINDOW *, int);
84 static int	 __slk_set_finalise(SCREEN *, int);
85 static int	 __slk_draw(SCREEN *, int);
86 static int	 __slk_redraw(SCREEN *);
87 
88 /*
89  * slk_init --
90  *	Init Soft Label Keys.
91  */
92 int
93 slk_init(int fmt)
94 {
95 
96 	switch(fmt) {
97 	case SLK_FMT_3_2_3:
98 	case SLK_FMT_4_4:
99 		break;
100 	default:
101 		return ERR;
102 	}
103 
104 	slk_fmt = fmt;
105 	/* Even if the terminal supports soft label keys directly,
106 	 * we need to reserve a line. */
107 	return ripoffline(-1, __slk_ripoffline);
108 }
109 
110 /*
111  * slk_attron --
112  *	Test and set attributes on ripped off slk window.
113  */
114 int
115 slk_attron(const chtype attr)
116 {
117 
118 	return __slk_attron(_cursesi_screen, attr);
119 }
120 
121 #ifdef HAVE_WCHAR
122 /*
123  * slk_attr_on --
124  *	Test and set wide attributes on ripped off slk window.
125  */
126 int
127 slk_attr_on(const attr_t attr, void *opt)
128 {
129 
130 	return __slk_attr_on(_cursesi_screen, attr, opt);
131 }
132 #endif	/* HAVE_WCHAR */
133 
134 /*
135  * slk_attroff --
136  *	Test and unset attributes on ripped off slk window.
137  */
138 int
139 slk_attroff(const chtype attr)
140 {
141 
142 	return __slk_attroff(_cursesi_screen, attr);
143 }
144 
145 #ifdef HAVE_WCHAR
146 /*
147  * slk_attr_off --
148  *	Test and unset wide attributes on ripped off slk window.
149  */
150 int
151 slk_attr_off(const attr_t attr, void *opt)
152 {
153 
154 	return __slk_attr_off(_cursesi_screen, attr, opt);
155 }
156 #endif	/* HAVE_WCHAR */
157 
158 /*
159  * slk_attrset --
160  *	Set attributes and color pair on ripped off slk window.
161  */
162 int
163 slk_attrset(const chtype attr)
164 {
165 
166 	return __slk_attrset(_cursesi_screen, attr);
167 }
168 
169 #ifdef HAVE_WCHAR
170 /*
171  * slk_attr_set --
172  *	Set wide attributes and color pair on ripped off slk window.
173  */
174 int
175 slk_attr_set(const attr_t attr, short pair, void *opt)
176 {
177 
178 	return __slk_attr_set(_cursesi_screen, attr, pair, opt);
179 }
180 #endif	/* HAVE_WCHAR */
181 
182 /*
183  * slk_clear --
184  *	Clear slk from the current screen.
185  */
186 int
187 slk_clear(void)
188 {
189 
190 	return __slk_clear(_cursesi_screen);
191 }
192 
193 #ifdef HAVE_WCHAR
194 /*
195  * slk_color --
196  *	Set color pair on ripped off slk window.
197  */
198 int
199 slk_color(short pair)
200 {
201 
202 	return __slk_color(_cursesi_screen, pair);
203 }
204 #endif	/* HAVE_WCHAR */
205 
206 /*
207  * slk_label --
208  *	Return a pointer to the saved label for key labnum.
209  */
210 char *
211 slk_label(int labnum)
212 {
213 
214 	return __slk_label(_cursesi_screen, labnum);
215 }
216 
217 /*
218  * slk_wnoutrefresh --
219  *	Add the contents of the ripped off slk window to the virtual window.
220  */
221 int
222 slk_noutrefresh(void)
223 {
224 
225 	return __slk_noutrefresh(_cursesi_screen);
226 }
227 
228 /*
229  * slk_refresh --
230  *	Force a refresh for the ripped off slk window.
231  */
232 int
233 slk_refresh(void)
234 {
235 
236 	if (slk_noutrefresh() == ERR)
237 		return ERR;
238 	return doupdate();
239 }
240 
241 /*
242  * slk_restore --
243  *	Retore slk to the screen after a slk_clear.
244  */
245 int
246 slk_restore(void)
247 {
248 
249 	return __slk_restore(_cursesi_screen);
250 }
251 
252 /*
253  * slk_set --
254  *	Sets the text of the label specified by labnum
255  *	and how it is displayed.
256  */
257 int
258 slk_set(int labnum, const char *label, int justify)
259 {
260 
261 	return __slk_set(_cursesi_screen, labnum, label, justify);
262 }
263 
264 /*
265  * slk_touch --
266  *	Sets the ripped off slk window as modified.
267  */
268 int
269 slk_touch(void)
270 {
271 
272 	return __slk_touch(_cursesi_screen);
273 }
274 
275 #ifdef HAVE_WCHAR
276 /*
277  * slk_wset --
278  *	Sets the wide text of the label specified by labnum
279  *	and how it is displayed.
280  */
281 int
282 slk_wset(int labnum, const wchar_t *label, int justify)
283 {
284 
285 	return __slk_wset(_cursesi_screen, labnum, label, justify);
286 }
287 #endif	/* HAVE_WCHAR */
288 
289 /*
290  * __slk_attron --
291  *	Test and set attributes on ripped off slk window.
292  */
293 static int
294 __slk_attron(SCREEN *screen, const chtype attr)
295 {
296 
297 	if (screen == NULL || screen->slk_window == NULL)
298 		return ERR;
299 	return wattron(screen->slk_window, attr);
300 }
301 
302 #ifdef HAVE_WCHAR
303 /*
304  * __slk_attr_on --
305  *	Test and set wide attributes on ripped off slk window.
306  */
307 static int
308 __slk_attr_on(SCREEN *screen, const attr_t attr, void *opt)
309 {
310 
311 	if (screen == NULL || screen->slk_window == NULL)
312 		return ERR;
313 	return wattr_on(screen->slk_window, attr, opt);
314 }
315 #endif	/* HAVE_WCHAR */
316 
317 /*
318  * __slk_attroff --
319  *	Test and unset attributes on ripped off slk window.
320  */
321 static int
322 __slk_attroff(SCREEN *screen, const chtype attr)
323 {
324 
325 	if (screen == NULL || screen->slk_window == NULL)
326 		return ERR;
327 	return wattroff(screen->slk_window, attr);
328 }
329 
330 #ifdef HAVE_WCHAR
331 /*
332  * __slk_attr_off --
333  *	Test and unset wide attributes on ripped off slk window.
334  */
335 static int
336 __slk_attr_off(SCREEN *screen, const attr_t attr, void *opt)
337 {
338 
339 	if (screen == NULL || screen->slk_window == NULL)
340 		return ERR;
341 	return wattr_off(screen->slk_window, attr, opt);
342 }
343 #endif	/* HAVE_WCHAR */
344 
345 /*
346  * __slk_attrset --
347  *	Set attributes and color pair on ripped off slk window.
348  */
349 static int
350 __slk_attrset(SCREEN *screen, const chtype attr)
351 {
352 
353 	if (screen == NULL || screen->slk_window == NULL)
354 		return ERR;
355 	return wattrset(screen->slk_window, attr);
356 }
357 
358 #ifdef HAVE_WCHAR
359 /*
360  * __slk_attr_set --
361  *	Set wide attributes and color pair on ripped off slk window.
362  */
363 static int
364 __slk_attr_set(SCREEN *screen, const attr_t attr, short pair, void *opt)
365 {
366 
367 	if (screen == NULL || screen->slk_window == NULL)
368 		return ERR;
369 	return wattr_set(screen->slk_window, attr, pair, opt);
370 }
371 #endif	/* HAVE_WCHAR */
372 
373 /*
374  * __slk_clear --
375  *	Clear slk from the current screen.
376  */
377 static int
378 __slk_clear(SCREEN *screen)
379 {
380 
381 	if (screen == NULL)
382 		return ERR;
383 	screen->slk_hidden = true;
384 	if (screen->is_term_slk) {
385 		if (t_label_off(screen->term) == NULL)
386 			return ERR;
387 		return ti_putp(screen->term,
388 		    ti_tiparm(screen->term, t_label_off(screen->term)));
389 	}
390 	if (screen->slk_window == NULL)
391 		return ERR;
392 	werase(screen->slk_window);
393 	return wrefresh(screen->slk_window);
394 }
395 
396 #ifdef HAVE_WCHAR
397 /*
398  * __slk_color --
399  *	Set color pair on ripped off slk window.
400  */
401 static int
402 __slk_color(SCREEN *screen, short pair)
403 {
404 
405 	if (screen == NULL || screen->slk_window == NULL)
406 		return ERR;
407 	return wcolor_set(screen->slk_window, pair, NULL);
408 }
409 #endif	/* HAVE_WCHAR */
410 
411 /*
412  * __slk_label --
413  *	Return a pointer to the saved label for key labnum.
414  */
415 static char *
416 __slk_label(SCREEN *screen, int labnum)
417 {
418 
419 	if (screen == NULL || labnum < 1 || labnum > screen->slk_nlabels)
420 		return NULL;
421 	return screen->slk_labels[--labnum].text;
422 }
423 
424 /*
425  * __slk_wnoutrefresh --
426  *	Add the contents of the ripped off slk window to the virtual window.
427  */
428 int
429 __slk_noutrefresh(SCREEN *screen)
430 {
431 
432 	if (screen == NULL || screen->slk_window == NULL)
433 		return ERR;
434 	return wnoutrefresh(screen->slk_window);
435 }
436 
437 /*
438  * __slk_restore --
439  *	Retore slk to the screen after a slk_clear.
440  */
441 static int
442 __slk_restore(SCREEN *screen)
443 {
444 
445 	if (screen == NULL)
446 		return ERR;
447 	screen->slk_hidden = false;
448 	if (screen->is_term_slk) {
449 		if (t_label_on(screen->term) == NULL)
450 			return ERR;
451 		return ti_putp(screen->term,
452 		    ti_tiparm(screen->term, t_label_on(screen->term)));
453 	}
454 	if (screen->slk_window == NULL)
455 		return ERR;
456 	if (__slk_redraw(screen) == ERR)
457 		return ERR;
458 	return wrefresh(screen->slk_window);
459 }
460 
461 /*
462  * __slk_set --
463  *	Sets the text of the label specified by labnum
464  *	and how it is displayed.
465  */
466 static int
467 __slk_set(SCREEN *screen, int labnum, const char *label, int justify)
468 {
469 	struct __slk_label *l;
470 	const char *end;
471 	size_t len;
472 	char *text;
473 #ifdef HAVE_WCHAR
474 	wchar_t wc;
475 	size_t wc_len;
476 #endif
477 
478 	/* Check args. */
479 	if (screen == NULL || labnum < 1 || labnum > screen->slk_nlabels)
480 		return ERR;
481 	switch(justify) {
482 	case SLK_JUSTIFY_LEFT:
483 	case SLK_JUSTIFY_CENTER:
484 	case SLK_JUSTIFY_RIGHT:
485 		break;
486 	default:
487 		return ERR;
488 	}
489 	if (label == NULL)
490 		label = "";
491 
492 	/* Skip leading whitespace. */
493 	while(isspace((unsigned char)*label))
494 		label++;
495 	/* Grab end. */
496 	end = label;
497 
498 #ifdef HAVE_WCHAR
499 	size_t endlen = strlen(end);
500 	while (*end != '\0') {
501 		wc_len = mbrtowc(&wc, end, endlen, &screen->sp);
502 		if ((ssize_t)wc_len < 0)
503 			return ERR;
504 		if (!iswprint((wint_t)wc))
505 			break;
506 		end += wc_len;
507 		endlen -= wc_len;
508 	}
509 #else
510 	while(isprint((unsigned char)*end))
511 		end++;
512 #endif
513 	len = end - label;
514 
515 	/* Take a backup, in-case we can grow the label. */
516 	if ((text = strndup(label, len)) == NULL)
517 		return ERR;
518 
519 	/* All checks out, assign. */
520 	l = &screen->slk_labels[--labnum]; /* internal zero based index */
521 	l->text = text;
522 	l->justify = justify;
523 
524 	__slk_set_finalise(screen, labnum);
525 	return OK;
526 }
527 
528 /*
529  * __slk_touch --
530  *	Sets the ripped off slk window as modified.
531  */
532 static int
533 __slk_touch(SCREEN *screen)
534 {
535 
536 	if (screen == NULL || screen->slk_window == NULL)
537 		return ERR;
538 	return touchwin(screen->slk_window);
539 }
540 
541 
542 #ifdef HAVE_WCHAR
543 /*
544  * __slk_wset --
545  *	Sets the wide text of the label specified by labnum
546  *	and how it is displayed.
547  */
548 static int
549 __slk_wset(SCREEN *screen, int labnum, const wchar_t *label, int justify)
550 {
551 	const wchar_t *olabel;
552 	size_t len;
553 	char *str;
554 	int result = ERR;
555 
556 	if (screen == NULL)
557 		return ERR;
558 	__CTRACE(__CTRACE_INPUT, "__slk_wset: entry\n");
559 	olabel = label;
560 	if ((len = wcsrtombs(NULL, &olabel, 0, &screen->sp)) == -1) {
561 	__CTRACE(__CTRACE_INPUT,
562 	    "__slk_wset: conversion failed on char 0x%hx\n",
563 	    (uint16_t)*olabel);
564 		return ERR;
565 	}
566 
567 	__CTRACE(__CTRACE_INPUT, "__slk_wset: wcsrtombs %zu\n", len);
568 	len++; /* We need to store the NULL character. */
569 	if ((str = malloc(len)) == NULL)
570 		return ERR;
571 	olabel = label;
572 	if (wcsrtombs(str, &olabel, len, &screen->sp) == -1)
573 		goto out;
574 	result = __slk_set(screen, labnum, str, justify);
575 out:
576 	free(str);
577 	__CTRACE(__CTRACE_INPUT, "__slk_wset: return %s\n",
578 	    result == OK ? "OK" : "ERR");
579 	return result;
580 }
581 #endif	/* HAVE_WCHAR */
582 
583 
584 /*
585  * __slk_init --
586  *	Allocate structures.
587  */
588 int
589 __slk_init(SCREEN *screen)
590 {
591 
592 	__slk_free(screen);	/* safety */
593 
594 	screen->slk_format = slk_fmt;
595 	if (slk_fmt == SLK_FMT_INVAL)
596 		return OK;
597 	slk_fmt = SLK_FMT_INVAL;
598 
599 	switch(screen->slk_format) {
600 	case SLK_FMT_3_2_3:
601 	case SLK_FMT_4_4:
602 		screen->slk_nlabels = 8;
603 		break;
604 	default:	/* impossible */
605 		return ERR;
606 	}
607 
608 	screen->slk_labels = calloc(screen->slk_nlabels,
609 				    sizeof(*screen->slk_labels));
610 	if (screen->slk_labels == NULL)
611 		return ERR;
612 
613 	screen->is_term_slk =
614 	    t_plab_norm(screen->term) != NULL &&
615 	    t_num_labels(screen->term) > 0;
616 	if (screen->is_term_slk) {
617 		__unripoffline(__slk_ripoffline);
618 		screen->slk_nlabels = t_num_labels(screen->term);
619 		screen->slk_label_len = t_label_width(screen->term);
620 		/* XXX label_height, label_format? */
621 	}
622 
623 	return OK;
624 }
625 
626 /*
627  * __slk_free --
628  *	Free allocates resources.
629  */
630 void
631 __slk_free(SCREEN *screen)
632 {
633 	int i;
634 
635 	if (screen->slk_window != NULL)
636 		delwin(screen->slk_window);
637 	for (i = 0; i < screen->slk_nlabels; i++)
638 		free(screen->slk_labels[i].text);
639 	free(screen->slk_labels);
640 }
641 
642 /*
643  * __slk_ripoffline --
644  *	ripoffline callback to accept a WINDOW to create our keys.
645  */
646 static int
647 __slk_ripoffline(WINDOW *window, int cols)
648 {
649 
650 	if (window == NULL)
651 		return ERR;
652 	window->screen->slk_window = window;
653 	wattron(window,
654 	    (t_no_color_video(window->screen->term) & 1) == 0
655 	    ? A_STANDOUT : A_REVERSE);
656 	__slk_resize(window->screen, cols);
657 	return OK;
658 }
659 
660 /*
661  * __slk_resize --
662  *	Size and position the labels in the ripped off slk window.
663  */
664 int
665 __slk_resize(SCREEN *screen, int cols)
666 {
667 	int x = 0;
668 	struct __slk_label *l;
669 
670 	if (screen == NULL)
671 		return ERR;
672 	if (screen->is_term_slk || screen->slk_nlabels == 0)
673 		return OK;
674 
675 	screen->slk_label_len = (cols / screen->slk_nlabels) - 1;
676 	if (screen->slk_label_len > SLK_SIZE)
677 		screen->slk_label_len = SLK_SIZE;
678 
679 	l = screen->slk_labels;
680 
681 	switch(screen->slk_format) {
682 	case SLK_FMT_3_2_3:
683 		/* Left 3 */
684 		(l++)->x = x;
685 		(l++)->x = (x += screen->slk_label_len + 1);
686 		(l++)->x = (x += screen->slk_label_len + 1);
687 
688 		/* Middle 2 */
689 		x = cols / 2;
690 		(l++)->x = x -(screen->slk_label_len + 1);
691 		(l++)->x = x + 1;
692 
693 		/* Right 3 */
694 		x = (cols - ((screen->slk_label_len + 1) * 3)) + 1;
695 		(l++)->x = x;
696 		(l++)->x = (x += screen->slk_label_len + 1);
697 		(l++)->x = (x += screen->slk_label_len + 1);
698 		break;
699 
700 	case SLK_FMT_4_4:
701 	{
702 		int i, half;
703 
704 		half = screen->slk_nlabels / 2;
705 		for (i = 0; i < screen->slk_nlabels; i++) {
706 			(l++)->x = x;
707 			x += screen->slk_label_len;
708 			/* Split labels in half */
709 			if (i == half - 1)
710 				x = cols - (screen->slk_label_len * half) + 1;
711 		}
712 		break;
713 	}
714 	}
715 
716 	/* Write text to the labels. */
717 	for (x = 0; x < screen->slk_nlabels; x++)
718 		__slk_set_finalise(screen, x);
719 
720 	return __slk_redraw(screen);
721 }
722 
723 /*
724  * __slk_set_finalise --
725  *	Does the grunt work of positioning and sizing the text in the label.
726  */
727 static int
728 __slk_set_finalise(SCREEN *screen, int labnum)
729 {
730 	struct __slk_label *l;
731 	size_t spc, len, width, x;
732 	char *p;
733 
734 	l = &screen->slk_labels[labnum];
735 	spc = screen->slk_label_len;
736 
737 #ifdef HAVE_WCHAR
738 	len = 0;
739 	width = 0;
740 	if (l->text != NULL) {
741 		size_t plen;
742 
743 		p = l->text;
744 		plen = strlen(l->text);
745 		while (*p != '\0') {
746 			size_t mblen;
747 			wchar_t wc;
748 			int w;
749 
750 			mblen = mbrtowc(&wc, p, plen, &screen->sp);
751 			if ((ssize_t)mblen < 0)
752 				return ERR;
753 			w = wcwidth(wc);
754 			if (width + w > spc)
755 				break;
756 			width += w;
757 			len += mblen;
758 			p += mblen;
759 			plen -= mblen;
760 		}
761 	}
762 #else
763 	len = l->text == NULL ? 0 : strlen(l->text);
764 	if (len > spc)
765 		len = spc;
766 	width = len;
767 #endif
768 
769 	switch(l->justify) {
770 	case SLK_JUSTIFY_LEFT:
771 		x = 0;
772 		break;
773 	case SLK_JUSTIFY_CENTER:
774 		x = (spc - width) / 2;
775 		if (x + width > spc)
776 			x--;
777 		break;
778 	case SLK_JUSTIFY_RIGHT:
779 		x = spc - width;
780 		break;
781 	default:
782 		return ERR; /* impossible */
783 	}
784 
785 	p = l->label;
786 	if (x != 0) {
787 		memset(p, ' ', x);
788 		p += x;
789 		spc -= x;
790 	}
791 	if (len != 0) {
792 		memcpy(p, l->text, len);
793 		p += len;
794 		spc -= width;
795 	}
796 	if (spc != 0) {
797 		memset(p, ' ', spc);
798 		p += spc;
799 	}
800 	*p = '\0'; /* Terminate for plab_norm. */
801 
802 	return __slk_draw(screen, labnum);
803 }
804 
805 /*
806  * __slk_draw --
807  *	Draws the specified key.
808  */
809 static int
810 __slk_draw(SCREEN *screen, int labnum)
811 {
812 	const struct __slk_label *l;
813 	int retval, inc, lcnt, tx;
814 	char ts[MB_LEN_MAX];
815 #ifdef HAVE_WCHAR
816 	cchar_t cc;
817 	wchar_t wc[2];
818 #endif
819 
820 	if (screen->slk_hidden)
821 		return OK;
822 
823 	retval = OK; /* quiet gcc... */
824 
825 	l = &screen->slk_labels[labnum];
826 	if (screen->is_term_slk)
827 		return ti_putp(screen->term,
828 		    ti_tiparm(screen->term,
829 		    t_plab_norm(screen->term), labnum + 1, l->label));
830 	else if (screen->slk_window != NULL) {
831 		if ((labnum != screen->slk_nlabels - 1) ||
832 		    (screen->slk_window->flags & __SCROLLOK) ||
833 		    ((l->x + screen->slk_label_len) < screen->slk_window->maxx)) {
834 			retval = mvwaddnstr(screen->slk_window, 0, l->x,
835 			    l->label, screen->slk_label_len);
836 		} else {
837 			lcnt = 0;
838 			tx = 0;
839 			while (lcnt < screen->slk_label_len) {
840 				inc = wctomb(ts, l->label[lcnt]);
841 				if (inc < 0) {
842 					/* conversion failed, skip? */
843 					lcnt++;
844 					continue;
845 				}
846 
847 	__CTRACE(__CTRACE_INPUT,
848 	    "__slk_draw: last label, (%d,%d) char[%d] 0x%x\n",
849 	    l->x + tx, 0, lcnt, l->label[lcnt]);
850 	__CTRACE(__CTRACE_INPUT, "__slk_draw: label len %d, wcwidth %d\n",
851 	    screen->slk_label_len, wcwidth(l->label[lcnt]));
852 #ifdef HAVE_WCHAR
853 				wc[0] = l->label[lcnt];
854 				wc[1] = L'\0';
855 				if (setcchar(&cc, wc,
856 				    screen->slk_window->wattr, 0,
857 				    NULL) == ERR)
858 					return ERR;
859 #endif
860 
861 				if (l->x + wcwidth(l->label[lcnt] + tx) >=
862 				    screen->slk_label_len) {
863 					/* last character that will fit
864 					 * so insert it to avoid scroll
865 					 */
866 #ifdef HAVE_WCHAR
867 					retval = mvwins_wch(screen->slk_window,
868 					    0, l->x + tx, &cc);
869 #else
870 					retval = mvwinsch(screen->slk_window,
871 					    0, l->x + tx, l->label[lcnt]);
872 #endif
873 				} else {
874 #ifdef HAVE_WCHAR
875 					retval = mvwadd_wch(screen->slk_window,
876 					    0, l->x + tx, &cc);
877 #else
878 					retval = mvwaddch(screen->slk_window,
879 					    0, l->x + tx, l->label[lcnt]);
880 #endif
881 				}
882 				tx += wcwidth(l->label[lcnt]);
883 				lcnt += inc;
884 			}
885 		}
886 
887 		return retval;
888 	} else
889 		return ERR;
890 }
891 
892 /*
893  * __slk_draw --
894  *	Draws all the keys.
895  */
896 static int
897 __slk_redraw(SCREEN *screen)
898 {
899 	int i, result = OK;
900 
901 	for (i = 0; i < screen->slk_nlabels; i++) {
902 		if (__slk_draw(screen, i) == ERR)
903 			result = ERR;
904 	}
905 	return result;
906 }
907