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