Home | History | Annotate | Line # | Download | only in libcurses
slk.c revision 1.13
      1 /*	$NetBSD: slk.c,v 1.13 2021/08/15 11:54:12 christos 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.13 2021/08/15 11:54:12 christos Exp $");
     35 #endif				/* not lint */
     36 
     37 #include <ctype.h>
     38 #include <stdlib.h>
     39 #include <string.h>
     40 #include <limits.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 #ifdef DEBUG
    560 	__CTRACE(__CTRACE_INPUT, "__slk_wset: entry\n");
    561 #endif
    562 	olabel = label;
    563 	if ((len = wcsrtombs(NULL, &olabel, 0, &screen->sp)) == -1) {
    564 #ifdef DEBUG
    565 	__CTRACE(__CTRACE_INPUT, "__slk_wset: conversion failed on char 0x%x\n",
    566 	    (uint16_t) *olabel);
    567 #endif
    568 		return ERR;
    569 	}
    570 
    571 #ifdef DEBUG
    572 	__CTRACE(__CTRACE_INPUT, "__slk_wset: wcsrtombs %zu\n", len);
    573 #endif
    574 	len++; /* We need to store the NULL character. */
    575 	if ((str = malloc(len)) == NULL)
    576 		return ERR;
    577 	olabel = label;
    578 	if (wcsrtombs(str, &olabel, len, &screen->sp) == -1)
    579 		goto out;
    580 	result = __slk_set(screen, labnum, str, justify);
    581 out:
    582 	free(str);
    583 #ifdef DEBUG
    584 	__CTRACE(__CTRACE_INPUT, "__slk_wset: return %s\n",
    585 	    (result == OK)?"OK":"ERR");
    586 #endif
    587 	return result;
    588 }
    589 #endif	/* HAVE_WCHAR */
    590 
    591 
    592 /*
    593  * __slk_init --
    594  *	Allocate structures.
    595  */
    596 int
    597 __slk_init(SCREEN *screen)
    598 {
    599 
    600 	__slk_free(screen);	/* safety */
    601 
    602 	screen->slk_format = slk_fmt;
    603 	if (slk_fmt == SLK_FMT_INVAL)
    604 		return OK;
    605 	slk_fmt = SLK_FMT_INVAL;
    606 
    607 	switch(screen->slk_format) {
    608 	case SLK_FMT_3_2_3:
    609 	case SLK_FMT_4_4:
    610 		screen->slk_nlabels = 8;
    611 		break;
    612 	default:	/* impossible */
    613 		return ERR;
    614 	}
    615 
    616 	screen->slk_labels = calloc(screen->slk_nlabels,
    617 				    sizeof(*screen->slk_labels));
    618 	if (screen->slk_labels == NULL)
    619 		return ERR;
    620 
    621 	screen->is_term_slk =
    622 	    t_plab_norm(screen->term) != NULL &&
    623 	    t_num_labels(screen->term) > 0;
    624 	if (screen->is_term_slk) {
    625 		__unripoffline(__slk_ripoffline);
    626 		screen->slk_nlabels = t_num_labels(screen->term);
    627 		screen->slk_label_len = t_label_width(screen->term);
    628 		/* XXX label_height, label_format? */
    629 	}
    630 
    631 	return OK;
    632 }
    633 
    634 /*
    635  * __slk_free --
    636  *	Free allocates resources.
    637  */
    638 void
    639 __slk_free(SCREEN *screen)
    640 {
    641 	int i;
    642 
    643 	if (screen->slk_window != NULL)
    644 		delwin(screen->slk_window);
    645 	for (i = 0; i < screen->slk_nlabels; i++)
    646 		free(screen->slk_labels[i].text);
    647 	free(screen->slk_labels);
    648 }
    649 
    650 /*
    651  * __slk_ripoffline --
    652  *	ripoffline callback to accept a WINDOW to create our keys.
    653  */
    654 static int
    655 __slk_ripoffline(WINDOW *window, int cols)
    656 {
    657 
    658 	if (window == NULL)
    659 		return ERR;
    660 	window->screen->slk_window = window;
    661 	wattron(window,
    662 	    (t_no_color_video(window->screen->term) & 1) == 0
    663 	    ? A_STANDOUT : A_REVERSE);
    664 	__slk_resize(window->screen, cols);
    665 	return OK;
    666 }
    667 
    668 /*
    669  * __slk_resize --
    670  *	Size and position the labels in the ripped off slk window.
    671  */
    672 int
    673 __slk_resize(SCREEN *screen, int cols)
    674 {
    675 	int x = 0;
    676 	struct __slk_label *l;
    677 
    678 	if (screen == NULL)
    679 		return ERR;
    680 	if (screen->is_term_slk || screen->slk_nlabels == 0)
    681 		return OK;
    682 
    683 	screen->slk_label_len = (cols / screen->slk_nlabels) - 1;
    684 	if (screen->slk_label_len > SLK_SIZE)
    685 		screen->slk_label_len = SLK_SIZE;
    686 
    687 	l = screen->slk_labels;
    688 
    689 	switch(screen->slk_format) {
    690 	case SLK_FMT_3_2_3:
    691 		/* Left 3 */
    692 		(l++)->x = x;
    693 		(l++)->x = (x += screen->slk_label_len + 1);
    694 		(l++)->x = (x += screen->slk_label_len + 1);
    695 
    696 		/* Middle 2 */
    697 		x = cols / 2;
    698 		(l++)->x = x -(screen->slk_label_len + 1);
    699 		(l++)->x = x + 1;
    700 
    701 		/* Right 3 */
    702 		x = (cols - ((screen->slk_label_len + 1) * 3)) + 1;
    703 		(l++)->x = x;
    704 		(l++)->x = (x += screen->slk_label_len + 1);
    705 		(l++)->x = (x += screen->slk_label_len + 1);
    706 		break;
    707 
    708 	case SLK_FMT_4_4:
    709 	{
    710 		int i, half;
    711 
    712 		half = screen->slk_nlabels / 2;
    713 		for (i = 0; i < screen->slk_nlabels; i++) {
    714 			(l++)->x = x;
    715 			x += screen->slk_label_len;
    716 			/* Split labels in half */
    717 			if (i == half - 1)
    718 				x = cols - (screen->slk_label_len * half) + 1;
    719 		}
    720 		break;
    721 	}
    722 	}
    723 
    724 	/* Write text to the labels. */
    725 	for (x = 0; x < screen->slk_nlabels; x++)
    726 		__slk_set_finalise(screen, x);
    727 
    728 	return __slk_redraw(screen);
    729 }
    730 
    731 /*
    732  * __slk_set_finalise --
    733  *	Does the grunt work of positioning and sizing the text in the label.
    734  */
    735 static int
    736 __slk_set_finalise(SCREEN *screen, int labnum)
    737 {
    738 	struct __slk_label *l;
    739 	size_t spc, len, width, x;
    740 	char *p;
    741 
    742 	l = &screen->slk_labels[labnum];
    743 	spc = screen->slk_label_len;
    744 
    745 #ifdef HAVE_WCHAR
    746 	len = 0;
    747 	width = 0;
    748 	if (l->text != NULL) {
    749 		size_t plen;
    750 
    751 		p = l->text;
    752 		plen = strlen(l->text);
    753 		while (*p != '\0') {
    754 			size_t mblen;
    755 			wchar_t wc;
    756 			int w;
    757 
    758 			mblen = mbrtowc(&wc, p, plen, &screen->sp);
    759 			if ((ssize_t)mblen < 0)
    760 				return ERR;
    761 			w = wcwidth(wc);
    762 			if (width + w > spc)
    763 				break;
    764 			width += w;
    765 			len += mblen;
    766 			p += mblen;
    767 			plen -= mblen;
    768 		}
    769 	}
    770 #else
    771 	len = l->text == NULL ? 0 : strlen(l->text);
    772 	if (len > spc)
    773 		len = spc;
    774 	width = len;
    775 #endif
    776 
    777 	switch(l->justify) {
    778 	case SLK_JUSTIFY_LEFT:
    779 		x = 0;
    780 		break;
    781 	case SLK_JUSTIFY_CENTER:
    782 		x = (spc - width) / 2;
    783 		if (x + width > spc)
    784 			x--;
    785 		break;
    786 	case SLK_JUSTIFY_RIGHT:
    787 		x = spc - width;
    788 		break;
    789 	default:
    790 		return ERR; /* impossible */
    791 	}
    792 
    793 	p = l->label;
    794 	if (x != 0) {
    795 		memset(p, ' ', x);
    796 		p += x;
    797 		spc -= x;
    798 	}
    799 	if (len != 0) {
    800 		memcpy(p, l->text, len);
    801 		p += len;
    802 		spc -= width;
    803 	}
    804 	if (spc != 0) {
    805 		memset(p, ' ', spc);
    806 		p += spc;
    807 	}
    808 	*p = '\0'; /* Terminate for plab_norm. */
    809 
    810 	return __slk_draw(screen, labnum);
    811 }
    812 
    813 /*
    814  * __slk_draw --
    815  *	Draws the specified key.
    816  */
    817 static int
    818 __slk_draw(SCREEN *screen, int labnum)
    819 {
    820 	const struct __slk_label *l;
    821 	int retval, inc, lcnt, tx;
    822 	char ts[MB_LEN_MAX];
    823 #ifdef HAVE_WCHAR
    824 	cchar_t cc;
    825 	wchar_t wc[2];
    826 #endif
    827 
    828 	if (screen->slk_hidden)
    829 		return OK;
    830 
    831 	retval = OK; /* quiet gcc... */
    832 
    833 	l = &screen->slk_labels[labnum];
    834 	if (screen->is_term_slk)
    835 		return ti_putp(screen->term,
    836 		    ti_tiparm(screen->term,
    837 		    t_plab_norm(screen->term), labnum + 1, l->label));
    838 	else if (screen->slk_window != NULL) {
    839 		if ((labnum != screen->slk_nlabels - 1) ||
    840 		    (screen->slk_window->flags & __SCROLLOK) ||
    841 		    ((l->x + screen->slk_label_len) < screen->slk_window->maxx)) {
    842 			retval = mvwaddnstr(screen->slk_window, 0, l->x,
    843 			    l->label, screen->slk_label_len);
    844 		} else {
    845 			lcnt = 0;
    846 			tx = 0;
    847 			while (lcnt < screen->slk_label_len) {
    848 				inc = wctomb(ts, l->label[lcnt]);
    849 				if (inc < 0) {
    850 					/* conversion failed, skip? */
    851 					lcnt++;
    852 					continue;
    853 				}
    854 
    855 #ifdef DEBUG
    856 	__CTRACE(__CTRACE_INPUT, "__slk_draw: last label, (%d,%d) char[%d] 0x%x\n",
    857 	    l->x + tx, 0, lcnt, l->label[lcnt]);
    858 	__CTRACE(__CTRACE_INPUT, "__slk_draw: label len %d, wcwidth %d\n",
    859 	    screen->slk_label_len, wcwidth(l->label[lcnt]));
    860 #endif
    861 #ifdef HAVE_WCHAR
    862 				wc[0] = l->label[lcnt];
    863 				wc[1] = L'\0';
    864 				if (setcchar(&cc, wc,
    865 				    screen->slk_window->wattr, 0,
    866 				    NULL) == ERR)
    867 					return ERR;
    868 #endif
    869 
    870 				if (l->x + wcwidth(l->label[lcnt] + tx) >=
    871 				    screen->slk_label_len) {
    872 					/* last character that will fit
    873 					 * so insert it to avoid scroll
    874 					 */
    875 #ifdef HAVE_WCHAR
    876 					retval = mvwins_wch(screen->slk_window,
    877 					    0, l->x + tx, &cc);
    878 #else
    879 					retval = mvwinsch(screen->slk_window,
    880 					    0, l->x + tx, l->label[lcnt]);
    881 #endif
    882 				} else {
    883 #ifdef HAVE_WCHAR
    884 					retval = mvwadd_wch(screen->slk_window,
    885 					    0, l->x + tx, &cc);
    886 #else
    887 					retval = mvwaddch(screen->slk_window,
    888 					    0, l->x + tx, l->label[lcnt]);
    889 #endif
    890 				}
    891 				tx += wcwidth(l->label[lcnt]);
    892 				lcnt += inc;
    893 			}
    894 		}
    895 
    896 		return retval;
    897 	} else
    898 		return ERR;
    899 }
    900 
    901 /*
    902  * __slk_draw --
    903  *	Draws all the keys.
    904  */
    905 static int
    906 __slk_redraw(SCREEN *screen)
    907 {
    908 	int i, result = OK;
    909 
    910 	for (i = 0; i < screen->slk_nlabels; i++) {
    911 		if (__slk_draw(screen, i) == ERR)
    912 			result = ERR;
    913 	}
    914 	return result;
    915 }
    916