Home | History | Annotate | Line # | Download | only in libcurses
      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 
     62 static int	 slk_fmt = SLK_FMT_INVAL;	/* fmt of slk_init */
     63 
     64 /* Safe variants of public functions. */
     65 static int	 __slk_attroff(SCREEN *, const chtype);
     66 static int	 __slk_attron(SCREEN *, const chtype);
     67 static int	 __slk_attrset(SCREEN *, const chtype);
     68 #ifdef HAVE_WCHAR
     69 static int	 __slk_attr_off(SCREEN *, const attr_t, void *);
     70 static int	 __slk_attr_on(SCREEN *, const attr_t, void *);
     71 static int	 __slk_attr_set(SCREEN *, const attr_t, short, void *opt);
     72 static int	 __slk_color(SCREEN *, short);
     73 #endif
     74 
     75 static int	 __slk_clear(SCREEN *);
     76 static char	*__slk_label(SCREEN *, int);
     77 static int	 __slk_restore(SCREEN *);
     78 static int	 __slk_set(SCREEN *, int, const char *, int);
     79 static int	 __slk_touch(SCREEN *);
     80 #ifdef HAVE_WCHAR
     81 static int	 __slk_wset(SCREEN *, int, const wchar_t *, int);
     82 #endif
     83 
     84 /* Internal engine parts. */
     85 static int	 __slk_ripoffline(WINDOW *, int);
     86 static int	 __slk_set_finalise(SCREEN *, int);
     87 static int	 __slk_draw(SCREEN *, int);
     88 static int	 __slk_redraw(SCREEN *);
     89 
     90 /*
     91  * slk_init --
     92  *	Init Soft Label Keys.
     93  */
     94 int
     95 slk_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  */
    116 int
    117 slk_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  */
    128 int
    129 slk_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  */
    140 int
    141 slk_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  */
    152 int
    153 slk_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  */
    164 int
    165 slk_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  */
    176 int
    177 slk_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  */
    188 int
    189 slk_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  */
    200 int
    201 slk_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  */
    212 char *
    213 slk_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  */
    223 int
    224 slk_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  */
    234 int
    235 slk_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  */
    247 int
    248 slk_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  */
    259 int
    260 slk_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  */
    270 int
    271 slk_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  */
    283 int
    284 slk_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  */
    295 static 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  */
    309 static 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  */
    323 static 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  */
    337 static 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  */
    351 static 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  */
    365 static 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  */
    379 static 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  */
    403 static 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  */
    417 static 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  */
    430 int
    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  */
    443 static 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  */
    468 static 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  */
    534 static 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  */
    550 static 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);
    577 out:
    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  */
    590 int
    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  */
    632 void
    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  */
    648 static 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  */
    666 int
    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  */
    729 static 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  */
    811 static 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  */
    901 static 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