Home | History | Annotate | Line # | Download | only in libcurses
slk.c revision 1.5
      1 /*	$NetBSD: slk.c,v 1.5 2019/07/27 00:46:43 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.5 2019/07/27 00:46:43 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 #ifdef HAVE_WCHAR
     64 static int	 __slk_attr_off(SCREEN *, const attr_t, void *);
     65 static int	 __slk_attr_on(SCREEN *, const attr_t, void *);
     66 static int	 __slk_attr_set(SCREEN *, const attr_t, short, void *opt);
     67 #endif
     68 static int	 __slk_attroff(SCREEN *, const chtype);
     69 static int	 __slk_attron(SCREEN *, const chtype);
     70 static int	 __slk_attrset(SCREEN *, const chtype);
     71 
     72 static int	 __slk_color(SCREEN *, short);
     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 /*
    194  * slk_color --
    195  *	Set color pair on ripped off slk window.
    196  */
    197 int
    198 slk_color(short pair)
    199 {
    200 
    201 	return __slk_color(_cursesi_screen, pair);
    202 }
    203 
    204 /*
    205  * slk_label --
    206  *	Return a pointer to the saved label for key labnum.
    207  */
    208 char *
    209 slk_label(int labnum)
    210 {
    211 
    212 	return __slk_label(_cursesi_screen, labnum);
    213 }
    214 
    215 /*
    216  * slk_wnoutrefresh --
    217  *	Add the contents of the ripped off slk window to the virtual window.
    218  */
    219 int
    220 slk_noutrefresh(void)
    221 {
    222 
    223 	return __slk_noutrefresh(_cursesi_screen);
    224 }
    225 
    226 /*
    227  * slk_refresh --
    228  *	Force a refresh for the ripped off slk window.
    229  */
    230 int
    231 slk_refresh(void)
    232 {
    233 
    234 	if (slk_noutrefresh() == ERR)
    235 		return ERR;
    236 	return doupdate();
    237 }
    238 
    239 /*
    240  * slk_restore --
    241  *	Retore slk to the screen after a slk_clear.
    242  */
    243 int
    244 slk_restore(void)
    245 {
    246 
    247 	return __slk_restore(_cursesi_screen);
    248 }
    249 
    250 /*
    251  * slk_set --
    252  *	Sets the text of the label specified by labnum
    253  *	and how it is displayed.
    254  */
    255 int
    256 slk_set(int labnum, const char *label, int justify)
    257 {
    258 
    259 	return __slk_set(_cursesi_screen, labnum, label, justify);
    260 }
    261 
    262 /*
    263  * slk_touch --
    264  *	Sets the ripped off slk window as modified.
    265  */
    266 int
    267 slk_touch(void)
    268 {
    269 
    270 	return __slk_touch(_cursesi_screen);
    271 }
    272 
    273 #ifdef HAVE_WCHAR
    274 /*
    275  * slk_wset --
    276  *	Sets the wide text of the label specified by labnum
    277  *	and how it is displayed.
    278  */
    279 int
    280 slk_wset(int labnum, const wchar_t *label, int justify)
    281 {
    282 
    283 	return __slk_wset(_cursesi_screen, labnum, label, justify);
    284 }
    285 #endif	/* HAVE_WCHAR */
    286 
    287 /*
    288  * __slk_attron --
    289  *	Test and set attributes on ripped off slk window.
    290  */
    291 static int
    292 __slk_attron(SCREEN *screen, const chtype attr)
    293 {
    294 
    295 	if (screen == NULL || screen->slk_window == NULL)
    296 		return ERR;
    297 	return wattron(screen->slk_window, attr);
    298 }
    299 
    300 #ifdef HAVE_WCHAR
    301 /*
    302  * __slk_attr_on --
    303  *	Test and set wide attributes on ripped off slk window.
    304  */
    305 static int
    306 __slk_attr_on(SCREEN *screen, const attr_t attr, void *opt)
    307 {
    308 
    309 	if (screen == NULL || screen->slk_window == NULL)
    310 		return ERR;
    311 	return wattr_on(screen->slk_window, attr, opt);
    312 }
    313 #endif	/* HAVE_WCHAR */
    314 
    315 /*
    316  * __slk_attroff --
    317  *	Test and unset attributes on ripped off slk window.
    318  */
    319 static int
    320 __slk_attroff(SCREEN *screen, const chtype attr)
    321 {
    322 
    323 	if (screen == NULL || screen->slk_window == NULL)
    324 		return ERR;
    325 	return wattroff(screen->slk_window, attr);
    326 }
    327 
    328 #ifdef HAVE_WCHAR
    329 /*
    330  * __slk_attr_off --
    331  *	Test and unset wide attributes on ripped off slk window.
    332  */
    333 static int
    334 __slk_attr_off(SCREEN *screen, const attr_t attr, void *opt)
    335 {
    336 
    337 	if (screen == NULL || screen->slk_window == NULL)
    338 		return ERR;
    339 	return wattr_off(screen->slk_window, attr, opt);
    340 }
    341 #endif	/* HAVE_WCHAR */
    342 
    343 /*
    344  * __slk_attrset --
    345  *	Set attributes and color pair on ripped off slk window.
    346  */
    347 static int
    348 __slk_attrset(SCREEN *screen, const chtype attr)
    349 {
    350 
    351 	if (screen == NULL || screen->slk_window == NULL)
    352 		return ERR;
    353 	return wattrset(screen->slk_window, attr);
    354 }
    355 
    356 #ifdef HAVE_WCHAR
    357 /*
    358  * __slk_attr_set --
    359  *	Set wide attributes and color pair on ripped off slk window.
    360  */
    361 static int
    362 __slk_attr_set(SCREEN *screen, const attr_t attr, short pair, void *opt)
    363 {
    364 
    365 	if (screen == NULL || screen->slk_window == NULL)
    366 		return ERR;
    367 	return wattr_set(screen->slk_window, attr, pair, opt);
    368 }
    369 #endif	/* HAVE_WCHAR */
    370 
    371 /*
    372  * __slk_clear --
    373  *	Clear slk from the current screen.
    374  */
    375 static int
    376 __slk_clear(SCREEN *screen)
    377 {
    378 
    379 	if (screen == NULL)
    380 		return ERR;
    381 	screen->slk_hidden = true;
    382 	if (screen->is_term_slk) {
    383 		if (t_label_off(screen->term) == NULL)
    384 			return ERR;
    385 		return ti_putp(screen->term,
    386 		    ti_tiparm(screen->term, t_label_off(screen->term)));
    387 	}
    388 	if (screen->slk_window == NULL)
    389 		return ERR;
    390 	werase(screen->slk_window);
    391 	return wrefresh(screen->slk_window);
    392 }
    393 
    394 /*
    395  * __slk_color --
    396  *	Set color pair on ripped off slk window.
    397  */
    398 static int
    399 __slk_color(SCREEN *screen, short pair)
    400 {
    401 
    402 	if (screen == NULL || screen->slk_window == NULL)
    403 		return ERR;
    404 	return wcolor_set(screen->slk_window, pair, NULL);
    405 }
    406 
    407 
    408 /*
    409  * __slk_label --
    410  *	Return a pointer to the saved label for key labnum.
    411  */
    412 static char *
    413 __slk_label(SCREEN *screen, int labnum)
    414 {
    415 
    416 	if (screen == NULL || labnum < 1 || labnum > screen->slk_nlabels)
    417 		return NULL;
    418 	return screen->slk_labels[--labnum].text;
    419 }
    420 
    421 /*
    422  * __slk_wnoutrefresh --
    423  *	Add the contents of the ripped off slk window to the virtual window.
    424  */
    425 int
    426 __slk_noutrefresh(SCREEN *screen)
    427 {
    428 
    429 	if (screen == NULL || screen->slk_window == NULL)
    430 		return ERR;
    431 	return wnoutrefresh(screen->slk_window);
    432 }
    433 
    434 /*
    435  * __slk_restore --
    436  *	Retore slk to the screen after a slk_clear.
    437  */
    438 static int
    439 __slk_restore(SCREEN *screen)
    440 {
    441 
    442 	if (screen == NULL)
    443 		return ERR;
    444 	screen->slk_hidden = false;
    445 	if (screen->is_term_slk) {
    446 		if (t_label_on(screen->term) == NULL)
    447 			return ERR;
    448 		return ti_putp(screen->term,
    449 		    ti_tiparm(screen->term, t_label_on(screen->term)));
    450 	}
    451 	if (screen->slk_window == NULL)
    452 		return ERR;
    453 	if (__slk_redraw(screen) == ERR)
    454 		return ERR;
    455 	return wrefresh(screen->slk_window);
    456 }
    457 
    458 /*
    459  * __slk_set --
    460  *	Sets the text of the label specified by labnum
    461  *	and how it is displayed.
    462  */
    463 static int
    464 __slk_set(SCREEN *screen, int labnum, const char *label, int justify)
    465 {
    466 	struct __slk_label *l;
    467 	const char *end;
    468 	size_t len;
    469 	char *text;
    470 #ifdef HAVE_WCHAR
    471 	wchar_t wc;
    472 	size_t wc_len;
    473 #endif
    474 
    475 	/* Check args. */
    476 	if (screen == NULL || labnum < 1 || labnum > screen->slk_nlabels)
    477 		return ERR;
    478 	switch(justify) {
    479 	case SLK_JUSTIFY_LEFT:
    480 	case SLK_JUSTIFY_CENTER:
    481 	case SLK_JUSTIFY_RIGHT:
    482 		break;
    483 	default:
    484 		return ERR;
    485 	}
    486 	if (label == NULL)
    487 		label = "";
    488 
    489 	/* Skip leading whitespace. */
    490 	while(isspace((unsigned char)*label))
    491 		label++;
    492 	/* Grab end. */
    493 	end = label;
    494 
    495 #ifdef HAVE_WCHAR
    496 	len = 0;
    497 	while (*end != '\0') {
    498 		if ((wc_len = mbrtowc(0, end, strlen(end), &screen->sp)) == -1)
    499 			return ERR;
    500 		mbrtowc(&wc, end, wc_len, &screen->sp);
    501 		if (!iswprint((wint_t)wc))
    502 			break;
    503 		len += wcwidth(wc);
    504 		end += wc_len;
    505 	}
    506 #else
    507 	while(isprint((unsigned char)*end))
    508 		end++;
    509 	len = end - label;
    510 #endif
    511 
    512 	/* Take a backup, in-case we can grow the label. */
    513 	if ((text = strndup(label, len)) == NULL)
    514 		return ERR;
    515 
    516 	/* All checks out, assign. */
    517 	l = &screen->slk_labels[--labnum]; /* internal zero based index */
    518 	l->text = text;
    519 	l->justify = justify;
    520 
    521 	__slk_set_finalise(screen, labnum);
    522 	return OK;
    523 }
    524 
    525 /*
    526  * __slk_touch --
    527  *	Sets the ripped off slk window as modified.
    528  */
    529 static int
    530 __slk_touch(SCREEN *screen)
    531 {
    532 
    533 	if (screen == NULL || screen->slk_window == NULL)
    534 		return ERR;
    535 	return touchwin(screen->slk_window);
    536 }
    537 
    538 
    539 #ifdef HAVE_WCHAR
    540 /*
    541  * __slk_wset --
    542  *	Sets the wide text of the label specified by labnum
    543  *	and how it is displayed.
    544  */
    545 static int
    546 __slk_wset(SCREEN *screen, int labnum, const wchar_t *label, int justify)
    547 {
    548 	const wchar_t *olabel;
    549 	size_t len;
    550 	char *str;
    551 	int result = ERR;
    552 
    553 	if (screen == NULL)
    554 		return ERR;
    555 	olabel = label;
    556 	if ((len = wcsrtombs(NULL, &olabel, 0, &screen->sp)) == -1)
    557 		return ERR;
    558 	len++; /* We need to store the NULL character. */
    559 	if ((str = malloc(len)) == NULL)
    560 		return ERR;
    561 	olabel = label;
    562 	if (wcsrtombs(str, &olabel, len, &screen->sp) == -1)
    563 		goto out;
    564 	result = __slk_set(screen, labnum, str, justify);
    565 out:
    566 	free(str);
    567 	return result;
    568 }
    569 #endif	/* HAVE_WCHAR */
    570 
    571 
    572 /*
    573  * __slk_init --
    574  *	Allocate structures.
    575  */
    576 int
    577 __slk_init(SCREEN *screen)
    578 {
    579 
    580 	__slk_free(screen);	/* safety */
    581 
    582 	screen->slk_format = slk_fmt;
    583 	if (slk_fmt == SLK_FMT_INVAL)
    584 		return OK;
    585 	slk_fmt = SLK_FMT_INVAL;
    586 
    587 	switch(screen->slk_format) {
    588 	case SLK_FMT_3_2_3:
    589 	case SLK_FMT_4_4:
    590 		screen->slk_nlabels = 8;
    591 		break;
    592 	default:	/* impossible */
    593 		return ERR;
    594 	}
    595 
    596 	screen->slk_labels = calloc(screen->slk_nlabels,
    597 				    sizeof(*screen->slk_labels));
    598 	if (screen->slk_labels == NULL)
    599 		return ERR;
    600 
    601 	screen->is_term_slk =
    602 	    t_plab_norm(screen->term) != NULL &&
    603 	    t_num_labels(screen->term) > 0;
    604 	if (screen->is_term_slk) {
    605 		__unripoffline(__slk_ripoffline);
    606 		screen->slk_nlabels = t_num_labels(screen->term);
    607 		screen->slk_label_len = t_label_width(screen->term);
    608 		/* XXX label_height, label_format? */
    609 	}
    610 
    611 	return OK;
    612 }
    613 
    614 /*
    615  * __slk_free --
    616  *	Free allocates resources.
    617  */
    618 void
    619 __slk_free(SCREEN *screen)
    620 {
    621 	int i;
    622 
    623 	if (screen->slk_window != NULL)
    624 		delwin(screen->slk_window);
    625 	for (i = 0; i < screen->slk_nlabels; i++)
    626 		free(screen->slk_labels[i].text);
    627 	free(screen->slk_labels);
    628 }
    629 
    630 /*
    631  * __slk_ripoffline --
    632  *	ripoffline callback to accept a WINDOW to create our keys.
    633  */
    634 static int
    635 __slk_ripoffline(WINDOW *window, int cols)
    636 {
    637 
    638 	if (window == NULL)
    639 		return ERR;
    640 	window->screen->slk_window = window;
    641 	wattron(window,
    642 	    (t_no_color_video(window->screen->term) & 1) == 0
    643 	    ? A_STANDOUT : A_REVERSE);
    644 	__slk_resize(window->screen, cols);
    645 	return OK;
    646 }
    647 
    648 /*
    649  * __slk_resize --
    650  *	Size and position the labels in the ripped off slk window.
    651  */
    652 int
    653 __slk_resize(SCREEN *screen, int cols)
    654 {
    655 	int x = 0;
    656 	struct __slk_label *l;
    657 
    658 	if (screen == NULL)
    659 		return ERR;
    660 	if (screen->is_term_slk || screen->slk_nlabels == 0)
    661 		return OK;
    662 
    663 	screen->slk_label_len = (cols / screen->slk_nlabels) - 1;
    664 	if (screen->slk_label_len > SLK_SIZE)
    665 		screen->slk_label_len = SLK_SIZE;
    666 
    667 	l = screen->slk_labels;
    668 
    669 	switch(screen->slk_format) {
    670 	case SLK_FMT_3_2_3:
    671 		/* Left 3 */
    672 		(l++)->x = x;
    673 		(l++)->x = (x += screen->slk_label_len + 1);
    674 		(l++)->x = (x += screen->slk_label_len + 1);
    675 
    676 		/* Middle 2 */
    677 		x = cols / 2;
    678 		(l++)->x = x -(screen->slk_label_len + 1);
    679 		(l++)->x = x + 1;
    680 
    681 		/* Right 3 */
    682 		x = (cols - ((screen->slk_label_len + 1) * 3)) + 1;
    683 		(l++)->x = x;
    684 		(l++)->x = (x += screen->slk_label_len + 1);
    685 		(l++)->x = (x += screen->slk_label_len + 1);
    686 		break;
    687 
    688 	case SLK_FMT_4_4:
    689 	{
    690 		int i, half;
    691 
    692 		half = screen->slk_nlabels / 2;
    693 		for (i = 0; i < screen->slk_nlabels; i++) {
    694 			(l++)->x = x;
    695 			x += screen->slk_label_len;
    696 			/* Split labels in half */
    697 			if (i == half - 1)
    698 				x = cols - (screen->slk_label_len * half) + 1;
    699 		}
    700 		break;
    701 	}
    702 	}
    703 
    704 	/* Write text to the labels. */
    705 	for (x = 0; x < screen->slk_nlabels; x++)
    706 		__slk_set_finalise(screen, x);
    707 
    708 	return __slk_redraw(screen);
    709 }
    710 
    711 /*
    712  * __slk_set_finalise --
    713  *	Does the grunt work of positioning and sizing the text in the label.
    714  */
    715 static int
    716 __slk_set_finalise(SCREEN *screen, int labnum)
    717 {
    718 	struct __slk_label *l;
    719 	size_t spc, len, x;
    720 	char *p;
    721 
    722 	l = &screen->slk_labels[labnum];
    723 	spc = screen->slk_label_len;
    724 
    725 #ifdef HAVE_WCHAR
    726 	len = 0;
    727 	if (l->text != NULL) {
    728 		wchar_t wc;
    729 
    730 		p = l->text;
    731 		while (*p != '\0') {
    732 			if ((x = mbrtowc(0, p, strlen(p), &screen->sp)) == -1)
    733 				return ERR;
    734 			mbrtowc(&wc, p, x, &screen->sp);
    735 			if (len + wcwidth(wc) > spc)
    736 				break;
    737 			len += wcwidth(wc);
    738 			p += x;
    739 		}
    740 	}
    741 #else
    742 	len = l->text == NULL ? 0 : strlen(l->text);
    743 	if (len > spc)
    744 		len = spc;
    745 #endif
    746 
    747 	switch(l->justify) {
    748 	case SLK_JUSTIFY_LEFT:
    749 		x = 0;
    750 		break;
    751 	case SLK_JUSTIFY_CENTER:
    752 		x = (spc - len) / 2;
    753 		if (x + len > spc)
    754 			x--;
    755 		break;
    756 	case SLK_JUSTIFY_RIGHT:
    757 		x = spc - len;
    758 		break;
    759 	default:
    760 		return ERR; /* impossible */
    761 	}
    762 
    763 	p = l->label;
    764 	if (x != 0) {
    765 		memset(p, ' ', x);
    766 		p += x;
    767 		spc -= x;
    768 	}
    769 	if (len != 0) {
    770 		memcpy(p, l->text, len);
    771 		p += len;
    772 		spc -= len;
    773 	}
    774 	if (spc != 0) {
    775 		memset(p, ' ', spc);
    776 		p += spc;
    777 	}
    778 	*p = '\0'; /* Terminate for plab_norm. */
    779 
    780 	return __slk_draw(screen, labnum);
    781 }
    782 
    783 /*
    784  * __slk_draw --
    785  *	Draws the specified key.
    786  */
    787 static int
    788 __slk_draw(SCREEN *screen, int labnum)
    789 {
    790 	const struct __slk_label *l;
    791 
    792 	if (screen->slk_hidden)
    793 		return OK;
    794 
    795 	l = &screen->slk_labels[labnum];
    796 	if (screen->is_term_slk)
    797 		return ti_putp(screen->term,
    798 		    ti_tiparm(screen->term,
    799 		    t_plab_norm(screen->term), labnum + 1, l->label));
    800 	else if (screen->slk_window != NULL)
    801 		return mvwaddnstr(screen->slk_window, 0, l->x,
    802 		    l->label, screen->slk_label_len);
    803 	else
    804 		return ERR;
    805 }
    806 
    807 /*
    808  * __slk_draw --
    809  *	Draws all the keys.
    810  */
    811 static int
    812 __slk_redraw(SCREEN *screen)
    813 {
    814 	int i, result = OK;
    815 
    816 	for (i = 0; i < screen->slk_nlabels; i++) {
    817 		if (__slk_draw(screen, i) == ERR)
    818 			result = ERR;
    819 	}
    820 	return result;
    821 }
    822