menu_sys.def revision 1.54
1/* $NetBSD: menu_sys.def,v 1.54 2004/08/02 21:29:07 dsl Exp $ */ 2 3/* 4 * Copyright 1997 Piermont Information Systems Inc. 5 * All rights reserved. 6 * 7 * Written by Philip A. Nelson for Piermont Information Systems Inc. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software develooped for the NetBSD Project by 20 * Piermont Information Systems Inc. 21 * 4. The name of Piermont Information Systems Inc. may not be used to endorse 22 * or promote products derived from this software without specific prior 23 * written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY PIERMONT INFORMATION SYSTEMS INC. ``AS IS'' 26 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28 * ARE DISCLAIMED. IN NO EVENT SHALL PIERMONT INFORMATION SYSTEMS INC. BE 29 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 35 * THE POSSIBILITY OF SUCH DAMAGE. 36 * 37 */ 38 39/* menu_sys.defs -- Menu system standard routines. */ 40 41#include <string.h> 42#include <ctype.h> 43 44#define REQ_EXECUTE 1000 45#define REQ_NEXT_ITEM 1001 46#define REQ_PREV_ITEM 1002 47#define REQ_REDISPLAY 1003 48#define REQ_SCROLLDOWN 1004 49#define REQ_SCROLLUP 1005 50#define REQ_HELP 1006 51 52/* Macros */ 53#define MAX(x,y) ((x)>(y)?(x):(y)) 54#define MIN(x,y) ((x)<(y)?(x):(y)) 55 56/* Initialization state. */ 57static int __menu_init = 0; 58static int max_lines = 0, max_cols = 0; 59#ifndef scrolltext 60static const char *scrolltext = " <: page up, >: page down"; 61#endif 62 63#ifdef DYNAMIC_MENUS 64static int num_menus = 0; 65#define DYN_INIT_NUM 32 66static menudesc **menu_list; 67#define MENUS(n) (*(menu_list[n])) 68#else 69#define MENUS(n) (menu_def[n]) 70#endif 71 72/* prototypes for in here! */ 73static void init_menu(menudesc *m); 74static char opt_ch(menudesc *m, int op_no); 75static void draw_menu(menudesc *m, void *arg); 76static void process_help(menudesc *m); 77static void process_req(menudesc *m, void *arg, int req); 78static int menucmd(WINDOW *w); 79 80#ifndef NULL 81#define NULL 0 82#endif 83 84/* menu system processing routines */ 85#define mbeep() (void)fputc('\a', stderr) 86 87static int 88menucmd(WINDOW *w) 89{ 90 int ch; 91 92 while (TRUE) { 93 ch = wgetch(w); 94 95 switch (ch) { 96 case '\n': 97 return REQ_EXECUTE; 98 case '\016': /* Control-P */ 99 case KEY_DOWN: 100 return REQ_NEXT_ITEM; 101 case '\020': /* Control-N */ 102 case KEY_UP: 103 return REQ_PREV_ITEM; 104 case '\014': /* Control-L */ 105 return REQ_REDISPLAY; 106 case '<': 107 case '\010': /* Control-H (backspace) */ 108 case KEY_PPAGE: 109 case KEY_LEFT: 110 return REQ_SCROLLUP; 111 case '\026': /* Control-V */ 112 case '>': 113 case ' ': 114 case KEY_NPAGE: 115 case KEY_RIGHT: 116 return REQ_SCROLLDOWN; 117 case '?': 118 return REQ_HELP; 119 case '\033': /* esc-v is scroll down */ 120 ch = wgetch(w); 121 if (ch == 'v') 122 return REQ_SCROLLUP; 123 else 124 ch = 0; /* zap char so we beep */ 125 } 126 127 if (isalpha(ch)) 128 return ch; 129 130 mbeep(); 131 wrefresh(w); 132 } 133} 134 135WINDOW *sv_mw; 136static void 137init_menu(menudesc *m) 138{ 139 int wmax; 140 int hadd, wadd, exithadd; 141 int i; 142 int x, y, w; 143 const char *title, *tp, *ep; 144 145 x = m->x; 146 y = m->y; 147 w = m->w; 148 wmax = 0; 149 hadd = ((m->mopt & MC_NOBOX) ? 0 : 2); 150 wadd = ((m->mopt & MC_NOBOX) ? 2 : 4); 151 152 if (m->title && *(title = MSG_XLAT(m->title)) != 0) { 153 /* Allow multiple line titles */ 154 for (tp = title; ep = strchr(tp, '\n'); tp = ep + 1) { 155 i = ep - tp; 156 wmax = MAX(wmax, i); 157 hadd++; 158 } 159 hadd++; 160 i = strlen(tp); 161 wmax = MAX(wmax, i); 162 if (i != 0) 163 hadd++; 164 } else { 165 m->title = NULL; 166 title = "untitled"; 167 } 168 exithadd = ((m->mopt & MC_NOEXITOPT) ? 0 : 1); 169 170#ifdef MSG_DEFS_H 171 if (y < 0) { 172 /* put menu box below message text */ 173 y = -y; 174 i = msg_row(); 175 if (i > y) 176 y = i; 177 } 178#endif 179 180 /* Calculate h? h == number of visible options. */ 181 if (m->h == 0) 182 m->h = m->numopts + exithadd; 183 184 if (m->h > max_lines - y - hadd) { 185 /* Not enough space for all the options */ 186 if (m->h <= 4 || !(m->mopt & (MC_SCROLL | MC_ALWAYS_SCROLL))) { 187 /* move menu up screen */ 188 y = max_lines - hadd - m->h; 189 if (y < 0) 190 y = 0; 191 } 192 m->h = max_lines - y - hadd; 193 } 194 195 if (m->h < m->numopts + exithadd || m->mopt & MC_ALWAYS_SCROLL) { 196 /* We need to add the scroll text... 197 * The used to be a check for MC_SCROLL here, but it is 198 * fairly pointless - you just don't want the program 199 * to exit on this sort of error. 200 */ 201 if (m->h < 3) { 202 endwin(); 203 (void)fprintf(stderr, 204 "Window too short (m->h %d, m->numopts %d, exithadd %d, y %d, max_lines %d, hadd %d) for menu \"%.30s\"\n", 205 m->h, m->numopts, exithadd, y, max_lines, hadd, 206 title); 207 exit(1); 208 } 209 hadd++; 210 m->h = MIN(m->h, max_lines - y - hadd); 211 i = strlen(scrolltext); 212 wmax = MAX(wmax, i); 213 } 214 215 /* Calculate w? */ 216 if (w == 0) { 217 int l; 218 for (i = 0; i < m->numopts; i++) { 219 l = strlen(MSG_XLAT(m->opts[i].opt_name)); 220 if (!(m->mopt & MC_NOSHORTCUT)) 221 l += 3; 222 wmax = MAX(wmax, l); 223 } 224 w = wmax; 225 } 226 227 /* check and adjust for screen fit */ 228 if (w + wadd > max_cols) { 229 endwin(); 230 (void)fprintf(stderr, 231 "Screen too narrow (%d + %d > %d) for menu \"%s\"\n", 232 w, wadd, max_cols, title); 233 exit(1); 234 235 } 236 237 if (x == -1) 238 x = (max_cols - (w + wadd)) / 2; /* center */ 239 else if (x + w + wadd > max_cols) 240 x = max_cols - (w + wadd); /* right align */ 241 242 if (y == 0) { 243 /* Center - rather than top */ 244 y = (max_lines - hadd - m->h) / 2; 245 } 246 247 /* Get the windows. */ 248 m->mw = newwin(m->h + hadd, w + wadd, y, x); 249 250 if (m->mw == NULL) { 251 endwin(); 252 (void)fprintf(stderr, 253 "Could not create window (%d + %d, %d + %d, %d, %d) for menu \"%s\"\n", 254 m->h, hadd, w, wadd, y, x, title); 255 exit(1); 256 } 257 keypad(m->mw, TRUE); /* enable multi-key assembling for win */ 258 259 /* XXX is it even worth doing this right? */ 260 if (has_colors()) { 261 wbkgd(m->mw, COLOR_PAIR(1)); 262 wattrset(m->mw, COLOR_PAIR(1)); 263 } 264 265 if (m->mopt & MC_SUBMENU) { 266 /* Keep a copy of what is on the screen under the window */ 267 sv_mw = m->sv_mw = newwin(m->h + hadd, w + wadd, y, x); 268 if (m->sv_mw) 269 overwrite(curscr, m->sv_mw); 270 } 271} 272 273static char 274opt_ch(menudesc *m, int op_no) 275{ 276 char c; 277 278 if (op_no == m->numopts) 279 return 'x'; 280 281 if (op_no < 25) { 282 c = 'a' + op_no; 283 if (c >= 'x') 284 c++; 285 } else 286 c = 'A' + op_no - 25; 287 return c; 288} 289 290static void 291draw_menu_line(menudesc *m, int opt, int cury, void *arg, const char *text) 292{ 293 int hasbox = m->mopt & MC_NOBOX ? 0 : 1; 294 295 if (m->cursel == opt) { 296 mvwaddstr(m->mw, cury, hasbox, ">"); 297 wstandout(m->mw); 298 } else 299 mvwaddstr(m->mw, cury, hasbox, " "); 300 if (!(m->mopt & MC_NOSHORTCUT)) 301 wprintw(m->mw, "%c: ", opt_ch(m, opt)); 302 303 if (!text && m->draw_line) 304 m->draw_line(m, opt, arg); 305 else 306 waddstr(m->mw, MSG_XLAT(text)); 307 if (m->cursel == opt) 308 wstandend(m->mw); 309} 310 311static void 312draw_menu(menudesc *m, void *arg) 313{ 314 int opt; 315 int hasbox, cury, maxy; 316 int tadd; 317 int hasexit = (m->mopt & MC_NOEXITOPT ? 0 : 1); 318 const char *tp, *ep; 319 320 hasbox = (m->mopt & MC_NOBOX ? 0 : 1); 321 322 /* Clear the window */ 323 wclear(m->mw); 324 325 tadd = hasbox; 326 if (m->title) { 327 for (tp = MSG_XLAT(m->title); ; tp = ep + 1) { 328 ep = strchr(tp , '\n'); 329 mvwaddnstr(m->mw, tadd++, hasbox + 1, tp, 330 ep ? ep - tp : -1); 331 if (ep == NULL || *ep == 0) 332 break; 333 } 334 tadd++; 335 } 336 337 cury = tadd; 338 maxy = getmaxy(m->mw) - hasbox; 339 if (m->numopts + hasexit > m->h) 340 /* allow for scroll text */ 341 maxy--; 342 343 if (m->cursel == -1) { 344 m->cursel = m->numopts; 345 if (m->h <= m->numopts) 346 m->topline = m->numopts + 1 - m->h; 347 } 348 349 while (m->cursel >= m->topline + m->h) 350 m->topline = MIN(m->topline + m->h, 351 m->numopts + hasexit - m->h); 352 while (m->cursel < m->topline) 353 m->topline = MAX(0, m->topline - m->h); 354 355 for (opt = m->topline; opt < m->numopts; opt++) { 356 if (cury >= maxy) 357 break; 358 draw_menu_line(m, opt, cury++, arg, m->opts[opt].opt_name); 359 } 360 361 /* Add the exit option. */ 362 if (!(m->mopt & MC_NOEXITOPT)) { 363 if (cury < maxy) 364 draw_menu_line(m, m->numopts, cury++, arg, m->exitstr); 365 else 366 opt = 0; 367 } 368 369 /* Add the scroll line */ 370 if (opt != m->numopts || m->topline != 0) 371 mvwaddstr(m->mw, cury, hasbox, scrolltext); 372 373 /* Add the box. */ 374 if (!(m->mopt & MC_NOBOX)) 375 box(m->mw, 0, 0); 376 377 wmove(m->mw, tadd + m->cursel - m->topline, hasbox); 378 wrefresh(m->mw); 379} 380 381 382static void 383/*ARGSUSED*/ 384process_help(menudesc *m) 385{ 386 const char *help = m->helpstr; 387 WINDOW *sv_curscr; 388 int lineoff = 0; 389 int curoff = 0; 390 int again; 391 int winin; 392 393 /* Is there help? */ 394 if (!help) { 395 mbeep(); 396 return; 397 } 398 sv_curscr = newwin(getmaxy(curscr), getmaxx(curscr), 0, 0); 399 if (!sv_curscr) { 400 mbeep(); 401 return; 402 } 403 /* Save screen contents so we can restore before returning */ 404 overwrite(curscr, sv_curscr); 405 touchwin(stdscr); 406 407 help = MSG_XLAT(help); 408 /* Display the help information. */ 409 do { 410 if (lineoff < curoff) { 411 help = MSG_XLAT(m->helpstr); 412 curoff = 0; 413 } 414 while (*help && curoff < lineoff) { 415 if (*help == '\n') 416 curoff++; 417 help++; 418 } 419 420 wclear(stdscr); 421 mvwaddstr(stdscr, 0, 0, 422 "Help: exit: x, page up: u <, page down: d >"); 423 mvwaddstr(stdscr, 2, 0, help); 424 wmove(stdscr, 1, 0); 425 wrefresh(stdscr); 426 427 do { 428 winin = wgetch(stdscr); 429 if (winin < KEY_MIN) 430 winin = tolower(winin); 431 again = 0; 432 switch (winin) { 433 case '<': 434 case 'u': 435 case KEY_UP: 436 case KEY_LEFT: 437 case KEY_PPAGE: 438 if (lineoff) 439 lineoff -= max_lines - 2; 440 else 441 again = 1; 442 break; 443 case '>': 444 case 'd': 445 case KEY_DOWN: 446 case KEY_RIGHT: 447 case KEY_NPAGE: 448 if (*help) 449 lineoff += max_lines - 2; 450 else 451 again = 1; 452 break; 453 case 'q': 454 break; 455 case 'x': 456 winin = 'q'; 457 break; 458 default: 459 again = 1; 460 } 461 if (again) 462 mbeep(); 463 } while (again); 464 } while (winin != 'q'); 465 466 /* Restore the original screen contents */ 467 touchwin(sv_curscr); 468 wnoutrefresh(sv_curscr); 469 delwin(sv_curscr); 470 471 /* Some code thinks that wrefresh(stdout) is a good idea... */ 472 wclear(stdscr); 473} 474 475static void 476process_req(menudesc *m, void *arg, int req) 477{ 478 int ch; 479 int hasexit = (m->mopt & MC_NOEXITOPT ? 0 : 1); 480 481 switch(req) { 482 483 case REQ_EXECUTE: 484 return; 485 486 case REQ_NEXT_ITEM: 487 ch = m->cursel; 488 for (;;) { 489 ch++; 490 if (ch >= m->numopts + hasexit) { 491 mbeep(); 492 return; 493 } 494 if (hasexit && ch == m->numopts) 495 break; 496 if (!(m->opts[ch].opt_flags & OPT_IGNORE)) 497 break; 498 } 499 m->cursel = ch; 500 if (m->cursel >= m->topline + m->h) 501 m->topline = m->cursel - m->h + 1; 502 break; 503 504 case REQ_PREV_ITEM: 505 ch = m->cursel; 506 for (;;) { 507 if (ch <= 0) { 508 mbeep(); 509 return; 510 } 511 ch--; 512 if (!(m->opts[ch].opt_flags & OPT_IGNORE)) 513 break; 514 } 515 m->cursel = ch; 516 if (m->cursel < m->topline) 517 m->topline = m->cursel; 518 break; 519 520 case REQ_HELP: 521 process_help(m); 522 break; 523 524 case REQ_REDISPLAY: 525 endwin(); 526 doupdate(); 527 break; 528 529 case REQ_SCROLLUP: 530 if (m->cursel == 0) { 531 mbeep(); 532 return; 533 } 534 m->topline = MAX(0, m->topline - m->h); 535 m->cursel = MAX(0, m->cursel - m->h); 536 wclear(m->mw); 537 break; 538 539 case REQ_SCROLLDOWN: 540 if (m->cursel >= m->numopts + hasexit - 1) { 541 mbeep(); 542 return; 543 } 544 m->topline = MIN(m->topline + m->h, 545 MAX(m->numopts + hasexit - m->h, 0)); 546 m->cursel = MIN(m->numopts + hasexit - 1, m->cursel + m->h); 547 wclear(m->mw); 548 break; 549 550 default: 551 ch = req; 552 if (ch == 'x' && hasexit) { 553 m->cursel = m->numopts; 554 break; 555 } 556 if (m->mopt & MC_NOSHORTCUT) { 557 mbeep(); 558 return; 559 } 560 if (ch > 'z') 561 ch = 255; 562 if (ch >= 'a') { 563 if (ch > 'x') 564 ch--; 565 ch = ch - 'a'; 566 } else 567 ch = 25 + ch - 'A'; 568 if (ch < 0 || ch >= m->numopts) { 569 mbeep(); 570 return; 571 } 572 if (m->opts[ch].opt_flags & OPT_IGNORE) { 573 mbeep(); 574 return; 575 } 576 m->cursel = ch; 577 } 578 579 draw_menu(m, arg); 580} 581 582int 583menu_init(void) 584{ 585 int i; 586 587 if (__menu_init) 588 return 0; 589 590#ifdef USER_MENU_INIT 591 if (USER_MENU_INIT) 592 return 1; 593#endif 594 595 if (initscr() == NULL) 596 return 1; 597 598 cbreak(); 599 noecho(); 600 601 /* XXX Should be configurable but it almost isn't worth it. */ 602 if (has_colors()) { 603 start_color(); 604 init_pair(1, COLOR_WHITE, COLOR_BLUE); 605 bkgd(COLOR_PAIR(1)); 606 attrset(COLOR_PAIR(1)); 607 } 608 609 max_lines = getmaxy(stdscr); 610 max_cols = getmaxx(stdscr); 611 keypad(stdscr, TRUE); 612#ifdef DYNAMIC_MENUS 613 num_menus = DYN_INIT_NUM; 614 while (num_menus < DYN_MENU_START) 615 num_menus *= 2; 616 menu_list = malloc(sizeof *menu_list * num_menus); 617 if (menu_list == NULL) 618 return 2; 619 (void)memset(menu_list, 0, sizeof *menu_list * num_menus); 620 for (i = 0; i < DYN_MENU_START; i++) 621 menu_list[i] = &menu_def[i]; 622#endif 623 624 __menu_init = 1; 625 return 0; 626} 627 628void 629process_menu(int num, void *arg) 630{ 631 int sel = 0; 632 int req; 633 menu_ent *opt; 634 635 menudesc *m; 636 637 m = &MENUS(num); 638 639 /* Initialize? */ 640 if (menu_init()) { 641 __menu_initerror(); 642 return; 643 } 644 645 /* Default to select option 0 and display from 0 */ 646 m->topline = 0; 647 if ((m->mopt & (MC_DFLTEXIT | MC_NOEXITOPT)) == MC_DFLTEXIT) 648 m->cursel = -1; 649 else 650 m->cursel = 0; 651 652 for (;;) { 653 if (isendwin()) 654 /* I'm not sure this is needed with netbsd's curses */ 655 doupdate(); 656 /* Process the display action */ 657 if (m->post_act) 658 (*m->post_act)(m, arg); 659 if (m->mw == NULL) 660 init_menu(m); 661 draw_menu(m, arg); 662 663 while ((req = menucmd(m->mw)) != REQ_EXECUTE) 664 process_req(m, arg, req); 665 666 sel = m->cursel; 667 if (!(m->mopt & MC_NOCLEAR)) { 668 wclear(m->mw); 669 if (m->sv_mw) 670 overwrite(m->sv_mw, m->mw); 671 wnoutrefresh(m->mw); 672 } 673 674 /* Process the items */ 675 if (sel >= m->numopts) 676 /* exit option */ 677 break; 678 679 opt = &m->opts[sel]; 680 if (opt->opt_flags & OPT_IGNORE) 681 continue; 682 if (opt->opt_flags & OPT_ENDWIN) 683 endwin(); 684 if (opt->opt_action && (*opt->opt_action)(m, arg)) 685 break; 686 687 if (opt->opt_menu != -1) { 688 if (!(opt->opt_flags & OPT_SUB)) { 689 num = opt->opt_menu; 690 wclear(m->mw); 691 if (m->sv_mw) { 692 overwrite(m->sv_mw, m->mw); 693 delwin(m->sv_mw); 694 m->sv_mw = NULL; 695 } 696 wnoutrefresh(m->mw); 697 delwin(m->mw); 698 m->mw = NULL; 699 m = &MENUS(num); 700 continue; 701 } 702 process_menu(opt->opt_menu, arg); 703 } 704 if (opt->opt_flags & OPT_EXIT) 705 break; 706 } 707 708 if (m->mopt & MC_NOCLEAR) { 709 wclear(m->mw); 710 if (m->sv_mw) 711 overwrite(m->sv_mw, m->mw); 712 wnoutrefresh(m->mw); 713 } 714 715 /* Process the exit action */ 716 if (m->exit_act) 717 (*m->exit_act)(m, arg); 718 delwin(m->mw); 719 m->mw = NULL; 720 if (m->sv_mw) { 721 delwin(m->sv_mw); 722 m->sv_mw = NULL; 723 } 724} 725 726 727void 728set_menu_numopts(int menu, int numopts) 729{ 730 731 MENUS(menu).numopts = numopts; 732} 733 734/* Control L is end of standard routines, remaining only for dynamic. */ 735 736/* Beginning of routines for dynamic menus. */ 737 738static int 739double_menus(void) 740{ 741 menudesc **temp; 742 int sz = sizeof *menu_list * num_menus; 743 744 temp = realloc(menu_list, sz * 2); 745 if (temp == NULL) 746 return 0; 747 (void)memset(temp + num_menus, 0, sz); 748 menu_list = temp; 749 num_menus *= 2; 750 751 return 1; 752} 753 754int 755new_menu(const char *title, menu_ent *opts, int numopts, 756 int x, int y, int h, int w, int mopt, 757 void (*post_act)(menudesc *, void *), 758 void (*draw_line)(menudesc *, int, void *), 759 void (*exit_act)(menudesc *, void *), 760 const char *help, const char *exit_str) 761{ 762 int ix; 763 menudesc *m; 764 765 /* Find free menu entry. */ 766 for (ix = DYN_MENU_START; ; ix++) { 767 if (ix >= num_menus && !double_menus()) 768 return -1; 769 m = menu_list[ix]; 770 if (m == NULL) { 771 m = calloc(sizeof *m, 1); 772 if (m == NULL) 773 return -1; 774 menu_list[ix] = m; 775 break; 776 } 777 if (!(m->mopt & MC_VALID)) 778 break; 779 } 780 781 /* Set Entries */ 782 m->title = title; 783 m->opts = opts; 784 m->numopts = numopts; 785 m->x = x; 786 m->y = y; 787 m->h = h; 788 m->w = w; 789 m->mopt = mopt | MC_VALID; 790 m->post_act = post_act; 791 m->draw_line = draw_line; 792 m->exit_act = exit_act; 793 m->helpstr = help; 794 m->exitstr = exit_str ? exit_str : "Exit"; 795 796 return ix; 797} 798 799void 800free_menu(int menu_no) 801{ 802 menudesc *m; 803 804 if (menu_no < 0 || menu_no >= num_menus) 805 return; 806 807 m = menu_list[menu_no]; 808 if (!(m->mopt & MC_VALID)) 809 return; 810 if (m->mw != NULL) 811 delwin(m->mw); 812 memset(m, 0, sizeof *m); 813} 814