📄 gui.c
字号:
/** This program is free software; you can redistribute it and/or modify* it under the terms of the GNU General Public License as published by* the Free Software Foundation; either version 2 of the License, or* (at your option) any later version.** This program is distributed in the hope that it will be useful,* but WITHOUT ANY WARRANTY; without even the implied warranty of* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the* GNU Library General Public License for more details.** You should have received a copy of the GNU General Public License* along with this program; if not, write to the Free Software* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.** Author:* NAME: James Stevenson* EMAIL: mistral@stev.org* WWW: http://www.stev.org*/#include <stdio.h>#include <stdlib.h>#include <ctype.h>#include <ncurses.h>#include <sys/time.h>#include "list.h"#include "gui.h"/** * gui_print - Print to a window * @win: Window to print on * @fmt: Same as printf * * This is the same as the printf's in libc */void gui_print(WINDOW *win, char *fmt, ...) { va_list ap; va_start(ap, fmt); vwprintw(win, fmt, ap); va_end(ap);}/** * gui_scroll - A generic scroll fucntion for lists bigger that a screen * @usec: The number of usecs to wait for input * @sec: The number of seconds to wait for input * @height: The maximum value the offset can be set to * @offset: A pointer to an interger that will be modified depending * on what key was pressed * * makes it easyier to have a list that the user can scroll up and * down it will return the key if it did not understand * the key was pressed */int gui_scroll(int usec, int sec, int height, int *offset) { int key; if (gui_wait(usec, sec)) { key = getch(); switch(key) { case ERR: return ERR; break; case KEY_UP: /* move up the ist so offset-- */ if (*offset > 0) --*offset; return 0; break; case KEY_DOWN: /* go forward */ if (*offset < height) ++*offset; return 0; break; case KEY_PPAGE: /* page up */ if (*offset > GUI_SCROLL_PAGE) { *offset -= GUI_SCROLL_PAGE; } else { *offset = 0; } return 0; break; case KEY_NPAGE: /* go down a page */ if (*offset < height - GUI_SCROLL_PAGE) { *offset += GUI_SCROLL_PAGE; } else { *offset = height - 1; } return 0; break; default: /* we did not do anything so just ret the keypress */ return key; break; } /* end of switch(key) */ } /* if (gui_utils_wait) */ /* when we get this far no keypress was done so an error occured */ return -1;}/** * gui_menu - Draw a menu on the screen * @p: A valid pointer to a gui_t struct * @menu: A gui_menu_t type * @y: The y position * @x: The x position * @height: The height of the menu * @width: The width of the menu * * The gui_menu_y should contain an array of the following * char *text; text to display in the window * char *des; text to display in the footer * int type; the type of entry * void (*func) (struct list_t *windows, int x, int y) fucntion pointer * The type should be set to one of GUI_MENU_ and the last item in the array must * be GUI_MENU_END */int gui_menu(struct gui_t *p, struct gui_menu_t menu[], int y, int x, int height, int width) { WINDOW *tmp = NULL, *in = NULL; /* and inner and outer window */ char *old_foot; /* backup of the footer */ int i, sel = 0, max; /* i for loops sel for seleted item max for max items */ int ret, old; tmp = newwin(height, width, y, x); if (!tmp) return -1; in = derwin(tmp, height - 2, width - 2, 1, 1); if (!in) { /* we failed free some stuff off */ delwin(tmp); return -2; } if (p->color) { wbkgd(tmp, COLOR_PAIR(COL_BACK)); wbkgd(in, COLOR_PAIR(COL_BACK)); } old_foot = p->footer; gui_footer(p, "Use Up/Down Arrows to select Left to backout Right to exec"); if (list_add(p->windows, tmp) < 0) { delwin(in); delwin(tmp); return -1; } if (list_add(p->windows, in) < 0) { delwin(in); delwin(tmp); return -1; } gui_redraw(p); werase(tmp); werase(in); box(tmp, 0, 0); i = 0; while(menu[i].type != GUI_MENU_END) { gui_menu_line(in, i, &menu[i]); i++; } max = i; old = sel;restart: /* jump to here to re read a char */ /* refix the seleted item */ gui_menu_line(in, old, &menu[old]); wattron(in, COLOR_PAIR(COL_SELECT) | A_BOLD); gui_menu_line(in, sel, &menu[sel]); wattroff(in, COLOR_PAIR(COL_SELECT) | A_BOLD); wrefresh(in); wrefresh(tmp); if (gui_wait(0, 5) >= 0) { ret = getch(); switch(ret) { case ERR: gui_footer(p, menu[sel].des); goto restart; break; case KEY_UP: if (sel > 0) { old = sel; sel--; } if (menu[sel].type == GUI_MENU_SEP && sel > 0) sel--; gui_footer(p, menu[sel].des); goto restart; break; case KEY_DOWN: if (sel < max - 1) { old = sel; sel++; } if (menu[sel].type == GUI_MENU_SEP && sel < max - 1) sel++; gui_footer(p, menu[sel].des); goto restart; break; case KEY_RIGHT: if (!menu[sel].func) { gui_footer(p, "Function not avilable"); goto restart; } menu[sel].func(p, y + sel, x + width); gui_redraw(p); goto restart; break; case KEY_LEFT: ret = 0; goto out; break; default: /* an invalid key press */ goto restart; break; } /* end of switch(ret) */ } else { ret = -2; goto out; } /* we should never get here */ ret = -5;out: /* set ret and jump to here */ list_del(p->windows, list_len(p->windows) - 1); list_del(p->windows, list_len(p->windows) - 1); if (in) { werase(in); wrefresh(in); delwin(in); } if (tmp) { werase(tmp); wrefresh(tmp); delwin(tmp); } gui_footer(p, old_foot); gui_redraw(p); return ret;}/** * gui_menu_line - Draw a line in a menu * @in: A Valid window to draw onto * @y: How far down the window * @p: A pointer to a valid gui_menu_t type * * only called from gui_menu * */void gui_menu_line(WINDOW *in, int y, struct gui_menu_t *p) { switch(p->type) { case GUI_MENU_SEP: mvwhline(in, y, 0, 0, in->_maxx + 1); break; case GUI_MENU_SUB: mvwprintw(in, y, 0, "%s", p->text); mvwaddch(in, y, in->_maxx, ACS_RARROW); break; case GUI_MENU_FUNC: mvwprintw(in, y, 0, "%s", p->text); break; } /* end switch */ return;}/** * gui_msg - draw a message box on the screen * @p: A valid pointer to struct gui_t * @msg: A text string of a message format it with newlines * and make sure the box around it is big enough to * hold the contents * @opts: This may be either GUI_MSG_ANY or a combination of * GUI_MSG_OK | GUI_MSG_IGNORE | GUI_MSG_CANCEL * @y: The y position of the box * @x: The x position of the box * @height: The height of the box * @width: The width of the box * * This should be used to draw a box on the screen with a message in it * the return value will be < 0 if an error occurs or * GUI_MSG_ANY if it is used * or one of the other GUI_MSG_* defines but only one of the ones passed * in the parameter opts */int gui_msg(struct gui_t *p, char *msg, int opts, int y, int x, int height, int width) { WINDOW *tmp; /* the new window */ WINDOW *in; /* the window we draw in */ char *backup, *new_footer, buff[1024]; int c, ret; /* char to read */ tmp = newwin(height, width, y, x); if (!tmp) return -1; in = derwin(tmp, height - 2, width - 2, 1, 1); if (!in) { /* we failed free some stuff off */ delwin(tmp); return -2; } if (p->color) { wbkgd(tmp, COLOR_PAIR(COL_BACK)); wbkgd(in, COLOR_PAIR(COL_BACK)); } gui_redraw(p); werase(tmp); werase(in); box(tmp, 0, 0); mvwprintw(in, 0, 0, "%s", msg); if (opts == GUI_MSG_ANY) { new_footer = "Press *ANY* key to continue"; } else { new_footer = &buff[0]; if (opts & GUI_MSG_OK) new_footer += sprintf(new_footer, "Ok "); if (opts & GUI_MSG_CANCEL) new_footer += sprintf(new_footer, "Cancel "); if (opts & GUI_MSG_IGNORE) new_footer += sprintf(new_footer, "Ignore "); new_footer = &buff[0]; } backup = p->footer; gui_footer(p, new_footer); wrefresh(tmp); wrefresh(tmp); /* Ok here is where it gets ugly */restart: ret = gui_wait(0, 10); if (ret == 0) goto restart; if (ret) { c = getch(); if (c == ERR) goto restart; if (opts & GUI_MSG_OK && tolower(c) == 'o') { ret = GUI_MSG_OK; goto out; } if (opts & GUI_MSG_IGNORE && tolower(c) == 'i') { ret = GUI_MSG_IGNORE; goto out; } if (opts & GUI_MSG_CANCEL && tolower(c) == 'c') { ret = GUI_MSG_CANCEL; goto out; } if (opts == GUI_MSG_ANY) { ret = GUI_MSG_ANY; goto out; } /* if we get to here the correct button was not pressed */ goto restart; } else { return -3; /* this is an error */ }out: gui_footer(p, backup); werase(tmp); wrefresh(tmp); gui_redraw(p); delwin(tmp); return 0;}/** * gui_wait - wait for input on stdin * @usecs: the number of usecs to wait * @seconds: the number of seconds to wait * * This will return 1 if there is input * 0 if a timeout occured * < 0 if an error occured **/int gui_wait(int usecs, int seconds) { fd_set fds; int ret; struct timeval tv; FD_ZERO(&fds); FD_SET(fileno(stdin), &fds); tv.tv_sec = seconds; tv.tv_usec = usecs; ret = select(fileno(stdin) + 1, &fds, NULL, NULL, &tv); if (ret) { if (FD_ISSET(fileno(stdin), &fds)) return 1; } else { if (ret == 0) /* this is not am error just a timeout */ return 0; return -1; } return -1; /* if we get this far it means we have failed */}/** * gui_footer - Change the gui footer * @p: A valid pointer to struct gui_t * @footer: New text for the footer to hold * * This will change the contents of the footer on the window * it will not save the text that is already there */int gui_footer(struct gui_t *p, char *footer) { p->footer = footer; werase(p->fwin); mvwprintw(p->fwin, 0, 0, "%s", p->footer); wrefresh(p->fwin); return 0;}/** * gui_redraw - redraw a list of windows * @p: a valid pointer to a struct of gui_t * * This is used to redraw all the windows * in the gui_t type */int gui_redraw(struct gui_t *p) { int i, len; WINDOW *win; len = list_len(p->windows); /* twin is a special case */ redrawwin(p->twin); wnoutrefresh(p->twin); for(i=0;i<len;i++) { win = list_get(p->windows, i); redrawwin(win); wnoutrefresh(win); } doupdate(); return 0;}/** * gui_setup - setup a new screen * * Returns a pointer to a struct * of type gui_t or NULL on failure */struct gui_t *gui_setup() { struct gui_t *tmp; tmp = malloc(sizeof(struct gui_t)); if (!tmp) return NULL; /* just jump out there is nothing else alloced */ tmp->windows = list_init(); if (!tmp->windows) goto cleanup_malloc; /* get a new window */ tmp->twin = initscr(); if (!tmp->twin) goto cleanup_list; /* lets see if we want colors */ if (has_colors()) { if (start_color() == ERR) { tmp->color = 0; } else { tmp->color = 1; init_pair(COL_BACK, COLOR_WHITE, COLOR_BLUE); init_pair(COL_FOOTER, COLOR_YELLOW, COLOR_CYAN); init_pair(COL_SELECT, COLOR_CYAN, COLOR_BLUE); } } else { tmp->color = 0; } /* lets setup our footer window */ tmp->fwin = newwin(1, tmp->twin->_maxx - 2, tmp->twin->_maxy, 1); if (!tmp->fwin) goto cleanup_twin; if (tmp->color) wbkgd(tmp->fwin, COLOR_PAIR(COL_FOOTER)); if (list_add(tmp->windows, tmp->fwin) < 0) goto cleanup_fwin; gui_footer(tmp, ""); /* set up some more defaults on the main window */ if (tmp->color) wbkgd(tmp->twin, COLOR_PAIR(COL_BACK)); /* set up some nice defaults */ keypad(tmp->twin, TRUE); noecho(); cbreak(); nodelay(tmp->twin, TRUE); /* clear the window */ werase(tmp->twin); box(tmp->twin, 0, 0); gui_redraw(tmp); return tmp;cleanup_fwin: delwin(tmp->fwin);cleanup_twin: endwin();cleanup_list: list_free(tmp->windows);cleanup_malloc: free(tmp); /* free off gui */ return NULL;}/** * gui_end - cleanup the screen * @p: A valid pointer to a struct gui_t * * Cleanup the struct gui_t * the pointer you pass to this * should not be used again * return < 0 if an error occurs */int gui_end(struct gui_t *p) { while(list_len(p->windows)) { delwin(list_get(p->windows, list_len(p->windows) - 1)); list_del(p->windows, list_len(p->windows) - 1); } list_free(p->windows); endwin(); /* kill off the top window */ free(p); /* free off any memory we are using */ return 0;}/* EOF */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -