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