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