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