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