Home | History | Annotate | Line # | Download | only in libcurses
slk.c revision 1.8
      1 /*	$NetBSD: slk.c,v 1.8 2019/07/28 00:51:59 uwe 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.8 2019/07/28 00:51:59 uwe 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 	olabel = label;
    559 	if ((len = wcsrtombs(NULL, &olabel, 0, &screen->sp)) == -1)
    560 		return ERR;
    561 	len++; /* We need to store the NULL character. */
    562 	if ((str = malloc(len)) == NULL)
    563 		return ERR;
    564 	olabel = label;
    565 	if (wcsrtombs(str, &olabel, len, &screen->sp) == -1)
    566 		goto out;
    567 	result = __slk_set(screen, labnum, str, justify);
    568 out:
    569 	free(str);
    570 	return result;
    571 }
    572 #endif	/* HAVE_WCHAR */
    573 
    574 
    575 /*
    576  * __slk_init --
    577  *	Allocate structures.
    578  */
    579 int
    580 __slk_init(SCREEN *screen)
    581 {
    582 
    583 	__slk_free(screen);	/* safety */
    584 
    585 	screen->slk_format = slk_fmt;
    586 	if (slk_fmt == SLK_FMT_INVAL)
    587 		return OK;
    588 	slk_fmt = SLK_FMT_INVAL;
    589 
    590 	switch(screen->slk_format) {
    591 	case SLK_FMT_3_2_3:
    592 	case SLK_FMT_4_4:
    593 		screen->slk_nlabels = 8;
    594 		break;
    595 	default:	/* impossible */
    596 		return ERR;
    597 	}
    598 
    599 	screen->slk_labels = calloc(screen->slk_nlabels,
    600 				    sizeof(*screen->slk_labels));
    601 	if (screen->slk_labels == NULL)
    602 		return ERR;
    603 
    604 	screen->is_term_slk =
    605 	    t_plab_norm(screen->term) != NULL &&
    606 	    t_num_labels(screen->term) > 0;
    607 	if (screen->is_term_slk) {
    608 		__unripoffline(__slk_ripoffline);
    609 		screen->slk_nlabels = t_num_labels(screen->term);
    610 		screen->slk_label_len = t_label_width(screen->term);
    611 		/* XXX label_height, label_format? */
    612 	}
    613 
    614 	return OK;
    615 }
    616 
    617 /*
    618  * __slk_free --
    619  *	Free allocates resources.
    620  */
    621 void
    622 __slk_free(SCREEN *screen)
    623 {
    624 	int i;
    625 
    626 	if (screen->slk_window != NULL)
    627 		delwin(screen->slk_window);
    628 	for (i = 0; i < screen->slk_nlabels; i++)
    629 		free(screen->slk_labels[i].text);
    630 	free(screen->slk_labels);
    631 }
    632 
    633 /*
    634  * __slk_ripoffline --
    635  *	ripoffline callback to accept a WINDOW to create our keys.
    636  */
    637 static int
    638 __slk_ripoffline(WINDOW *window, int cols)
    639 {
    640 
    641 	if (window == NULL)
    642 		return ERR;
    643 	window->screen->slk_window = window;
    644 	wattron(window,
    645 	    (t_no_color_video(window->screen->term) & 1) == 0
    646 	    ? A_STANDOUT : A_REVERSE);
    647 	__slk_resize(window->screen, cols);
    648 	return OK;
    649 }
    650 
    651 /*
    652  * __slk_resize --
    653  *	Size and position the labels in the ripped off slk window.
    654  */
    655 int
    656 __slk_resize(SCREEN *screen, int cols)
    657 {
    658 	int x = 0;
    659 	struct __slk_label *l;
    660 
    661 	if (screen == NULL)
    662 		return ERR;
    663 	if (screen->is_term_slk || screen->slk_nlabels == 0)
    664 		return OK;
    665 
    666 	screen->slk_label_len = (cols / screen->slk_nlabels) - 1;
    667 	if (screen->slk_label_len > SLK_SIZE)
    668 		screen->slk_label_len = SLK_SIZE;
    669 
    670 	l = screen->slk_labels;
    671 
    672 	switch(screen->slk_format) {
    673 	case SLK_FMT_3_2_3:
    674 		/* Left 3 */
    675 		(l++)->x = x;
    676 		(l++)->x = (x += screen->slk_label_len + 1);
    677 		(l++)->x = (x += screen->slk_label_len + 1);
    678 
    679 		/* Middle 2 */
    680 		x = cols / 2;
    681 		(l++)->x = x -(screen->slk_label_len + 1);
    682 		(l++)->x = x + 1;
    683 
    684 		/* Right 3 */
    685 		x = (cols - ((screen->slk_label_len + 1) * 3)) + 1;
    686 		(l++)->x = x;
    687 		(l++)->x = (x += screen->slk_label_len + 1);
    688 		(l++)->x = (x += screen->slk_label_len + 1);
    689 		break;
    690 
    691 	case SLK_FMT_4_4:
    692 	{
    693 		int i, half;
    694 
    695 		half = screen->slk_nlabels / 2;
    696 		for (i = 0; i < screen->slk_nlabels; i++) {
    697 			(l++)->x = x;
    698 			x += screen->slk_label_len;
    699 			/* Split labels in half */
    700 			if (i == half - 1)
    701 				x = cols - (screen->slk_label_len * half) + 1;
    702 		}
    703 		break;
    704 	}
    705 	}
    706 
    707 	/* Write text to the labels. */
    708 	for (x = 0; x < screen->slk_nlabels; x++)
    709 		__slk_set_finalise(screen, x);
    710 
    711 	return __slk_redraw(screen);
    712 }
    713 
    714 /*
    715  * __slk_set_finalise --
    716  *	Does the grunt work of positioning and sizing the text in the label.
    717  */
    718 static int
    719 __slk_set_finalise(SCREEN *screen, int labnum)
    720 {
    721 	struct __slk_label *l;
    722 	size_t spc, len, width, x;
    723 	char *p;
    724 
    725 	l = &screen->slk_labels[labnum];
    726 	spc = screen->slk_label_len;
    727 
    728 #ifdef HAVE_WCHAR
    729 	len = 0;
    730 	width = 0;
    731 	if (l->text != NULL) {
    732 		size_t plen;
    733 
    734 		p = l->text;
    735 		plen = strlen(l->text);
    736 		while (*p != '\0') {
    737 			size_t mblen;
    738 			wchar_t wc;
    739 			int w;
    740 
    741 			mblen = mbrtowc(&wc, p, plen, &screen->sp);
    742 			if ((ssize_t)mblen < 0)
    743 				return ERR;
    744 			w = wcwidth(wc);
    745 			if (width + w > spc)
    746 				break;
    747 			width += w;
    748 			len += mblen;
    749 			p += mblen;
    750 			plen -= mblen;
    751 		}
    752 	}
    753 #else
    754 	len = l->text == NULL ? 0 : strlen(l->text);
    755 	if (len > spc)
    756 		len = spc;
    757 	width = len;
    758 #endif
    759 
    760 	switch(l->justify) {
    761 	case SLK_JUSTIFY_LEFT:
    762 		x = 0;
    763 		break;
    764 	case SLK_JUSTIFY_CENTER:
    765 		x = (spc - width) / 2;
    766 		if (x + width > spc)
    767 			x--;
    768 		break;
    769 	case SLK_JUSTIFY_RIGHT:
    770 		x = spc - width;
    771 		break;
    772 	default:
    773 		return ERR; /* impossible */
    774 	}
    775 
    776 	p = l->label;
    777 	if (x != 0) {
    778 		memset(p, ' ', x);
    779 		p += x;
    780 		spc -= x;
    781 	}
    782 	if (len != 0) {
    783 		memcpy(p, l->text, len);
    784 		p += len;
    785 		spc -= width;
    786 	}
    787 	if (spc != 0) {
    788 		memset(p, ' ', spc);
    789 		p += spc;
    790 	}
    791 	*p = '\0'; /* Terminate for plab_norm. */
    792 
    793 	return __slk_draw(screen, labnum);
    794 }
    795 
    796 /*
    797  * __slk_draw --
    798  *	Draws the specified key.
    799  */
    800 static int
    801 __slk_draw(SCREEN *screen, int labnum)
    802 {
    803 	const struct __slk_label *l;
    804 
    805 	if (screen->slk_hidden)
    806 		return OK;
    807 
    808 	l = &screen->slk_labels[labnum];
    809 	if (screen->is_term_slk)
    810 		return ti_putp(screen->term,
    811 		    ti_tiparm(screen->term,
    812 		    t_plab_norm(screen->term), labnum + 1, l->label));
    813 	else if (screen->slk_window != NULL)
    814 		return mvwaddnstr(screen->slk_window, 0, l->x,
    815 		    l->label, screen->slk_label_len);
    816 	else
    817 		return ERR;
    818 }
    819 
    820 /*
    821  * __slk_draw --
    822  *	Draws all the keys.
    823  */
    824 static int
    825 __slk_redraw(SCREEN *screen)
    826 {
    827 	int i, result = OK;
    828 
    829 	for (i = 0; i < screen->slk_nlabels; i++) {
    830 		if (__slk_draw(screen, i) == ERR)
    831 			result = ERR;
    832 	}
    833 	return result;
    834 }
    835