1 /* $NetBSD: slk.c,v 1.2 2017/01/30 17:15:52 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.2 2017/01/30 17:15:52 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; /* 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