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