diff options
Diffstat (limited to 'lib/libforms/ncurses.c')
-rw-r--r-- | lib/libforms/ncurses.c | 707 |
1 files changed, 707 insertions, 0 deletions
diff --git a/lib/libforms/ncurses.c b/lib/libforms/ncurses.c new file mode 100644 index 0000000..5db4dd1 --- /dev/null +++ b/lib/libforms/ncurses.c @@ -0,0 +1,707 @@ +/* + * Copyright (c) 1995 + * Paul Richards. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer, + * verbatim and that no modifications are made prior to this + * point in the file. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Paul Richards. + * 4. The name Paul Richards may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY PAUL RICHARDS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PAUL RICHARDS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +/* + * Low level ncurses support routines. + */ + +#include <forms.h> +#include <ncurses.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ttyent.h> + +#include "internal.h" + +/* ncurses key mappings */ +/* XXX -- need to look at implementing key mapping properly */ +#define K_UPL KEY_UP +#define K_DOWNL KEY_DOWN +#define K_RIGHTL 9 +#define K_LEFTL 8 +#define K_NEXTL 10 +#define K_LEFT KEY_LEFT +#define K_RIGHT KEY_RIGHT +#define K_HOME KEY_HOME +#define K_END KEY_END +#define K_BS 263 +#define K_DEL 330 +#define K_ACCEPT K_NEXTL + +/* Function declarations */ +DISPLAY *ncurses_open(DISPLAY *); +void ncurses_moveto(OBJECT *); +int ncurses_print_srtring(OBJECT *, char *); + +extern OBJECT *cur_obj; + +DISPLAY * +ncurses_open(DISPLAY *display) +{ + struct ttyent *tty; + NCURSDEV *device = display->device.ncurses; + FILE *in, *out; + + if (device) { + tty = getttynam(device->ttyname); + if (!tty) + return (0); + + in = fopen(device->input, "r"); + out = fopen(device->output, "w+"); + if ((!in) || (!out)) + return (0); + device->screen = newterm(tty->ty_type, in, out); + } else { + display->device.ncurses = malloc(sizeof (NCURSDEV)); + device = display->device.ncurses; + if (!device) + return (0); + device->screen = newterm(getenv("TERM"), stdout, stdin); + } + + if (!device->screen) + return (0); + + start_color(); + cbreak(); + noecho(); + + /* If we got here by default, set device type to ncurses */ + if (display->type == DT_ANY) + display->type = DT_NCURSES; + + + display->height = LINES; + display->width = COLS; + + return (display); +} + +ncurses_set_display(DISPLAY *display) +{ + set_term(display->device.ncurses->screen); +} + +ncurses_open_window(OBJECT *object) +{ + object->window.ncurses->win = + newwin(object->height, object->width, object->y, object->x); + if (!object->window.ncurses->win) + errx(-1, "Couldn't open window (%d)", lineno); + if (keypad(object->window.ncurses->win, TRUE) == ERR) + errx(-1, "Keypad call failed (%d)", lineno); +} + +void +ncurses_refresh_display(DISPLAY *display) +{ + doupdate(); +} + +/* + * Parse an attribute string. For ncurses we look up the display + * specific bindings table for the attribute strings. For ncurses + * the attribute entry is a simple integer which is passed to wattron. + */ + +int +ncurses_parse_attrs(OBJECT *object, char *string) +{ + hash_table *htable; + TUPLE *tuple; + int inc = 1; + char *attribute = 0; + AttrType attr_type; + int len, y, x; + int skip = 0; + + if ((!string) || (*string != '\\')) + return (0); + + do { + if (*(string + inc) == '\\') + return (skip); + + while ((!isspace(*(string + inc))) + && (*(string+inc) != '\\') + && (*(string + inc) != '\0')) + inc++; + + attribute = malloc(inc); + if (!attribute) + errx(-1, "Failed to allocate memory for attribute when parsing string"); + strncpy(attribute, string+1, inc-1); + attribute[inc-1] = 0; + +#ifdef no + /* Skip trailing space after the attribute string */ + while (isspace(*(string + inc))) + inc++; +#endif + + attr_type = parse_default_attributes(attribute); + free(attribute); + + switch (attr_type) { + case ATTR_CENTER: + len = calc_string_width(string+inc); + getyx(object->window.ncurses->win, y, x); + wmove(object->window.ncurses->win, y, + (object->width - x - len)/2); + break; + case ATTR_RIGHT: + len = calc_string_width(string+inc); + getyx(object->window.ncurses->win, y, x); + wmove(object->window.ncurses->win, y, COLS-len); + break; + case ATTR_UNKNOWN: + default: + /* + * If no bindings table is found just skip over the attribute + * string. i.e. ignore the attribute but keep printing the text. + */ + if (object->display && object->display->bind) { + tuple = get_tuple(object->display->bind, attribute, TT_ATTR); + if (tuple) + wattron(object->window.ncurses->win, COLOR_PAIR((int)*(tuple->addr))); + } + break; + } + + + skip += inc; + string += inc; + inc = 0; + } while (*(string + inc++) == '\\'); + + return (skip); +} + +int +ncurses_print_string(OBJECT *object, char *string) +{ + int len, skip; + int y = object->y; + int x; + int height = object->height; + + /* If it's a null string, clear the area using spaces. */ + if (!string) + len = -1; + else + len = strlen(string); + + wmove(object->window.ncurses->win, y, x); + + while (height--) { + x = object->x; + if (wmove(object->window.ncurses->win, y++, x) == ERR) + return (ERR); + while (x++ < (object->x + object->width)) { + if (len-- > 0) { + /* Print input objects without processing \'s */ + if ((*string == '\\') && (object->type != OT_INPUT)) { + skip = ncurses_parse_attrs(object, string); + len -= skip; + string += skip; + } + if (waddch(object->window.ncurses->win, *string++) == ERR) + return (ERR); + } else if (waddch(object->window.ncurses->win, ' ') == ERR) + return (ERR); + } + } + return (OK); +} + +void +ncurses_display_object(OBJECT *object) +{ + ncurses_set_attributes(object, 0); + + switch (object->type) { + case OT_ACTION: + ncurses_display_action(object); + break; + case OT_COMPOUND: + ncurses_display_compound(object); + break; + case OT_FUNCTION: + ncurses_display_function(object); + break; + case OT_INPUT: + ncurses_display_input(object); + break; + case OT_MENU: + ncurses_display_menu(object); + break; + case OT_TEXT: + ncurses_display_text(object); + break; + default: + break; + } +} + + +void +ncurses_process_object(OBJECT *object) +{ + ncurses_set_attributes(object, 1); + + switch (object->type) { + case OT_ACTION: + ncurses_process_action(object); + break; + case OT_FUNCTION: + break; + case OT_INPUT: + ncurses_process_input(object); + break; + case OT_MENU: + ncurses_process_menu(object); + break; + case OT_TEXT: + break; + default: + break; + } +} + +void +ncurses_draw_box(OBJECT *object) +{ + int y, x; + chtype box, border; + + ncurses_parse_attrs(object, object->highlight); + wattron(object->window.ncurses->win, A_BOLD); + + mvwaddch(object->window.ncurses->win, object->y, object->x, + ACS_ULCORNER); + + mvwaddch(object->window.ncurses->win, + object->y + object->height - 1, + object->x, + ACS_LLCORNER); + + for (y=object->y + 1; y < (object->y + object->height) - 1; y++) { + mvwaddch(object->window.ncurses->win, y, object->x, + ACS_VLINE); + } + for (x=object->x + 1; x < (object->x + object->width) - 1; x++) { + mvwaddch(object->window.ncurses->win, object->y, x, + ACS_HLINE); + } + + ncurses_parse_attrs(object, object->attributes); + wattroff(object->window.ncurses->win, A_BOLD); + + mvwaddch(object->window.ncurses->win, object->y, + object->x + object->width-1, + ACS_URCORNER); + + mvwaddch(object->window.ncurses->win, + object->y + object->height-1, + object->x + object->width-1, + ACS_LRCORNER); + + for (y=object->y + 1; y < (object->y + object->height) - 1; y++) { + mvwaddch(object->window.ncurses->win, y, object->x + object->width - 1, + ACS_VLINE); + } + for (x=object->x + 1; x < (object->x + object->width) - 1; x++) { + mvwaddch(object->window.ncurses->win, object->y + object->height - 1, x, + ACS_HLINE); + } + + wnoutrefresh(object->window.ncurses->win); +} + +void +ncurses_draw_shadow(OBJECT *object) +{ + int i; + + for (i=object->y + 1; i < (object->y + object->height); i++) { + wattron(object->window.ncurses->win, A_INVIS); + mvwaddch(object->window.ncurses->win, i, object->x + object->width, ' '); + waddch(object->window.ncurses->win, + mvwinch(object->window.ncurses->win, + i, object->x + object->width) & A_CHARTEXT); + waddch(object->window.ncurses->win, + mvwinch(object->window.ncurses->win, + i, object->x + object->width+1) & A_CHARTEXT); + } + for (i=object->x + 1; i < (object->x + object->width + 2); i++) { + wattrset(object->window.ncurses->win, A_INVIS); + wmove(object->window.ncurses->win, object->y+object->height, i); + waddstr(object->window.ncurses->win, " "); + wattrset(object->window.ncurses->win, COLOR_PAIR(2)|A_BOLD); + waddch(object->window.ncurses->win, + mvwinch(object->window.ncurses->win, + object->y+object->height, i) & A_CHARTEXT); + } + wnoutrefresh(object->window.ncurses->win); +} + +void +ncurses_display_compound(OBJECT *object) +{ + int y, x; + + for (y=object->y; y < (object->y + object->height); y++) + for (x=object->x; x < (object->x + object->width); x++) + waddch(object->window.ncurses->win, + mvwinch(object->window.ncurses->win, y, x) & A_CHARTEXT); +} + +void +ncurses_display_action(OBJECT *object) +{ + ncurses_print_string(object, object->object.action->text); + wnoutrefresh(object->window.ncurses->win); +} + +void +ncurses_display_function(OBJECT *object) +{ + TUPLE *tuple; + void (* fn)(); + + tuple = tuple_search(object, object->object.function->fn, TT_FUNC); + if (!tuple) + return; + fn = (FUNCP)tuple->addr; + if (fn) + (*fn)(object); + wnoutrefresh(object->window.ncurses->win); +} + +void +ncurses_display_text(OBJECT *object) +{ + ncurses_print_string(object, object->object.text->text); + wnoutrefresh(object->window.ncurses->win); +} + +void +ncurses_display_input(OBJECT *object) +{ + if (object->object.input->lbl_flag) + ncurses_print_string(object, object->object.input->label); + else + ncurses_print_string(object, object->object.input->input); + wnoutrefresh(object->window.ncurses->win); +} + +void +ncurses_process_action(OBJECT *object) +{ + TUPLE *tuple; + int ch; + + ncurses_display_action(object); + wmove(object->window.ncurses->win, object->y, object->x); + + for (;;) { + ch = wgetch(object->window.ncurses->win); + + if (ch == K_ACCEPT) { + tuple = tuple_search(object, + object->object.action->action, TT_FUNC); + + if (!tuple) { + ncurses_print_status("No function bound to action"); + continue; + } else { + (*tuple->addr)(object); + return; + } + } else { + ch = ncurses_bind_key(object, ch); + + if (ch == ST_ERROR) { + beep(); + continue; + } else + return; + } + } +} + +void +ncurses_process_input(OBJECT *object) +{ + int len; + int disp_off=0, abspos=0, cursor = 0; + int ch; + +#define DISPOFF ((len < object->width) ? 0 : len - object->width) +#define CURSPOS ((len < object->width) ? len : object->width) + + ncurses_display_input(object); + + len = strlen(object->object.input->input); + + cursor = CURSPOS; + abspos = cursor; + + for (;;) { + + wmove(object->window.ncurses->win, object->y, object->x+cursor); + + ch = wgetch(object->window.ncurses->win); + ch = ncurses_bind_key(object, ch); + + /* + * If there was a valid motion command then we've + * moved to a new object so just return. If the motion + * command was invalid then just go around and get another + * keystroke. Otherwise, it was not a motion command. + */ + + if (ch == ST_OK) + return; + else if (ch == ST_ERROR) + continue; + + ncurses_print_status(""); + + if (object->object.input->lbl_flag) { + object->object.input->lbl_flag = 0; + } + if ((ch == K_HOME) || (ch == '')) { + disp_off = 0; + cursor = 0; + abspos = 0; + } else if ((ch == K_END) || (ch == '')) { + disp_off = DISPOFF; + abspos = len; + cursor = CURSPOS; + } else if (ch == K_DEL) { + if (!(len-abspos)) + beep(); + else { + bcopy(object->object.input->input+abspos+1, + object->object.input->input+abspos, + len - abspos); + --len; + } + } else if ((ch == K_LEFT) || (ch == K_BS) || (ch == '')) { + if (!abspos) + beep(); + else { + if (ch == K_BS) { + bcopy(object->object.input->input+abspos, + object->object.input->input+abspos-1, + len-abspos+1); + --len; + } + --abspos; + --cursor; + if ((disp_off) && (cursor < 0)) { + --disp_off; + ++cursor; + } + } + }else if (ch == '') { + bzero(object->object.input->input, len); + len = 0; + abspos = 0; + cursor = 0; + disp_off = 0; + } else if ((ch == K_RIGHT) || (ch == '')) { + if (abspos == len) + beep(); + else { + ++abspos; + if (++cursor >= object->width) { + ++disp_off; + --cursor; + } + } + } else if ((isprint(ch)) && (len < object->object.input->limit)){ + bcopy(object->object.input->input+abspos, + object->object.input->input+abspos+1, len-abspos+1); + object->object.input->input[abspos++] = ch; + len++; + if (++cursor > object->width) { + ++disp_off; + --cursor; + } + } else + beep(); + ncurses_print_string(object, object->object.input->input+disp_off); + } +} + +void +ncurses_print_status(char *msg) +{ + if (wmove(stdscr, LINES-1, 0) == ERR) { + endwin(); + exit(1); + } + + wclrtoeol(stdscr); + + wstandout(stdscr); + if (wprintw(stdscr, "%s", + msg) == ERR) { + endwin(); + exit(1); + } + wstandend(stdscr); + wrefresh(stdscr); +} + +int +ncurses_bind_key(OBJECT *object, unsigned int ch) +{ + struct Tuple *tuple=0; + + /* XXX -- check for keymappings here --- not yet done */ + + if (ch == K_UPL) { + if (object->lup) { + tuple = tuple_search(object, object->lup, TT_OBJ_INST); + if (!tuple) + ncurses_print_status("Field to move up to does not exist"); + } else + ncurses_print_status("Can't move up from this object"); + } else if (ch == K_DOWNL) { + if (object->ldown) { + tuple = tuple_search(object, object->ldown, TT_OBJ_INST); + if (!tuple) + ncurses_print_status("Field to move down to does not exist"); + } else + ncurses_print_status("Can't move down from this object"); + } else if (ch == K_LEFTL) { + if (object->lleft) { + tuple = tuple_search(object, object->lleft, TT_OBJ_INST); + if (!tuple) + ncurses_print_status("Field to move left to does not exist"); + } else + ncurses_print_status("Can't move left from this object"); + } else if (ch == K_RIGHTL) { + if (object->lright) { + tuple = tuple_search(object, object->lright, TT_OBJ_INST); + if (!tuple) + ncurses_print_status("Field to move right to does not exist"); + } else + ncurses_print_status("Can't move right from this object"); + } else if (ch == K_NEXTL) { + if (object->lnext) { + tuple = tuple_search(object, object->lnext, TT_OBJ_INST); + if (!tuple) + ncurses_print_status("Field to move to next does not exist"); + } else + ncurses_print_status("There is no next object from this object"); + } else + /* No motion keys pressed */ + return (ch); + + if (tuple) { + cur_obj = (OBJECT *)tuple->addr; + return (ST_OK); + } else { + beep(); + return (ST_ERROR); + } +} + +void +ncurses_display_menu(OBJECT *object) +{ + if (ncurses_print_string(object, + object->object.menu->options[object->object.menu->selected]) == ERR) + ncurses_print_status("Illegal scroll in print_string"); + wnoutrefresh(object->window.ncurses->win); +} + +void +ncurses_process_menu(OBJECT *object) +{ + int ch; + + for (;;) { + + ncurses_display_menu(object); + wmove(object->window.ncurses->win, object->y, object->x); + + ch = wgetch(object->window.ncurses->win); + + switch (ch) { + case ' ': + ncurses_print_status(""); + object->object.menu->selected++; + if (object->object.menu->selected >= object->object.menu->no_options) + object->object.menu->selected = 0; + ch = ST_OK; + break; + default: + ch = ncurses_bind_key(object, ch); + break; + } + + if (ch == ST_OK) + return; + else if (ch == ST_ERROR) { + beep(); + continue; + } else { + ncurses_print_status("Hit the space bar to toggle through options"); + beep(); + continue; + } + } +} + +ncurses_set_attributes(OBJECT *object, int hl) +{ + int y, x; + char *attr = 0; + + if (hl && object->highlight) + attr = object->highlight; + else if (object->attributes) + attr = object->attributes; + if (attr) { + wattrset(object->window.ncurses->win, A_NORMAL); + ncurses_parse_attrs(object, attr); + } +} |