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