1 /* $NetBSD: slk.c,v 1.4 2018/10/04 19:11:09 roy 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.4 2018/10/04 19:11:09 roy 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