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